2 * main program for realtime control
7 const char *progname= "realtime";
9 /*---------- global variables ----------*/
12 int picio_send_noise= 1;
14 static const char *device= "/dev/railway";
15 static const char *logcopy_fn;
17 /*---------- general event handling ----------*/
19 static void comms_error(const char *ch, const char *e1,
20 const char *e2 /* may be 0 */) {
21 if (!e1 && !e2) e1= "end of file";
22 die("communications error: %s: %s%s%s", ch, e1, e2?": ":"", e2?e2:"");
25 static void *read_exception(oop_source *evts, int fd,
26 oop_event evt, void *cl_v) {
31 ch= (fd==cmdi.out.fd ? cmdi.out.desc :
32 fd==serial_fd ? "serial port" :
35 r= read(fd, &bufc, 1);
36 if (r==-1) comms_error(ch, "read error", strerror(errno));
37 else if (r==0) comms_error(ch, "reports exception, reads EOF", 0);
38 else comms_error(ch, "reports exception, but readable", 0);
43 /*---------- logging and output ----------*/
45 void safety_vpanic(Train *tra, Segment *seg,const char *fmt,va_list al) {
50 serial_transmit(&piob);
52 if (vasprintf(&msg,fmt,al) < 0)
53 diee("vasprintf failed in safety_vpanic fmt=\"%s\"", fmt);
55 die("fatal signalling problem: %s at %s: %s",
56 tra ? tra->pname : "?",
57 seg ? seg->i->pname : "?",
61 void safety_panic(Train *tra, Segment *seg, const char *fmt, ...) {
64 safety_vpanic(tra, seg, fmt, al);
67 void ouvprintf(const char *fmt, va_list al) {
68 ovprintf_ccb(&cmdi.out, simlog_ccb,0, fmt,al);
70 void ouprintf(const char *fmt, ...) {
77 void ouvprintf_only(const char *fmt, va_list al) {
78 ovprintf(&cmdi.out, fmt,al);
80 void ouprintf_only(const char *fmt, ...) {
83 ouvprintf_only(fmt,al);
87 /*---------- printing nmra data ----------*/
95 static void nmradec_init(NmraDecCtx *d, const PicInsn *pi) {
102 static void nmradec_getbits(NmraDecCtx *d, int *nbits_io, unsigned *bits_r) {
104 while (d->npending < use) {
105 if (d->i >= d->o) break;
106 d->datapending <<= 7;
107 d->datapending |= *d->i & 0x7f;
109 if (!(*d->i++ & 0x80)) break;
111 if (d->npending < use) use= d->npending;
113 *bits_r= (d->datapending >> d->npending) & ~(~0u << use);
117 static void nmra_decodeforpic(const PicInsn *pi,
118 void (*on_idle)(void *u, int),
119 void (*on_packet)(void *u, const Nmra*),
120 void (*on_error)(void *u, int reasonchar),
124 * ! - too little idle
129 int need_idle=7, got_idle=0;
135 nbits=1; nmradec_getbits(&d,&nbits,&got);
136 if (got) { got_idle++; continue; }
137 if (got_idle) on_idle(u,got_idle);
139 if (got_idle < need_idle) on_error(u,'!');
144 nbits=8; nmradec_getbits(&d,&nbits,&got);
147 if (nbits<8) on_error(u,'$');
149 nbits=1; nmradec_getbits(&d,&nbits,&got);
150 if (nbits<1) on_error(u,'$');
151 } while (nbits && !got);
153 if (csum) on_error(u,'#');
161 static void opn_idle(void *u, int idle) {
162 static const char dots[]= "......."; /* at least base/2 */
164 int largers= idle / base;
165 int units= idle % base;
167 ouprintf(" %.*s%.*s%.*s",
169 largers, ":::::::" /* enough */,
172 static void opn_error(void *u, int rc) { ouprintf(" %c",rc); }
173 static void opn_packet(void *u, const Nmra *n) {
175 const char *delim= " <";
176 for (i=0; i<n->l; i++) {
177 ouprintf("%s%02x",delim,n->d[i]);
183 static void oprint_nmradata(const PicInsn *pi) {
184 ouprintf("picio out nmradata");
185 nmra_decodeforpic(pi, opn_idle,opn_packet,opn_error, 0);
189 /*---------- command channel handling (oop_read, obc) ----------*/
191 int vbadcmd(ParseState *ps, const char *fmt, va_list al) {
192 ouprintf("ack %s BadCmd : ", current_cmd?current_cmd->name:"?");
198 void oupicio(const char *dirn, const PicInsnInfo *pii, int obj, int v,
199 void (*qprintf)(const char *fmt, ...)) {
201 qprintf("picio %s %s\n", dirn, pii->name);
202 else if (!pii->vbits)
203 qprintf("picio %s %s %#x\n", dirn, pii->name, obj);
205 qprintf("picio %s %s %#x %d\n", dirn, pii->name, obj, v);
208 static void obc_error(OutBufferChain *ch, const char *e1, const char *e2) {
209 if (!e1) { assert(!e2); die_hook(); exit(0); }
210 fprintf(stderr,"command communication error: %s%s%s\n",
211 e1, e2?": ":"", e2?e2:"");
216 static void qouhex(const char *word, const Byte *command, int length,
217 void (*qprintf)(const char *fmt, ...)) {
219 while (length--) qprintf(" %02x", *command++);
223 void ouhex(const char *word, const Byte *command, int length) {
224 qouhex(word,command,length, ouprintf);
226 void ouhex_nosim(const char *word, const Byte *command, int length) {
227 qouhex(word,command,length, ouprintf_only);
230 void die_vprintf_hook(const char *fmt, va_list al) {
231 static int recursing;
237 void die_hook(void) {
243 write(serial_fd,off.d,off.l);
244 e= events ? obc_tryflush(&cmdi.out) : 0;
245 if (e) fprintf(stderr,"(unwritten command output: %s)\n",strerror(e));
248 /*---------- serial input (via oop) ----------*/
252 static void *serial_readable(oop_source *evts, int fd,
253 oop_event evt, void *u0) {
256 r= read(serial_fd, serial_buf.d + serial_buf.l,
257 sizeof(serial_buf.d) - serial_buf.l);
259 if (r==0) die("serial port - eof");
261 if (errno == EWOULDBLOCK || errno == EINTR)
263 diee("serial port - read error");
267 buf_used= serial_buf.l + r;
268 serial_indata_process(buf_used);
272 void serial_indata_process(int buf_used) {
274 serial_buf.l= buf_used;
275 serial_moredata(&serial_buf);
276 if (!serial_buf.l) break;
277 buf_used -= serial_buf.l;
278 memmove(serial_buf.d, serial_buf.d + serial_buf.l, buf_used);
279 if (!buf_used) break;
281 serial_buf.l= buf_used;
284 /*---------- serial port output (not via liboop) ----------*/
286 void serial_transmit(const PicInsn *pi) {
287 const PicInsnInfo *pii;
288 int obj, v, suppress=0;
290 if ((pi->d[0] & 0xf8) == 0x90) {
294 ouprintf("picio out polarity <");
297 if (!segi->invertible) continue;
298 if (!picinsn_polarity_testbit(pi,segi)) continue;
299 ouprintf("%s%s", delim, segi->pname);
303 } else if (pi->d[0] == 0xff) {
304 if (picio_send_noise < 3)
309 picinsn_decode(pi, pic_command_infos, &pii, &obj, &v);
311 ouprintf("picio out unknown\n");
312 else if (pii->noiselevel > picio_send_noise)
315 oupicio("out",pii,obj,v,ouprintf);
318 if (!suppress && picio_send_noise >= 2)
319 ouhex("picioh out", pi->d, pi->l);
321 /* note that the serial port is still in nonblocking mode. if
322 * we ever buffer up far enough that the kernel wants to make us
323 * block, we should die! */
324 serial_transmit_now(pi->d, pi->l);
327 /*---------- debugging ----------*/
329 unsigned long eventcounter;
331 void debug_count_event(const char *what) {
332 DPRINTF(misc,event, "#0x%lx %s\n",eventcounter,what);
336 #define DEFDFLAGS_safety ~(DBIT_safety_predictplan|DBIT_safety_predictseg)
337 #define DEFDFLAGS_movpos ~(DBIT_movpos_eval|DBIT_movpos_changeneeded)
338 #define DEFDFLAGS_speed ~(DBIT_speed_query)
339 #define DEFDFLAGS_retransmit ~(DBIT_retransmit_message)
341 #define DEBUG_FLAGS_H_DEFINE
342 #include "realtime+dflags.h"
344 static int debug_simulate_exactly;
346 static void debug_user_set(const DebugSelectorAreaInfo *dsai,
347 int op, unsigned long bits) {
349 case '+': *dsai->dflags |= bits; break;
350 case '-': *dsai->dflags &= ~bits; break;
353 *dsai->userset |= bits;
356 static void debug_arg_spec(const char *arg) {
364 * -D<area>+<kind>[<kinds>...]
365 * -D<area>-<kind>[<kinds>...]
366 * where <kind> can be `*'
368 int wlen, l, alen, op=0;
369 const char *delim, *area;
370 const DebugSelectorAreaInfo *dsai=0;
371 const DebugSelectorKindInfo *dski;
374 if (!strcmp("=",arg)) { debug_simulate_exactly= 1; return; }
377 /* possibilities, arg points to | dsai op
381 * -D|<area>?... 0 undef
382 * -D<area>...?|<kind> <area> char `?'
383 * -D<area>...?|<kind>?... <area> 1st `?'
386 wlen= strcspn(arg,"+-");
389 if (!dsai) { /* -D|... */
390 if (!*delim) badusage("-D without any + - or =");
391 if (wlen) { /* -D|<area>?... */
394 } else { /* -D|?[<area>] */
397 if (!alen) { /* -D|? */
398 for (dsai=dsais; dsai->name; dsai++)
399 debug_user_set(dsai,*delim,~0UL);
403 /* -D|<area>?... or -D?<area> */
404 for (dsai=dsais; dsai->name; dsai++) {
405 l= strlen(dsai->name);
406 if (l==alen && !memcmp(dsai->name,area,l)) goto area_found;
408 badusage("unknown debug message area");
410 if (!wlen) { /* -D|?<area> */
411 debug_user_set(dsai,*delim,~0UL);
414 /* -D|<area>?<kind>... */
415 } else { /* -D<area>...?|<kind>[?...] */
416 if (wlen==1 && arg[0]=='*') {
420 for (dski=dsai->kinds; dski->name; dski++) {
421 l= strlen(dski->name);
422 if (l==wlen && !memcmp(dski->name,arg,l)) {
427 badusage("unknown debug message kind");
429 debug_user_set(dsai,op,bits);
430 if (!*delim) /* -D<area>...?|<kind> */
432 /* -D<area>...?|<kind>?<kind>... */
434 /* -D...?|<something>?<kind>... */
437 /* -D...?<something>?|<kind>... */
441 static void debug_setup(void) {
442 const DebugSelectorAreaInfo *dsai;
445 for (dsai=dsais; dsai->name; dsai++) {
446 def= (simulate && !debug_simulate_exactly) ? ~0UL : dsai->defdflags;
447 *dsai->dflags |= def & ~*dsai->userset;
451 /*---------- initialisation ----------*/
453 int main(int argc, const char **argv) {
454 oop_source_sys *sys_events;
458 persist_map_veryearly();
460 if (argv[0] && argv[1] && !strcmp(argv[1],PERSIST_CONVERT_OPTION))
461 /* do this before we call malloc so that MAP_FIXED is sure to work */
462 persist_entrails_run_converter();
464 while ((arg=*++argv) && *arg=='-' && arg[1]) {
466 while (arg && *arg) {
468 case 's': device= arg; arg=0; break;
469 case 'p': persist_fn= arg; arg=0; break;
470 case 'v': picio_send_noise= atoi(arg); arg=0; break;
471 case 'm': sta_state= Sta_Manual; break;
472 case 'V': simlog_full=1; break;
473 case 'L': logcopy_fn= arg; arg=0; break;
474 case 'S': simulate= arg; arg=0; break;
475 case 'D': debug_arg_spec(arg); arg=0; break;
478 while ((c= *arg++)) {
480 case 'd': rtfeats_use |= RTFEAT_DEFAULTS; break;
481 case 'p': rtfeats_use |= RTFEAT_CPU; break;
482 case 'P': rtfeats_use |= RTFEAT_ALL(CPU); break;
483 case 'm': rtfeats_use |= RTFEAT_MEM; break;
484 case 'M': rtfeats_use |= RTFEAT_ALL(MEM); break;
485 default: badusage("unknown -R suboption");
490 default: badusage("unknown option");
495 cmdi.out.desc= (char*)"command";
497 cmdi.out.error= obc_error;
498 cmdi.doline= command_doline;
499 cmdi.out.empty= cmdi_output_bufferempty;
504 sys_events= oop_sys_new(); if (!sys_events) diee("oop_sys_new");
505 events= oop_sys_source(sys_events); massert(events);
507 simlog_open(logcopy_fn);
511 r= oop_fd_nonblock(serial_fd, 1); if (r) diee("nonblock(serial_fd,1)");
513 events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0);
514 events->on_fd(events, serial_fd, OOP_EXCEPTION, read_exception, 0);
516 if (rtfeats_use & RTFEAT_DEFAULTS)
517 rtfeats_use |= RTFEAT_CPU | RTFEAT_MEM;
519 sim_initialise(logcopy_fn);
523 persist_entrails_interpret();
529 if (!simulate) oop_sys_run(sys_events);
535 DEFINE_ERRORCODELIST_DATA