chiark / gitweb /
1831bb3b7d8aa8303f32c863d6cf3bdeda2d8a17
[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   if (new_state < Sta_Run)
75     choreographers_all_abandon();
76   if (new_state < Sta_Resolving)
77     points_all_abandon();
78
79   piob.l= 0;
80   switch (new_state) {
81   case Sta_Flush:                                                  break;
82   case Sta_Off:   if (sta_state > Sta_Ping) enco_pic_off(&piob);   break;
83   case Sta_Ping:                                                   break;
84   case Sta_Fault:                                                  break;
85   case Sta_Settling:                        enco_pic_off(&piob);   break;
86   case Sta_Resolving:
87     resolve_begin();
88     points_turning_on();
89     enco_pic_on(&piob);
90     break;
91   case Sta_Run:
92     if (resolve_complete() <0)
93       /* in this case, we get stuck - user has to power cycle the layout */
94       return;
95     persist_install();
96     retransmit_start();
97     break;
98   }
99   if (piob.l) serial_transmit(&piob);
100
101   toev_start(&sta_toev);
102   sta_state= new_state;
103
104   /* notify various people: */
105   oprintf(UPO, "stastate %s\n", stastatelist[sta_state]);
106   /* ... add others here. */
107 }   
108
109 void serial_moredata(PicInsn *buf) {
110   const PicInsnInfo *pii;
111   int objnum, suppress;
112   Byte *ep;
113
114   /* Called when more data is received from PICs.
115    * On entry, buf->l is amount of data available.
116    * Does one of the following:
117    *  - determines that there is no complete message; sets buf->l = 0
118    *  - finds and handles one PicInsn message, sets buf->l = message length
119    *  - handles some series of bytes structured some other way,
120    *       sets buf->l to the numer of bytes handled.
121    */
122   assert(buf->l > 0);
123   
124   if (sta_state == Sta_Flush) {
125     toev_start(&sta_toev);
126     ouhex("picioh in junk", buf->d, buf->l);
127     return; /* junk absolutely everything */
128   }
129   if (PICMSG_AAARGH_P(buf->d[0])) {
130     ouhex("picioh in aaargh", buf->d, buf->l);
131     die("PIC sent us AAARGH!");
132   }
133   if (PICMSG_HELLO_P(buf->d[0])) {
134     ouhex("picioh in hello", buf->d, 1);
135     sta_goto(Sta_Flush);
136     buf->l= 1;
137     return;
138   }
139   if (sta_state == Sta_Off) {
140     ouhex("picioh in off", buf->d, 1);
141     buf->l= 1;
142     return;
143   }
144
145   assert(sta_state >= Sta_Ping);
146   /* So, we are expecting properly formed messages. */
147   
148   for (ep= buf->d; ep < buf->d + buf->l; ep++)
149     if (!(*ep & 0x80u))
150       goto found_end;
151
152   if (buf->l == sizeof(buf->d)) {
153     ouhex("picioh in toolong", buf->d, buf->l);
154     die("PIC sent packet too long");
155   }
156   buf->l= 0; /* message not yet finished, so consume nothing */
157   return;
158
159  found_end:
160   /* Aha! */
161   buf->l= ep - buf->d + 1;
162   picinsn_decode(buf, pic_reply_infos, &pii, &objnum);
163   suppress= pii && pii->noiselevel > picio_send_noise;
164   if (!suppress && picio_send_noise >= 2)
165     ouhex("picioh in msg", buf->d, buf->l);
166   if (!pii) { oprintf(UPO, "picio in unknown\n"); return; }
167   if (!suppress)
168     oupicio("in",pii,objnum);
169   pii->input_fn(pii,buf,objnum);
170 }
171
172 void on_pic_pong(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
173   if (objnum != ping_seq)
174     die("PIC sent wrong ping response (0x%x, wanted 0x%x)", objnum, ping_seq);
175
176   ping_toev.duration= 1000;
177   ping_toev.callback= timefor_ping;
178   toev_start(&ping_toev);
179
180   if (sta_state == Sta_Ping)
181     sta_goto(Sta_Settling);
182 }
183
184 void on_pic_fixed(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
185   if (sta_state >= Sta_Resolving) die("PIC sent unexpected FIXED");
186   if (sta_state == Sta_Fault) sta_goto(Sta_Resolving);
187 }
188
189 void on_pic_fault(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
190   if (sta_state <= Sta_Ping) return;
191   if (sta_state == Sta_Fault) die("PIC sent two FAULTs");
192   sta_goto(Sta_Fault);
193 }
194
195 void on_pic_wtimeout(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
196   if (sta_state <= Sta_Settling) return;
197   if (sta_state == Sta_Resolving) die("PIC sent WTIMEOUT in Resolving");
198   oprintf(UPO, "warning watchdog \"PIC watchdog timer triggered\"\n");
199 }
200
201 void on_pic_hello(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
202   { abort(); }
203 void on_pic_aaargh(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
204   { abort(); }
205 void on_pic_spurious(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
206   oprintf(UPO,"warning spurious %d \"spurious short circuit (fault)"
207           " detection interrupts\"", objnum);
208 }
209
210 void on_pic_detect1(const PicInsnInfo *pii, const PicInsn *pi, int segn) {
211   Segment *seg;
212   if (segn >= NUM_SEGMENTS) die("PIC sent detect @#%d out of range",segn);
213
214   seg= &segments[segn];
215   
216   switch (sta_state) {
217   case Sta_Flush:
218   case Sta_Off:
219   case Sta_Ping:
220   case Sta_Fault:
221   case Sta_Settling:
222     oprintf(UPO, "warning fixme ignored non-Run detection @%s\n",
223             info_segments[segn].pname);
224     break;
225   case Sta_Resolving:  seg->res_detect= 1;             break;
226   case Sta_Run:        safety_notify_detection(seg);   break;
227   }
228 }
229
230 /*---------- fixme move these to where they want to go ----------*/
231
232 void on_pic_nmradone(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
233   if (!objnum) die("PIC sent NUL!");
234   if (sta_state <= Sta_Settling) return;
235   if (sta_state != Sta_Run) die("PIC sent NMRADONE in Resolving");
236
237   while (objnum--)
238     retransmit_something();
239 }
240
241 void on_pic_detect0(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
242 }
243
244 void choreographers_all_abandon(void) { }