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/ttya0";
16 /*---------- general event handling ----------*/
18 static void comms_error(const char *ch, const char *e1,
19 const char *e2 /* may be 0 */) {
20 if (!e1 && !e2) e1= "end of file";
21 die("communications error: %s: %s%s%s", ch, e1, e2?": ":"", e2?e2:"");
24 static void *read_exception(oop_source *evts, int fd,
25 oop_event evt, void *cl_v) {
30 ch= (fd==UPO->fd ? UPO->desc :
31 fd==serial_fd ? "serial port" :
34 r= read(fd, &bufc, 1);
35 if (r==-1) comms_error(ch, "read error", strerror(errno));
36 else if (r==0) comms_error(ch, "reports exception, reads EOF", 0);
37 else comms_error(ch, "reports exception, but readable", 0);
42 /*---------- logging etc. ----------*/
44 static char *transegn2suffixstring(Train *tra, Segment *seg) {
45 /* Either arg may be 0, in which case it's not included.
46 * Result string will be empty, or start with ": "
47 * Result string is from malloc.
50 const char *trapn, *segpn;
54 segpn= seg ? seg->i->pname : 0;
55 trapn= tra ? tra->pname : 0;
57 r= asprintf(&s, "%s%s%s%s%s",
58 segpn||trapn ? ":" : "",
63 if (r<0) diee("vasprintf failed in transegn2suffixstring");
67 void vlogmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
68 const char *fmt, va_list al) {
69 oprintf(UPO, "message %s ", ec ? errorcodelist[ec] : "info");
71 if (segi || tra) oprintf(UPO, ":");
72 if (segi) oprintf(UPO, " @%s", segi->pname);
73 if (tra) oprintf(UPO, " %s", tra->pname);
77 void logmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
78 const char *fmt,...) {
81 vlogmsg(ec,tra,segi,fmt,al);
85 void safety_vpanic(Train *tra, Segment *seg,const char *fmt,va_list al) {
90 serial_transmit(&piob);
92 if (vasprintf(&msg,fmt,al) < 0)
93 diee("vasprintf failed in safety_vpanic fmt=\"%s\"", fmt);
95 where= transegn2suffixstring(tra,seg);
96 die("fatal safety problem: %s%s", msg, where);
99 void safety_panic(Train *tra, Segment *seg, const char *fmt, ...) {
102 safety_vpanic(tra, seg, fmt, al);
105 ErrorCode safety_problem(Train *tra, Segment *seg, const char *fmt,...) {
108 vlogmsg(EC_Safety, tra, seg?seg->i:0, fmt, al);
113 /*---------- printing nmra data ----------*/
118 unsigned datapending;
121 static void nmradec_init(NmraDecCtx *d, const PicInsn *pi) {
128 static void nmradec_getbits(NmraDecCtx *d, int *nbits_io, unsigned *bits_r) {
130 while (d->npending < use) {
131 if (d->i >= d->o) break;
132 d->datapending <<= 7;
133 d->datapending |= *d->i & 0x7f;
135 if (!(*d->i++ & 0x80)) break;
137 if (d->npending < use) use= d->npending;
139 *bits_r= (d->datapending >> d->npending) & ~(~0u << use);
143 static void nmra_decodeforpic(const PicInsn *pi,
144 void (*on_idle)(void *u, int),
145 void (*on_packet)(void *u, const Nmra*),
146 void (*on_error)(void *u, int reasonchar),
150 * ! - too little idle
155 int need_idle=7, got_idle=0;
161 nbits=1; nmradec_getbits(&d,&nbits,&got);
162 if (got) { got_idle++; continue; }
163 if (got_idle) on_idle(u,got_idle);
165 if (got_idle < need_idle) on_error(u,'!');
170 nbits=8; nmradec_getbits(&d,&nbits,&got);
173 if (nbits<8) on_error(u,'$');
175 nbits=1; nmradec_getbits(&d,&nbits,&got);
176 if (nbits<1) on_error(u,'$');
177 } while (nbits && !got);
179 if (csum) on_error(u,'#');
187 static void opn_idle(void *u, int idle) {
188 static const char dots[]= "......."; /* at least base/2 */
190 int largers= idle / base;
191 int units= idle % base;
193 oprintf(UPO," %.*s%.*s%.*s",
195 largers, ":::::::" /* enough */,
198 static void opn_error(void *u, int rc) { oprintf(UPO," %c",rc); }
199 static void opn_packet(void *u, const Nmra *n) {
201 const char *delim= " <";
202 for (i=0; i<n->l; i++) {
203 oprintf(UPO,"%s%02x",delim,n->d[i]);
209 static void oprint_nmradata(const PicInsn *pi) {
210 oprintf(UPO,"picio out nmradata");
211 nmra_decodeforpic(pi, opn_idle,opn_packet,opn_error, 0);
215 /*---------- command channel handling (oop_read, obc) ----------*/
217 static void command_doline(ParseState *ps, CommandInput *cmdi) {
219 ci= some_needword_lookup(ps, toplevel_cmds, "command");
220 if (ci) ci->fn(ps,ci);
223 void oupicio(const char *dirn, const PicInsnInfo *pii, int objnum) {
225 oprintf(UPO, "picio %s %s\n", dirn, pii->name);
227 oprintf(UPO, "picio %s %s %u\n", dirn, pii->name, objnum);
230 void vbadcmd(ParseState *ps, const char *fmt, va_list al) {
234 static void obc_error(OutBufferChain *ch, const char *e1, const char *e2) {
235 if (!e1) { assert(!e2); exit(0); }
236 fprintf(stderr,"command communication error: %s%s%s\n",
237 e1, e2?": ":"", e2?e2:"");
241 void ouhex(const char *word, const Byte *command, int length) {
242 oprintf(UPO, "%s", word);
244 oprintf(UPO, " %02x", *command++);
250 void die_vprintf_hook(const char *fmt, va_list al) {
251 if (events) ovprintf(UPO, fmt, al);
254 void die_hook(void) {
256 e= events ? obc_tryflush(UPO) : 0;
257 if (e) fprintf(stderr,"(unwritten command output: %s)\n",strerror(e));
260 int ps_needsegment(ParseState *ps, Segment **seg_r,
261 const SegmentInfo **segi_r) {
262 const SegmentInfo *segi;
263 segi= some_needword_lookup_counted(ps,info_segments,info_nsegments,
266 if (segi_r) *segi_r= segi;
267 if (seg_r) *seg_r= segments + (segi - info_segments);
271 /*---------- serial input (via oop) ----------*/
273 static PicInsn serial_buf;
275 static void *serial_readable(oop_source *evts, int fd,
276 oop_event evt, void *u0) {
279 r= read(serial_fd, serial_buf.d + serial_buf.l,
280 sizeof(serial_buf.d) - serial_buf.l);
282 if (r==0) die("serial port - eof");
284 if (errno == EWOULDBLOCK || errno == EINTR)
286 diee("serial port - read error");
290 buf_used= serial_buf.l + r;
293 serial_buf.l= buf_used;
294 serial_moredata(&serial_buf);
295 if (!serial_buf.l) break;
296 buf_used -= serial_buf.l;
297 memmove(serial_buf.d, serial_buf.d + serial_buf.l, buf_used);
298 if (!buf_used) break;
300 serial_buf.l= buf_used;
304 /*---------- serial port output (not via liboop) ----------*/
306 void serial_transmit(const PicInsn *pi) {
307 const PicInsnInfo *pii;
308 int objnum, suppress=0;
310 if ((pi->d[0] & 0xf8) == 0x90) {
314 oprintf(UPO,"picio out polarity <");
317 if (!segi->invertible) continue;
318 if (!picinsn_polarity_testbit(pi,segi)) continue;
319 oprintf(UPO,"%s%s", delim, segi->pname);
323 } else if (pi->d[0] == 0xff) {
324 if (picio_send_noise < 3)
329 picinsn_decode(pi, pic_command_infos, &pii, &objnum);
331 oprintf(UPO, "picio out unknown\n");
332 else if (pii->noiselevel > picio_send_noise)
335 oupicio("out",pii,objnum);
338 if (!suppress && picio_send_noise >= 2)
339 ouhex("picioh out", pi->d, pi->l);
341 /* note that the serial port is still in nonblocking mode. if
342 * we ever buffer up far enough that the kernel wants to make us
343 * block, we should die! */
344 serial_transmit_now(pi->d, pi->l);
347 /*---------- initialisation ----------*/
349 int main(int argc, const char **argv) {
350 oop_source_sys *sys_events;
354 persist_map_veryearly();
356 if (argv[0] && argv[1] && !strcmp(argv[1],PERSIST_CONVERT_OPTION))
357 /* do this before we call malloc so that MAP_FIXED is sure to work */
358 persist_entrails_run_converter();
360 sys_events= oop_sys_new(); if (!sys_events) diee("oop_sys_new");
361 events= oop_sys_source(sys_events); massert(events);
363 cmdi.out.desc= (char*)"command";
365 cmdi.out.error= obc_error;
366 cmdi.doline= command_doline;
370 while ((arg=*++argv) && *arg=='-') {
373 case 's': device= arg; break;
374 case 'p': persist_fn= arg; break;
375 case 'v': picio_send_noise= atoi(arg); break;
376 default: badusage("unknown option");
380 persist_entrails_interpret();
384 r= oop_fd_nonblock(serial_fd, 1); if (r) diee("nonblock(serial_fd,1)");
386 events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0);
387 events->on_fd(events, serial_fd, OOP_EXCEPTION, read_exception, 0);
390 oop_sys_run(sys_events);
394 DEFINE_ERRORCODELIST_DATA