chiark / gitweb /
print object numbers in hex
[trains.git] / hostside / realtime.c
1 /*
2  * main program for realtime control
3  */
4
5 #include "realtime.h"
6
7 const char *progname= "realtime";
8
9 /*---------- global variables ----------*/
10
11 CommandInput cmdi;
12 int picio_send_noise= 1;
13
14 static const char *device= "/dev/ttya0";
15
16 /*---------- general event handling ----------*/
17
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:"");
22 }
23
24 static void *read_exception(oop_source *evts, int fd,
25                             oop_event evt, void *cl_v) {
26   const char *ch;
27   char bufc;
28   int r;
29
30   ch= (fd==UPO->fd ? UPO->desc :
31        fd==serial_fd ? "serial port" :
32        0);
33   
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);
38
39   return OOP_CONTINUE;
40 }
41
42 /*---------- logging etc. ----------*/
43
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.
48    * Never fails.
49    */
50   const char *trapn, *segpn;
51   char *s;
52   int r;
53
54   segpn= seg ? seg->i->pname : 0;
55   trapn= tra ? tra->pname : 0;
56
57   r= asprintf(&s, "%s%s%s%s%s",
58               segpn||trapn ? ":" : "",
59               segpn ? " @" : "",
60               segpn ? segpn : "",
61               trapn ? " " : "",
62               trapn ? trapn : "");
63   if (r<0) diee("vasprintf failed in transegn2suffixstring");
64   return s;
65 }
66
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");
70   ovprintf(UPO,fmt,al);
71   if (segi || tra) oprintf(UPO, ":");
72   if (segi) oprintf(UPO, " @%s", segi->pname);
73   if (tra) oprintf(UPO, " %s",  tra->pname);
74   oprintf(UPO, "\n");
75 }
76
77 void logmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
78             const char *fmt,...) {
79   va_list al;
80   va_start(al,fmt);
81   vlogmsg(ec,tra,segi,fmt,al);
82   va_end(al);
83 }
84
85 void safety_vpanic(Train *tra, Segment *seg,const char *fmt,va_list al) {
86   char *msg, *where;
87   PicInsn piob;
88
89   enco_pic_off(&piob);
90   serial_transmit(&piob);
91
92   if (vasprintf(&msg,fmt,al) < 0)
93     diee("vasprintf failed in safety_vpanic fmt=\"%s\"", fmt);
94
95   where= transegn2suffixstring(tra,seg);
96   die("fatal safety problem: %s%s", msg, where);
97 }
98
99 void safety_panic(Train *tra, Segment *seg, const char *fmt, ...) {
100   va_list al;
101   va_start(al,fmt);
102   safety_vpanic(tra, seg, fmt, al);
103 }
104
105 ErrorCode safety_problem(Train *tra, Segment *seg, const char *fmt,...) {
106   va_list al;
107   va_start(al,fmt);
108   vlogmsg(EC_Safety, tra, seg?seg->i:0, fmt, al);
109   va_end(al);
110   return EC_Safety;
111 }
112
113 /*---------- printing nmra data ----------*/
114
115 typedef struct {
116   const Byte *i, *o;
117   int npending;
118   unsigned datapending;
119 } NmraDecCtx;
120
121 static void nmradec_init(NmraDecCtx *d, const PicInsn *pi) {
122   d->i= pi->d;
123   d->o= pi->d + pi->l;
124   d->npending= 0;
125   d->datapending= 0;
126 }
127
128 static void nmradec_getbits(NmraDecCtx *d, int *nbits_io, unsigned *bits_r) {
129   int use= *nbits_io;
130   while (d->npending < use) {
131     if (d->i >= d->o) break;
132     d->datapending <<= 7;
133     d->datapending |= *d->i & 0x7f;
134     d->npending += 7;
135     if (!(*d->i++ & 0x80)) break;
136   }
137   if (d->npending < use) use= d->npending;
138   d->npending -= use;
139   *bits_r= (d->datapending >> d->npending) & ~(~0u << use);
140   *nbits_io= use;
141 }
142   
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),
147                               void *u) {
148   /* reasonchars:
149    *    #  - bad checksum
150    *    !  - too little idle
151    *    $  - truncated
152    */
153   NmraDecCtx d;
154   Nmra n;
155   int need_idle=7, got_idle=0;
156   unsigned got, csum;
157   int nbits;
158   
159   nmradec_init(&d,pi);
160   for (;;) {
161     nbits=1; nmradec_getbits(&d,&nbits,&got);
162     if (got) { got_idle++; continue; }
163     if (got_idle) on_idle(u,got_idle);
164     if (!nbits) return;
165     if (got_idle < need_idle) on_error(u,'!');
166     got_idle= 0;
167
168     n.l=0; csum=0;
169     do {
170       nbits=8; nmradec_getbits(&d,&nbits,&got);
171       n.d[n.l++]= got;
172       csum ^= got;
173       if (nbits<8) on_error(u,'$');
174
175       nbits=1; nmradec_getbits(&d,&nbits,&got);
176       if (nbits<1) on_error(u,'$');
177     } while (nbits && !got);
178
179     if (csum) on_error(u,'#');
180     else n.l--;
181
182     on_packet(u,&n);
183     need_idle= 14;
184   }
185 }
186
187 static void opn_idle(void *u, int idle) {
188   static const char dots[]= "......."; /* at least base/2 */
189   const int base= 14;
190   int largers= idle / base;
191   int units= idle % base;
192
193   oprintf(UPO," %.*s%.*s%.*s",
194           units/2, dots,
195           largers, ":::::::" /* enough */,
196           (units+1)/2, dots);
197 }
198 static void opn_error(void *u, int rc) { oprintf(UPO," %c",rc); }
199 static void opn_packet(void *u, const Nmra *n) {
200   int i;
201   const char *delim= " <";
202   for (i=0; i<n->l; i++) {
203     oprintf(UPO,"%s%02x",delim,n->d[i]);
204     delim=" ";
205   }
206   oprintf(UPO,">");
207 }
208
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);
212   oprintf(UPO,"\n");
213 }
214   
215 /*---------- command channel handling (oop_read, obc) ----------*/
216
217 static void command_doline(ParseState *ps, CommandInput *cmdi) {
218   const CmdInfo *ci;
219   ci= some_needword_lookup(ps, toplevel_cmds, "command");
220   if (ci) ci->fn(ps,ci);
221 }
222
223 void oupicio(const char *dirn, const PicInsnInfo *pii, int objnum) {
224   if (!pii->argbits)
225     oprintf(UPO, "picio %s %s\n", dirn, pii->name);
226   else
227     oprintf(UPO, "picio %s %s %#x\n", dirn, pii->name, objnum);
228 }
229
230 void vbadcmd(ParseState *ps, const char *fmt, va_list al) {
231   voerror(UPO,fmt,al);
232 }
233
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:"");
238   exit(-1);
239 }
240
241 void ouhex(const char *word, const Byte *command, int length) {
242   oprintf(UPO, "%s", word);
243   while (length) {
244     oprintf(UPO, " %02x", *command++);
245     length--;
246   }
247   oprintf(UPO, "\n");
248 }
249  
250 void die_vprintf_hook(const char *fmt, va_list al) {
251   if (events) ovprintf(UPO, fmt, al);
252 }
253  
254 void die_hook(void) {
255   int e;
256   e= events ? obc_tryflush(UPO) : 0;
257   if (e) fprintf(stderr,"(unwritten command output: %s)\n",strerror(e));
258 }
259
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,
264                                      "segment");
265   if (!segi) return 0;
266   if (segi_r) *segi_r= segi;
267   if (seg_r) *seg_r= segments + (segi - info_segments);
268   return 1;
269 }
270
271 /*---------- serial input (via oop) ----------*/
272
273 static PicInsn serial_buf;
274
275 static void *serial_readable(oop_source *evts, int fd,
276                              oop_event evt, void *u0) {
277   int r, buf_used;
278
279   r= read(serial_fd, serial_buf.d + serial_buf.l,
280           sizeof(serial_buf.d) - serial_buf.l);
281
282   if (r==0) die("serial port - eof");
283   if (r==-1) {
284     if (errno == EWOULDBLOCK || errno == EINTR)
285       return OOP_CONTINUE;
286     diee("serial port - read error");
287   }
288   assert(r>0);
289
290   buf_used= serial_buf.l + r;
291
292   for (;;) {
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;
299   }
300   serial_buf.l= buf_used;
301   return OOP_CONTINUE;
302 }
303
304 /*---------- serial port output (not via liboop) ----------*/
305
306 void serial_transmit(const PicInsn *pi) {
307   const PicInsnInfo *pii;
308   int objnum, suppress=0;
309
310   if ((pi->d[0] & 0xf8) == 0x90) {
311     SEG_IV;
312     const char *delim;
313     
314     oprintf(UPO,"picio out polarity <");
315     delim="";
316     FOR_SEG {
317       if (!segi->invertible) continue;
318       if (!picinsn_polarity_testbit(pi,segi)) continue;
319       oprintf(UPO,"%s%s", delim, segi->pname);
320       delim= ",";
321     }
322     oprintf(UPO,">\n");
323   } else if (pi->d[0] == 0xff) {
324     if (picio_send_noise < 3)
325       suppress= 1;
326     else 
327       oprint_nmradata(pi);
328   } else {
329     picinsn_decode(pi, pic_command_infos, &pii, &objnum);
330     if (!pii)
331       oprintf(UPO, "picio out unknown\n");
332     else if (pii->noiselevel > picio_send_noise)
333       suppress= 1;
334     else
335       oupicio("out",pii,objnum);
336   }
337
338   if (!suppress && picio_send_noise >= 2)
339     ouhex("picioh out", pi->d, pi->l);
340
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);
345 }
346
347 /*---------- initialisation ----------*/
348
349 int main(int argc, const char **argv) {
350   oop_source_sys *sys_events;
351   const char *arg;
352   int r;
353
354   persist_map_veryearly();
355
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();
359
360   sys_events= oop_sys_new();  if (!sys_events) diee("oop_sys_new");
361   events= oop_sys_source(sys_events);  massert(events);
362
363   cmdi.out.desc= (char*)"command";
364   cmdi.out.fd= 1;
365   cmdi.out.error= obc_error;
366   cmdi.doline= command_doline;
367
368   cmdin_new(&cmdi, 0);
369
370   while ((arg=*++argv) && *arg=='-') {
371     arg++;
372     switch (*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");
377     }
378   }
379
380   persist_entrails_interpret();
381   records_parse(argv);
382
383   serial_open(device);
384   r= oop_fd_nonblock(serial_fd, 1);  if (r) diee("nonblock(serial_fd,1)");
385
386   events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0);
387   events->on_fd(events, serial_fd, OOP_EXCEPTION, read_exception, 0);
388
389   sta_startup();
390   oop_sys_run(sys_events);
391   abort();
392 }
393
394 DEFINE_ERRORCODELIST_DATA