chiark / gitweb /
e56b4cfa4e1df4f1a4bc4a8c4ca99935d73bf989
[trains.git] / hostside / realtime.c
1 /*
2  * main program for realtime control
3  */
4
5 #include <sys/resource.h>
6
7 #include "realtime.h"
8
9 const char *progname= "realtime";
10
11 /*---------- global variables ----------*/
12
13 CommandInput cmdi;
14 int picio_send_noise= 1;
15 int disable_watchdog;
16
17 static const char *device= "/dev/railway";
18 static const char *logcopy_fn;
19
20 /*---------- general event handling ----------*/
21
22 static void comms_error(const char *ch, const char *e1,
23                         const char *e2 /* may be 0 */) {
24   if (!e1 && !e2) e1= "end of file";
25   die("communications error: %s: %s%s%s", ch, e1, e2?": ":"", e2?e2:"");
26 }
27
28 static void *read_exception(oop_source *evts, int fd,
29                             oop_event evt, void *cl_v) {
30   const char *ch;
31   char bufc;
32   int r;
33
34   ch= (fd==cmdi.out.fd ? cmdi.out.desc :
35        fd==serial_fd ? "serial port" :
36        0);
37   
38   r= read(fd, &bufc, 1);
39   if (r==-1) comms_error(ch, "read error", strerror(errno));
40   else if (r==0) comms_error(ch, "reports exception, reads EOF", 0);
41   else comms_error(ch, "reports exception, but readable", 0);
42
43   return OOP_CONTINUE;
44 }
45
46 /*---------- logging and output ----------*/
47
48 void safety_vpanic(Train *tra, Segment *seg,const char *fmt,va_list al) {
49   char *msg;
50   PicInsn piob;
51
52   enco_pic_off(&piob);
53   serial_transmit(&piob);
54
55   if (vasprintf(&msg,fmt,al) < 0)
56     diee("vasprintf failed in safety_vpanic fmt=\"%s\"", fmt);
57
58   die("fatal signalling problem: %s at %s: %s",
59       tra ? tra->pname : "?",
60       seg ? seg->i->pname : "?",
61       msg);
62 }
63
64 void safety_panic(Train *tra, Segment *seg, const char *fmt, ...) {
65   va_list al;
66   va_start(al,fmt);
67   safety_vpanic(tra, seg, fmt, al);
68 }
69
70 void ouvprintf(const char *fmt, va_list al) {
71   ovprintf_ccb(&cmdi.out, simlog_ccb,0, fmt,al);
72 }
73 void ouprintf(const char *fmt, ...) {
74   va_list al;
75   va_start(al,fmt);
76   ouvprintf(fmt,al);
77   va_end(al);
78 }
79
80 void ouvprintf_only(const char *fmt, va_list al) {
81   ovprintf(&cmdi.out, fmt,al);
82 }
83 void ouprintf_only(const char *fmt, ...) {
84   va_list al;
85   va_start(al,fmt);
86   ouvprintf_only(fmt,al);
87   va_end(al);
88 }
89
90 /*---------- printing nmra data ----------*/
91
92 typedef struct {
93   const Byte *i, *o;
94   int npending;
95   unsigned datapending;
96 } NmraDecCtx;
97
98 static void nmradec_init(NmraDecCtx *d, const PicInsn *pi) {
99   d->i= pi->d;
100   d->o= pi->d + pi->l;
101   d->npending= 0;
102   d->datapending= 0;
103 }
104
105 static void nmradec_getbits(NmraDecCtx *d, int *nbits_io, unsigned *bits_r) {
106   int use= *nbits_io;
107   while (d->npending < use) {
108     if (d->i >= d->o) break;
109     d->datapending <<= 7;
110     d->datapending |= *d->i & 0x7f;
111     d->npending += 7;
112     if (!(*d->i++ & 0x80)) break;
113   }
114   if (d->npending < use) use= d->npending;
115   d->npending -= use;
116   *bits_r= (d->datapending >> d->npending) & ~(~0u << use);
117   *nbits_io= use;
118 }
119   
120 static void nmra_decodeforpic(const PicInsn *pi,
121                               void (*on_idle)(void *u, int),
122                               void (*on_packet)(void *u, const Nmra*),
123                               void (*on_error)(void *u, int reasonchar),
124                               void *u) {
125   /* reasonchars:
126    *    #  - bad checksum
127    *    !  - too little idle
128    *    $  - truncated
129    */
130   NmraDecCtx d;
131   Nmra n;
132   int need_idle=7, got_idle=0;
133   unsigned got, csum;
134   int nbits;
135   
136   nmradec_init(&d,pi);
137   for (;;) {
138     nbits=1; nmradec_getbits(&d,&nbits,&got);
139     if (got) { got_idle++; continue; }
140     if (got_idle) on_idle(u,got_idle);
141     if (!nbits) return;
142     if (got_idle < need_idle) on_error(u,'!');
143     got_idle= 0;
144
145     n.l=0; csum=0;
146     do {
147       nbits=8; nmradec_getbits(&d,&nbits,&got);
148       n.d[n.l++]= got;
149       csum ^= got;
150       if (nbits<8) on_error(u,'$');
151
152       nbits=1; nmradec_getbits(&d,&nbits,&got);
153       if (nbits<1) on_error(u,'$');
154     } while (nbits && !got);
155
156     if (csum) on_error(u,'#');
157     else n.l--;
158
159     on_packet(u,&n);
160     need_idle= 14;
161   }
162 }
163
164 static void opn_idle(void *u, int idle) {
165   static const char dots[]= "......."; /* at least base/2 */
166   const int base= 14;
167   int largers= idle / base;
168   int units= idle % base;
169
170   ouprintf(" %.*s%.*s%.*s",
171           units/2, dots,
172           largers, ":::::::" /* enough */,
173           (units+1)/2, dots);
174 }
175 static void opn_error(void *u, int rc) { ouprintf(" %c",rc); }
176 static void opn_packet(void *u, const Nmra *n) {
177   int i;
178   const char *delim= " <";
179   for (i=0; i<n->l; i++) {
180     ouprintf("%s%02x",delim,n->d[i]);
181     delim=" ";
182   }
183   ouprintf(">");
184 }
185
186 static void oprint_nmradata(const PicInsn *pi) {
187   ouprintf("picio out nmradata");
188   nmra_decodeforpic(pi, opn_idle,opn_packet,opn_error, 0);
189   ouprintf("\n");
190 }
191   
192 /*---------- command channel handling (oop_read, obc) ----------*/
193
194 int vbadcmd(ParseState *ps, const char *fmt, va_list al) {
195   ouprintf("ack %s BadCmd : ", current_cmd?current_cmd->name:"?");
196   ouvprintf(fmt,al);
197   ouprintf("\n");
198   return EC_BadCmd;
199 }
200
201 void oupicio(const char *dirn, const PicInsnInfo *pii, int obj, int v,
202              void (*qprintf)(const char *fmt, ...)) {
203   if (!pii->argsbits)
204     qprintf("picio %s %s\n", dirn, pii->name);
205   else if (!pii->vbits)
206     qprintf("picio %s %s %#x\n", dirn, pii->name, obj);
207   else
208     qprintf("picio %s %s %#x %d\n", dirn, pii->name, obj, v);
209 }
210
211 static void obc_error(OutBufferChain *ch, const char *e1, const char *e2) {
212   if (!e1) { assert(!e2); die_hook(); exit(0); }
213   fprintf(stderr,"command communication error: %s%s%s\n",
214           e1, e2?": ":"", e2?e2:"");
215   die_hook();
216   exit(-1);
217 }
218
219 static void qouhex(const char *word, const Byte *command, int length,
220                    void (*qprintf)(const char *fmt, ...)) {
221   qprintf("%s", word);
222   while (length--) qprintf(" %02x", *command++);
223   qprintf("\n");
224 }
225
226 void ouhex(const char *word, const Byte *command, int length) {
227   qouhex(word,command,length, ouprintf);
228 }
229 void ouhex_nosim(const char *word, const Byte *command, int length) {
230   qouhex(word,command,length, ouprintf_only);
231 }
232
233 void die_vprintf_hook(const char *fmt, va_list al) {
234   static int recursing;
235   if (cmdi.out.desc && !recursing++)
236     ouvprintf(fmt, al);
237   recursing--;
238 }
239  
240 void die_hook(void) {
241   PicInsn off;
242   int e;
243
244   enco_pic_off(&off);
245   
246   write(serial_fd,off.d,off.l);
247   e= events ? obc_tryflush(&cmdi.out) : 0;
248   if (e) fprintf(stderr,"(unwritten command output: %s)\n",strerror(e));
249 }
250
251 /*---------- serial input (via oop) ----------*/
252
253 PicInsn serial_buf;
254
255 static void *serial_readable(oop_source *evts, int fd,
256                              oop_event evt, void *u0) {
257   int r, buf_used;
258
259   r= read(serial_fd, serial_buf.d + serial_buf.l,
260           sizeof(serial_buf.d) - serial_buf.l);
261
262   if (r==0) die("serial port - eof");
263   if (r==-1) {
264     if (errno == EWOULDBLOCK || errno == EINTR)
265       return OOP_CONTINUE;
266     diee("serial port - read error");
267   }
268   assert(r>0);
269
270   buf_used= serial_buf.l + r;
271   serial_indata_process(buf_used);
272   return OOP_CONTINUE;
273 }
274
275 void serial_indata_process(int buf_used) {
276   for (;;) {
277     serial_buf.l= buf_used;
278     serial_moredata(&serial_buf);
279     if (!serial_buf.l) break;
280     buf_used -= serial_buf.l;
281     memmove(serial_buf.d, serial_buf.d + serial_buf.l, buf_used);
282     if (!buf_used) break;
283   }
284   serial_buf.l= buf_used;
285 }
286
287 /*---------- serial port output (not via liboop) ----------*/
288
289 void serial_transmit(const PicInsn *pi) {
290   const PicInsnInfo *pii;
291   int obj, v, suppress=0;
292
293   if ((pi->d[0] & 0xf8) == 0x90) {
294     SEG_IV;
295     const char *delim;
296     
297     ouprintf("picio out polarity <");
298     delim="";
299     FOR_SEG {
300       if (!segi->invertible) continue;
301       if (!picinsn_polarity_testbit(pi,segi)) continue;
302       ouprintf("%s%s", delim, segi->pname);
303       delim= ",";
304     }
305     ouprintf(">\n");
306   } else if (pi->d[0] == 0xff) {
307     if (picio_send_noise < 3)
308       suppress= 1;
309     else 
310       oprint_nmradata(pi);
311   } else {
312     picinsn_decode(pi, pic_command_infos, &pii, &obj, &v);
313     if (!pii)
314       ouprintf("picio out unknown\n");
315     else if (pii->noiselevel > picio_send_noise)
316       suppress= 1;
317     else
318       oupicio("out",pii,obj,v,ouprintf);
319   }
320
321   if (!suppress && picio_send_noise >= 2)
322     ouhex("picioh out", pi->d, pi->l);
323
324   /* note that the serial port is still in nonblocking mode.  if
325    * we ever buffer up far enough that the kernel wants to make us
326    * block, we should die! */
327   serial_transmit_now(pi->d, pi->l);
328 }
329
330 /*---------- reporting page faults etc. ----------*/
331
332 #define CHECK_RUSAGE_FIELDS(F)                  \
333   F(ru_majflt)                                  \
334   F(ru_nswap)                                   \
335   F(ru_nivcsw)
336
337 /*  F(ru_minflt)                                        \
338  */
339
340 #define CRF_DECL(f) static long check_rusage_last_##f;
341 CHECK_RUSAGE_FIELDS(CRF_DECL)
342
343 static void getru(struct rusage *ru) {
344   int r= getrusage(RUSAGE_SELF, ru);  if (r) diee("getrusage");
345 }
346
347 void check_rusage_baseline(void) {
348   if (!rtfeats_use & RTFEAT_RUSAGE) return;
349   struct rusage ru;
350   ouprintf("info rusage : monitoring\n");
351   getru(&ru);
352   #define CRF_BASE(f) check_rusage_last_##f= ru.f;
353   CHECK_RUSAGE_FIELDS(CRF_BASE)
354 }
355
356 static void check_rusage_field(const char *f, long *last, long this, int alw) {
357   long diff= this - *last;
358   if (alw || diff) {
359     ouprintf(" %s+=%ld", f, diff);
360     *last= diff;
361   }
362 }
363
364 void check_rusage_check(int always_report) {
365   if (!rtfeats_use & RTFEAT_RUSAGE) return;
366   struct rusage ru;
367   getru(&ru);
368   #define CRF_CHANGED(f) || ru.f != check_rusage_last_##f
369   if (always_report
370       CHECK_RUSAGE_FIELDS(CRF_CHANGED)) {
371     ouprintf("info rusage :");
372     #define CRF_SHOW(f) \
373     check_rusage_field(STR(f), &check_rusage_last_##f, ru.f, always_report);
374     CHECK_RUSAGE_FIELDS(CRF_SHOW);
375     ouprintf(".\n");
376   }
377 }
378
379 /*---------- debugging ----------*/
380
381 unsigned long eventcounter;
382
383 void debug_count_event(const char *what) {
384   DPRINTF(misc,event, "#0x%lx %s\n",eventcounter,what);
385   eventcounter++;
386 }
387
388 #define DEFDFLAGS_safety ~(DBIT_safety_predictplan|DBIT_safety_predictseg)
389 #define DEFDFLAGS_movpos ~(DBIT_movpos_eval|DBIT_movpos_intern|DBIT_movpos_meth|DBIT_movpos_fsq)
390 #define DEFDFLAGS_speed ~(DBIT_speed_query)
391 #define DEFDFLAGS_retransmit ~(DBIT_retransmit_message)
392
393 #define DEBUG_FLAGS_H_DEFINE
394 #include "realtime+dflags.h"
395
396 static int debug_simulate_exactly;
397 static int nononblock_stdin;
398
399 static void debug_user_set(const DebugSelectorAreaInfo *dsai,
400                            int op, unsigned long bits) {
401   switch (op) {
402   case '+':  *dsai->dflags |=  bits;  break;
403   case '-':  *dsai->dflags &= ~bits;  break;
404   default: abort();
405   }
406   *dsai->userset |= bits;
407 }
408
409 static void debug_arg_spec(const char *arg) {
410   /* syntaxes:
411    *  -D+
412    *  -D-
413    *  -D+<area>
414    *  -D-<area>
415    *  -D<area><kinds>
416    * eg:
417    *  -D<area>+<kind>[<kinds>...]
418    *  -D<area>-<kind>[<kinds>...]
419    * where <kind> can be `*'
420    */
421   int wlen, l, alen, op=0;
422   const char *delim, *area;
423   const DebugSelectorAreaInfo *dsai=0;
424   const DebugSelectorKindInfo *dski;
425   unsigned long bits;
426
427   if (!strcmp("=",arg)) { debug_simulate_exactly= 1; return; }
428
429   for (;;) {
430     /* possibilities, arg points to |   dsai   op
431      *  -D|                              0      undef
432      *  -D|?                             0      undef
433      *  -D|?<area>                       0      undef
434      *  -D|<area>?...                    0      undef
435      *  -D<area>...?|<kind>            <area>   char `?'
436      *  -D<area>...?|<kind>?...        <area>   1st `?'
437      */
438
439     wlen= strcspn(arg,"+-");
440     delim= &arg[wlen];
441
442     if (!dsai) { /* -D|... */
443       if (!*delim) badusage("-D without any + - or =");
444       if (wlen) { /* -D|<area>?... */
445         area= arg;
446         alen= wlen;
447       } else { /* -D|?[<area>] */
448         area= arg+1;
449         alen= strlen(area);
450         if (!alen) { /* -D|? */
451           for (dsai=dsais; dsai->name; dsai++)
452             debug_user_set(dsai,*delim,~0UL);
453           return;
454         }
455       }
456       /* -D|<area>?... or -D?<area> */
457       for (dsai=dsais; dsai->name; dsai++) {
458         l= strlen(dsai->name);
459         if (l==alen && !memcmp(dsai->name,area,l)) goto area_found;
460       }
461       badusage("unknown debug message area");
462     area_found:
463       if (!wlen) { /* -D|?<area> */
464         debug_user_set(dsai,*delim,~0UL);
465         return;
466       }
467       /* -D|<area>?<kind>... */
468     } else { /* -D<area>...?|<kind>[?...] */
469       if (wlen==1 && arg[0]=='*') {
470         bits= ~0UL;
471         goto kind_found;
472       }
473       for (dski=dsai->kinds; dski->name; dski++) {
474         l= strlen(dski->name);
475         if (l==wlen && !memcmp(dski->name,arg,l)) {
476           bits= dski->bit;
477           goto kind_found;
478         }
479       }
480       badusage("unknown debug message kind");
481     kind_found:
482       debug_user_set(dsai,op,bits);
483       if (!*delim) /* -D<area>...?|<kind> */
484         return;
485        /* -D<area>...?|<kind>?<kind>... */
486     }
487     /* -D...?|<something>?<kind>... */
488     op= *delim++;
489     arg= delim;
490     /* -D...?<something>?|<kind>... */
491   }
492 }    
493
494 static void debug_setup(void) {
495   const DebugSelectorAreaInfo *dsai;
496   unsigned long def;
497
498   for (dsai=dsais; dsai->name; dsai++) {
499     def= (simulate && !debug_simulate_exactly) ? ~0UL : dsai->defdflags;
500     *dsai->dflags |= def & ~*dsai->userset;
501   }
502 }
503
504 /*---------- initialisation ----------*/
505
506 int main(int argc, const char **argv) {
507   oop_source_sys *sys_events;
508   const char *arg;
509   int r, c;
510
511   persist_map_veryearly();
512
513   if (argv[0] && argv[1] && !strcmp(argv[1],PERSIST_CONVERT_OPTION))
514     /* do this before we call malloc so that MAP_FIXED is sure to work */
515     persist_entrails_run_converter();
516
517   while ((arg=*++argv) && *arg=='-' && arg[1]) {
518     arg++;
519     while (arg && *arg) {
520       switch (*arg++) {
521       case 's': device= arg;                      arg=0; break;
522       case 'p': persist_fn= arg;                  arg=0; break;
523       case 'v': picio_send_noise= atoi(arg);      arg=0; break;
524       case 'm': sta_state= Sta_Manual;                   break;
525       case 'V': simlog_full=1;                           break;
526       case 'B': nononblock_stdin=1;                      break;
527       case 'W': disable_watchdog=1;                      break;
528       case 'L': logcopy_fn= arg;                  arg=0; break;
529       case 'S': simulate= arg;                    arg=0; break;
530       case 'D': debug_arg_spec(arg);              arg=0; break;
531       case 'R':
532         rtfeats_use= 0;
533         while ((c= *arg++)) {
534           switch (c) {
535           case 'd': rtfeats_use |= RTFEAT_DEFAULTS;     break;
536           case 'p': rtfeats_use |= RTFEAT_CPU;          break;
537           case 'P': rtfeats_use |= RTFEAT_ALL(CPU);     break;
538           case 'm': rtfeats_use |= RTFEAT_MEM;          break;
539           case 'M': rtfeats_use |= RTFEAT_ALL(MEM);     break;
540           case 'r': rtfeats_use |= RTFEAT_RUSAGE;       break;
541           default: badusage("unknown -R suboption");
542           }
543         }
544         arg= 0;
545         break;
546       default: badusage("unknown option");
547       }
548     }
549   }
550
551   cmdi.out.desc= (char*)"command";
552   cmdi.out.fd= 1;
553   cmdi.out.error= obc_error;
554   cmdi.doline= command_doline;
555   cmdi.out.empty= cmdi_output_bufferempty;
556
557   debug_setup();
558
559   if (!simulate) {
560     sys_events= oop_sys_new();  if (!sys_events) diee("oop_sys_new");
561     events= oop_sys_source(sys_events);  massert(events);
562
563     simlog_open(logcopy_fn);
564     cmdin_new(&cmdi, 0);
565     if (nononblock_stdin) oop_fd_nonblock(0,0);
566
567     serial_open(device);
568     r= oop_fd_nonblock(serial_fd, 1);  if (r) diee("nonblock(serial_fd,1)");
569
570     events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0);
571     events->on_fd(events, serial_fd, OOP_EXCEPTION, read_exception, 0);
572
573     if (rtfeats_use & RTFEAT_DEFAULTS)
574       rtfeats_use |= RTFEAT_CPU | RTFEAT_MEM | RTFEAT_RUSAGE;
575   } else {
576     sim_initialise(logcopy_fn);
577     sys_events= 0;
578   }
579
580   persist_entrails_interpret();
581   records_parse(argv);
582   realtime_priority();
583   speedmanager_init();
584   sta_startup();
585
586   if (!simulate) oop_sys_run(sys_events);
587   else sim_run();
588
589   abort();
590 }
591
592 DEFINE_ERRORCODELIST_DATA