chiark / gitweb /
merge changes from trunk into our branch; cvs up -j branchpoint-hostside-wip-2006...
[trains.git] / hostside / startup.c
1 /*
2  * realtime
3  * startup state machine
4  */
5
6 #include "realtime.h"
7
8 const char *const stastatelist[]= DEFINE_STASTATE_DATA;
9 StartupState sta_state;
10
11 static TimeoutEvent sta_toev;
12 static TimeoutEvent ping_toev;
13
14 static int ping_seq;
15 static PicInsn piob;
16 static void sta_goto(StartupState new_state);
17
18 static void timedout_onward(TimeoutEvent *toev) {
19   assert(sta_state != Sta_Run);
20   if (sta_state == Sta_Settling) {
21     enco_pic_off(&piob);
22     serial_transmit(&piob);
23   }
24   sta_goto(sta_state == Sta_Flush ? Sta_Ping : sta_state + 1);
25 }
26
27 static void timedout_ping(TimeoutEvent *toev) {
28   assert(sta_state >= Sta_Ping);
29   sta_goto(Sta_Off);
30 }
31
32 static void timefor_ping(TimeoutEvent *toev) {
33   enco_pic_ping(&piob, ping_seq);
34   serial_transmit(&piob);
35   ping_toev.callback= timedout_ping;
36   toev_start(&ping_toev);
37 }
38
39 static void initial_ping(void) {
40   struct timeval now;
41   
42   mgettimeofday(&now);
43   ping_seq= (now.tv_sec & 0x1fU) << 5; /* bottom 5bi of secs: period 32s */
44   ping_seq |= (now.tv_usec >> 15);      /* top 5bi of 20bi us: res.~2^15us */
45   ping_toev.duration=  300;
46   timefor_ping(0);
47 }
48
49 void sta_startup(void) { sta_goto(Sta_Flush); }
50
51 static void sta_goto(StartupState new_state) {
52   toev_stop(&sta_toev);
53   sta_toev.callback= timedout_onward;
54   sta_toev.duration= -1;
55
56   if (new_state < Sta_Ping) {
57     toev_stop(&ping_toev);
58   } else if (new_state == Sta_Ping) {
59     initial_ping();
60   } else {
61     assert(sta_state >= Sta_Ping);
62   }
63
64   switch (new_state) {
65   case Sta_Flush:      sta_toev.duration=   300;   break;
66   case Sta_Off:                                    break;
67   case Sta_Ping:                                   break;
68   case Sta_Fault:                                  break;
69   case Sta_Settling:   sta_toev.duration=   750;   break;
70   case Sta_Resolving:  sta_toev.duration=   500;   break;
71   case Sta_Run:                                    break;
72   }
73
74   piob.l= 0;
75   switch (new_state) {
76   case Sta_Flush:                                                  break;
77   case Sta_Off:   if (sta_state > Sta_Ping) enco_pic_off(&piob);   break;
78   case Sta_Ping:                                                   break;
79   case Sta_Fault:                                                  break;
80   case Sta_Settling:                        enco_pic_off(&piob);   break;
81   case Sta_Resolving:                       enco_pic_on(&piob);    break;
82   case Sta_Run:         retransmit_start();                        break;
83   }
84   if (piob.l) serial_transmit(&piob);
85
86   toev_start(&sta_toev);
87   sta_state= new_state;
88
89   /* notify various people: */
90   oprintf(UPO, "stastate %s\n", stastatelist[sta_state]);
91   /* ... add others here. */
92 }   
93
94 void serial_moredata(PicInsn *buf) {
95   const PicInsnInfo *pii;
96   int objnum, suppress;
97   Byte *ep;
98
99   /* Called when more data is received from PICs.
100    * On entry, buf->l is amount of data available.
101    * Does one of the following:
102    *  - determines that there is no complete message; sets buf->l = 0
103    *  - finds and handles one PicInsn message, sets buf->l = message length
104    *  - handles some series of bytes structured some other way,
105    *       sets buf->l to the numer of bytes handled.
106    */
107   assert(buf->l > 0);
108   
109   if (sta_state == Sta_Flush) {
110     toev_start(&sta_toev);
111     ouhex("picioh in junk", buf->d, buf->l);
112     return; /* junk absolutely everything */
113   }
114   if (PICMSG_AAARGH_P(buf->d[0])) {
115     ouhex("picioh in aaargh", buf->d, buf->l);
116     die("PIC sent us AAARGH!");
117   }
118   if (PICMSG_HELLO_P(buf->d[0])) {
119     ouhex("picioh in hello", buf->d, 1);
120     abandon_run();
121     sta_goto(Sta_Flush);
122     buf->l= 1;
123     return;
124   }
125   if (sta_state == Sta_Off) {
126     ouhex("picioh in off", buf->d, 1);
127     buf->l= 1;
128     return;
129   }
130
131   assert(sta_state >= Sta_Ping);
132   /* So, we are expecting properly formed messages. */
133   
134   for (ep= buf->d; ep < buf->d + buf->l; ep++)
135     if (!(*ep & 0x80u))
136       goto found_end;
137
138   if (buf->l == sizeof(buf->d)) {
139     ouhex("picioh in toolong", buf->d, buf->l);
140     die("PIC sent packet too long");
141   }
142   buf->l= 0; /* message not yet finished, so consume nothing */
143   return;
144
145  found_end:
146   /* Aha! */
147   buf->l= ep - buf->d + 1;
148   picinsn_decode(buf, pic_reply_infos, &pii, &objnum);
149   suppress= pii && pii->noiselevel > picio_send_noise;
150   if (!suppress && picio_send_noise >= 2)
151     ouhex("picioh in msg", buf->d, buf->l);
152   if (!pii) { oprintf(UPO, "picio in unknown\n"); return; }
153   if (!suppress)
154     oupicio("in",pii,objnum);
155   pii->input_fn(pii,buf,objnum);
156 }
157
158 void on_pic_pong(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
159   if (objnum != ping_seq)
160     die("PIC sent wrong ping response (0x%x, wanted 0x%x)", objnum, ping_seq);
161
162   ping_toev.duration= 1000;
163   ping_toev.callback= timefor_ping;
164   toev_start(&ping_toev);
165
166   if (sta_state == Sta_Ping)
167     sta_goto(Sta_Settling);
168 }
169
170 void on_pic_fixed(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
171   if (sta_state >= Sta_Resolving) die("PIC sent unexpected FIXED");
172   if (sta_state == Sta_Fault) sta_goto(Sta_Resolving);
173 }
174
175 void on_pic_fault(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
176   if (sta_state <= Sta_Ping) return;
177   if (sta_state == Sta_Fault) die("PIC sent two FAULTs");
178   sta_goto(Sta_Fault);
179 }
180
181 void on_pic_wtimeout(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
182   if (sta_state <= Sta_Settling) return;
183   if (sta_state == Sta_Resolving) die("PIC sent WTIMEOUT in Resolving");
184   oprintf(UPO, "warning watchdog \"PIC watchdog timer triggered\"\n");
185 }
186
187 void on_pic_hello(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
188   { abort(); }
189 void on_pic_aaargh(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
190   { abort(); }
191
192 void on_pic_detect1(const PicInsnInfo *pii, const PicInsn *pi, int segn) {
193   if (segn >= NUM_SEGMENTS) die("PIC sent detect @#%d out of range",segn);
194   if (sta_state < Sta_Run) {
195     oprintf(UPO, "warning fixme ignored non-Run detection @%s\n",
196             info_segments[segn].pname);
197     return;
198   }
199   safety_notify_detection(&segments[segn]);
200 }
201
202 /*---------- fixme move these to where they want to go ----------*/
203
204 void on_pic_nmradone(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
205   if (!objnum) die("PIC sent NUL!");
206   if (sta_state <= Sta_Settling) return;
207   if (sta_state != Sta_Run) die("PIC sent NMRADONE in Resolving");
208
209   while (objnum--)
210     retransmit_something();
211 }
212
213 void on_pic_detect0(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
214 }
215
216 void abandon_run(void) {
217   /* fixme do something here */
218 }