chiark / gitweb /
hostside: more length for bavarian
[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= { .pclass="startup", .pinst="sta" };
12
13 static PicInsn piob;
14 static void sta_goto(StartupState new_state);
15
16 /*---------- ping ----------*/
17
18 static int pong_seq;
19 static TimeoutEvent ping_toev= { .pinst="ping" };
20
21 static void timedout_ping(TimeoutEvent *toev) {
22   assert(sta_state >= Sta_Ping);
23   sta_goto(Sta_Off);
24 }
25
26 static void timefor_ping(TimeoutEvent *toev) {
27   enco_pic_ping(&piob, pong_seq ^ PING_PONG_PATTERN);
28   serial_transmit(&piob);
29   ping_toev.callback= timedout_ping;
30   ping_toev.pclass= "startup";
31   toev_start(&ping_toev);
32 }
33
34 static void initial_ping(void) {
35   struct timeval now;
36   
37   mgettimeofday(&now);
38   pong_seq= (now.tv_sec & 0x1fU) << 5; /* bottom 5bi of secs: period 32s */
39   pong_seq |= (now.tv_usec >> 15);      /* top 5bi of 20bi us: res.~2^15us */
40   ping_toev.duration=  300;
41   timefor_ping(0);
42 }
43
44 /*---------- watchdog ----------*/
45
46 static TimeoutEvent watchdog_toev= {
47   .pclass=toev_fast_pclass,
48   .pinst="watchdog",
49   .duration= UNMARGIN_WATCHDOG - MARGIN_WATCHDOG,
50 };
51 static PicInsn watchdog_piob;
52
53 static void watchdog_transmit(TimeoutEvent *toev) {
54   check_rusage_check(0);
55   toev_start(&watchdog_toev);
56   serial_transmit(&watchdog_piob);
57 }
58
59 static void watchdog_start(void) {
60   check_rusage_baseline();
61   watchdog_toev.callback= watchdog_transmit;
62   enco_pic_watchdog(&watchdog_piob, UNMARGIN_WATCHDOG_16);
63   watchdog_transmit(0);
64 }
65
66 static void watchdog_stop(void) {
67   if (!watchdog_toev.running) return;
68   toev_stop(&watchdog_toev);
69   enco_pic_watchdog(&watchdog_piob, 0);
70   serial_transmit(&watchdog_piob);
71   check_rusage_check(1);
72 }
73
74 /*---------- main startup algorithm ----------*/
75
76 static void timedout_onward(TimeoutEvent *toev) {
77   assert(sta_state != Sta_Run);
78   if (sta_state == Sta_Settling) {
79     enco_pic_off(&piob);
80     serial_transmit(&piob);
81   }
82   sta_goto(sta_state == Sta_Flush ? Sta_Ping : sta_state + 1);
83 }
84
85 static void sta_startup_manual(void) {
86   waggle_startup_manual();
87   retransmit_start();
88   ouprintf("stastate %s\n", stastatelist[sta_state]);
89 }
90
91 void sta_startup(void) {
92   if (sta_state == Sta_Manual) sta_startup_manual();
93   else sta_goto(Sta_Flush);
94 }
95
96 void sta_finalising_done(void) { sta_goto(Sta_Run); }
97
98 static void sta_goto(StartupState new_state) {
99   toev_stop(&sta_toev);
100   sta_toev.callback= timedout_onward;
101   sta_toev.duration= -1;
102
103   if (new_state < Sta_Ping) {
104     toev_stop(&ping_toev);
105   } else if (new_state == Sta_Ping) {
106     initial_ping();
107   } else {
108     assert(sta_state >= Sta_Ping);
109   }
110
111   switch (new_state) {
112   case Sta_Flush:      sta_toev.duration=   300;   break;
113   case Sta_Off:                                    break;
114   case Sta_Manual:                                 abort();
115   case Sta_Ping:                                   break;
116   case Sta_Settling:   sta_toev.duration=   750;   break;
117   case Sta_Resolving:  sta_toev.duration=   500;   break;
118   case Sta_Finalising:                             break;
119   case Sta_Run:                                    break;
120   case Sta_Crashed: abort();
121   }
122
123   if (new_state < Sta_Run)
124     choreographers_all_abandon();
125   if (new_state < Sta_Finalising) {
126     safety_abandon_run();
127     motions_all_abandon();
128   }
129
130   piob.l= 0;
131   switch (new_state) {
132   case Sta_Flush:                                                  break;
133   case Sta_Off:   if (sta_state > Sta_Ping) enco_pic_off(&piob);   break;
134   case Sta_Manual:                                                 abort();
135   case Sta_Ping:                                                   break;
136   case Sta_Settling:   waggle_settle();     enco_pic_off(&piob);   break;
137   case Sta_Resolving:
138     resolve_begin();
139     points_turning_on();
140     adjuncts_start_xmit();
141     enco_pic_on(&piob);
142     break;
143   case Sta_Finalising:
144     if (resolve_complete() <0)
145       /* in this case, we get stuck - user has to power cycle the layout */
146       return;
147     /* resolve_motioncheck will move us to Run eventually, we hope */
148     break;
149   case Sta_Run:
150     if (!simulate)
151       persist_install();
152     movpos_reportall();
153     retransmit_start();
154     break;
155   case Sta_Crashed: abort();
156   }
157   if (piob.l) serial_transmit(&piob);
158
159   if (new_state >= Sta_Run && !disable_watchdog) watchdog_start();
160   else watchdog_stop();
161      
162   toev_start(&sta_toev);
163   sta_state= new_state;
164
165   /* notify various people: */
166   ouprintf("stastate %s\n", stastatelist[sta_state]);
167   /* ... add others here. */
168   if (sta_state == Sta_Finalising) resolve_motioncheck();
169 }   
170
171 void serial_moredata(PicInsn *buf) {
172   const PicInsnInfo *pii;
173   int obj, v, suppress;
174   Byte *ep;
175
176   /* Called when more data is received from PICs.
177    * On entry, buf->l is amount of data available.
178    * Does one of the following:
179    *  - determines that there is no complete message; sets buf->l = 0
180    *  - finds and handles one PicInsn message, sets buf->l = message length
181    *  - handles some series of bytes structured some other way,
182    *       sets buf->l to the numer of bytes handled.
183    */
184   assert(buf->l > 0);
185   
186   if (sta_state == Sta_Flush) {
187     ouhex("picioh in junk", buf->d, buf->l);
188     toev_start(&sta_toev);
189     return; /* junk absolutely everything */
190   }
191   if (PICMSG_AAARGH_P(buf->d[0])) {
192     ouhex("picioh in aaargh", buf->d, buf->l);
193     die("PIC sent us AAARGH!");
194   }
195   if (PICMSG_HELLO_P(buf->d[0])) {
196     ouhex("picioh in hello", buf->d, 1);
197     if (sta_state != Sta_Manual)
198       sta_goto(Sta_Flush);
199     buf->l= 1;
200     return;
201   }
202   if (sta_state == Sta_Off) {
203     ouhex("picioh in off", buf->d, 1);
204     buf->l= 1;
205     return;
206   }
207
208   assert(sta_state >= Sta_Manual);
209   /* So, we are expecting properly formed messages. */
210   
211   for (ep= buf->d; ep < buf->d + buf->l; ep++)
212     if (!(*ep & 0x80u))
213       goto found_end;
214
215   if (buf->l == sizeof(buf->d)) {
216     ouhex("picioh in toolong", buf->d, buf->l);
217     die("PIC sent packet too long");
218   }
219   buf->l= 0; /* message not yet finished, so consume nothing */
220   return;
221
222  found_end:
223   /* Aha! */
224   buf->l= ep - buf->d + 1;
225   picinsn_decode(buf, pic_reply_infos, &pii, &obj, &v);
226   suppress= pii && pii->noiselevel > picio_send_noise;
227
228   if (!suppress && picio_send_noise >= 2)
229     ouhex_nosim("picioh in msg", buf->d, buf->l);
230
231   if (simlog_full || sta_state < Sta_Settling ||
232       !((pii->opcode==PICMSG_NMRADONE && obj==1) ||
233         (pii->opcode==PICMSG_PONG && obj==pong_seq)))
234     simlog_serial(buf->d, buf->l);
235   
236   if (!pii) { ouprintf("picio in unknown\n"); return; }
237   if (!suppress)
238     oupicio("in",pii,obj,v,ouprintf_only);
239   pii->input_fn(pii,buf,obj);
240 }
241
242 void on_pic_nul(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
243   /* layout turned off, probably */
244   if (sta_state == Sta_Manual) return;
245   enco_pic_off(&piob);
246   serial_transmit(&piob);
247   sta_goto(Sta_Flush);
248   return;
249 }
250
251 void on_pic_pong(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
252   if (sta_state == Sta_Manual)
253     return;
254
255   if (objnum != pong_seq) {
256     if (objnum == (pong_seq^PING_PONG_PATTERN))
257       die("PIC connection is looped back (ping %#05x bounced)", objnum);
258     die("PIC sent wrong ping response (%#05x, wanted %#05x)", objnum, pong_seq);
259   }
260
261   ping_toev.duration= 1000;
262   ping_toev.callback= timefor_ping;
263   ping_toev.pclass= toev_fast_pclass;
264   toev_start(&ping_toev);
265
266   if (sta_state == Sta_Ping)
267     sta_goto(Sta_Settling);
268 }
269
270 void on_pic_fault(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
271   if (sta_state <= Sta_Ping) return;
272   die("FAULT");
273 }
274
275 void on_pic_wtimeout(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
276   if (sta_state <= Sta_Settling) return;
277   check_rusage_check(1);
278   die("microcontrollers' watchdog timer triggered\n");
279 }
280
281 void on_pic_hello(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
282   { abort(); }
283 void on_pic_aaargh(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
284   { abort(); }
285 void on_pic_spurious(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
286   ouprintf("warning spurious %d : spurious short circuit (fault)"
287            " detection interrupts\n", objnum);
288 }
289 void on_pic_pointed(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { }
290 void on_pic_retriable(const PicInsnInfo *pii, const PicInsn *pi, int objnum){}
291
292 static int coalescing_detects;
293
294 static void detect_report_now(Segment *seg) {
295   ouprintf_only("detect %s %d\n", seg->i->pname, seg->detect_actual);
296   seg->detect_reported= seg->detect_actual;
297 }
298
299 static SegmentNum on_pic_detect_prep(int detyn, int objnum) {
300   SegmentNum segn;
301
302   if (objnum >= info_segmentmaplen ||
303       (segn= info_segmentmap[objnum]) < 0)
304     die("PIC sent detect%d @#%#x not in map",detyn,objnum);
305
306   Segment *seg= &segments[segn];
307   seg->detect_actual= detyn;
308
309   if (!(picio_send_noise <= 1 &&
310         seg->owner &&
311         seg->det_ignore)) {
312     if (cmdi.out.total >= 1024) coalescing_detects= 1;
313
314     if (coalescing_detects &&
315         seg->det_ignore &&
316         seg->detect_reported) {
317       assert(seg->detect_flaps < INT_MAX);
318       seg->detect_flaps++;
319     } else {
320       detect_report_now(seg);
321     }
322   }
323
324   return segn;
325 }
326
327 void cmdi_output_bufferempty(OutBufferChain *obc) {
328   SEG_IV;
329   
330   if (!coalescing_detects) return;
331   coalescing_detects= 0;
332
333   FOR_SEG {
334     if (seg->detect_flaps) {
335       ouprintf_only("detect-flaps %s %d\n", segi->pname, seg->detect_flaps);
336       seg->detect_flaps= 0;
337     }
338     if (seg->detect_actual != seg->detect_reported) {
339       detect_report_now(seg);
340     }
341   }
342 }
343
344 void on_pic_detect1(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
345   SegmentNum segn;
346   Segment *seg;
347   const char *pname;
348   
349   segn= on_pic_detect_prep(1,objnum);
350   seg= &segments[segn];
351   pname= info_segments[segn].pname;
352   
353   switch (sta_state) {
354   case Sta_Flush:
355   case Sta_Off:
356   case Sta_Manual:
357   case Sta_Ping:
358   case Sta_Settling:
359     break;
360   case Sta_Resolving:
361     seg->res_detect= 1;
362     break;
363   case Sta_Finalising:
364     if (!seg->res_detect) {
365       ouprintf("resolution new-detection-in-finalising @%s\n", pname);
366       sta_goto(Sta_Settling);
367     }
368     break;
369   case Sta_Run:
370     safety_notify_detection(seg);
371     break;
372   case Sta_Crashed: abort();
373   }
374 }
375
376 void on_pic_detect0(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
377   on_pic_detect_prep(0,objnum);
378 }
379
380 void choreographers_all_abandon(void) { }