chiark / gitweb /
giant reorg abolishes TrainNum most of the time; working on making it build
[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
13 static const char *device= "/dev/ttya0";
14
15 /*---------- general event handling ----------*/
16
17 static void comms_error(const char *ch, const char *e1,
18                         const char *e2 /* may be 0 */) {
19   if (!e1 && !e2) e1= "end of file";
20   die("communications error: %s: %s%s%s", ch, e1, e2?": ":"", e2?e2:"");
21 }
22
23 static void *read_exception(oop_source *evts, int fd,
24                             oop_event evt, void *cl_v) {
25   const char *ch;
26   char bufc;
27   int r;
28
29   ch= (fd==UPO->fd ? UPO->desc :
30        fd==serial_fd ? "serial port" :
31        0);
32   
33   r= read(fd, &bufc, 1);
34   if (r==-1) comms_error(ch, "read error", strerror(errno));
35   else if (r==0) comms_error(ch, "reports exception, reads EOF", 0);
36   else comms_error(ch, "reports exception, but readable", 0);
37
38   return OOP_CONTINUE;
39 }
40
41 /*---------- logging etc. ----------*/
42
43 static char *transegn2suffixstring(Train *tra, Segment *seg) {
44   /* Either arg may be 0, in which case it's not included.
45    * Result string will be empty, or start with ": "
46    * Result string is from malloc.
47    * Never fails.
48    */
49   const char *trapn, *segpn;
50   char *s;
51   int r;
52
53   segpn= seg ? seg->i->pname : 0;
54   trapn= tra ? tra->pname : 0;
55
56   r= asprintf(&s, "%s%s%s%s%s",
57               segpn||trapn ? ":" : "",
58               segpn ? " @" : "",
59               segpn ? segpn : "",
60               trapn ? " " : "",
61               trapn ? trapn : "");
62   if (r<0) diee("vasprintf failed in transegn2suffixstring");
63   return s;
64 }
65
66 void vlogmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
67              const char *fmt, va_list al) {
68   oprintf(UPO, "message %s ", ec ? errorcodelist[ec] : "info");
69   ovprintf(UPO,fmt,al);
70   if (segi || tra) oprintf(UPO, ":");
71   if (segi) oprintf(UPO, " @%s", segi->pname);
72   if (tra) oprintf(UPO, " %s",  tra->pname);
73   oprintf(UPO, "\n");
74 }
75
76 void logmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
77             const char *fmt,...) {
78   va_list al;
79   va_start(al,fmt);
80   vlogmsg(ec,tra,segi,fmt,al);
81   va_end(al);
82 }
83
84 void safety_vpanic(Train *tra, Segment *seg,const char *fmt,va_list al) {
85   char *msg, *where;
86   PicInsn piob;
87
88   enco_pic_off(&piob);
89   serial_transmit(&piob);
90
91   if (vasprintf(&msg,fmt,al) < 0)
92     diee("vasprintf failed in safety_vpanic fmt=\"%s\"", fmt);
93
94   where= transegn2suffixstring(tra,seg);
95   die("fatal safety problem: %s%s", msg, where);
96 }
97
98 void safety_panic(Train *tra, Segment *seg, const char *fmt, ...) {
99   va_list al;
100   va_start(al,fmt);
101   safety_panic(tra, seg, fmt, al);
102 }
103
104 ErrorCode safety_problem(Train *tra, Segment *seg, const char *fmt,...) {
105   va_list al;
106   va_start(al,fmt);
107   vlogmsg(EC_Safety, tra, seg?seg->i:0, fmt, al);
108   va_end(al);
109   return EC_Safety;
110 }
111
112 /*---------- command channel handling (oop_read, obc) ----------*/
113
114 static void command_doline(ParseState *ps, CommandInput *cmdi) {
115   const CmdInfo *ci;
116   ci= some_needword_lookup(ps, toplevel_cmds, "command");
117   if (ci) ci->fn(ps,ci);
118 }
119
120 void oupicio(const char *dirn, const PicInsnInfo *pii, int objnum) {
121   if (!pii->argbits)
122     oprintf(UPO, "picio %s %s\n", dirn, pii->name);
123   else
124     oprintf(UPO, "picio %s %s %u\n", dirn, pii->name, objnum);
125 }
126
127 void vbadcmd(ParseState *ps, const char *fmt, va_list al) {
128   voerror(UPO,fmt,al);
129 }
130
131 static void obc_error(OutBufferChain *ch, const char *e1, const char *e2) {
132   comms_error(ch->desc, e1, e2);
133 }
134
135 void ouhex(const char *word, const Byte *command, int length) {
136   oprintf(UPO, "%s", word);
137   while (length) {
138     oprintf(UPO, " %02x", *command++);
139     length--;
140   }
141   oprintf(UPO, "\n");
142 }
143  
144 void die_vprintf_hook(const char *fmt, va_list al) {
145   ovprintf(UPO, fmt, al);
146 }
147  
148 void die_hook(void) {
149   int e;
150   e= obc_tryflush(UPO);
151   if (e) fprintf(stderr,"(unwritten command output: %s)\n",strerror(e));
152 }
153
154 /*---------- serial input (via oop) ----------*/
155
156 static PicInsn serial_buf;
157
158 static void *serial_readable(oop_source *evts, int fd,
159                              oop_event evt, void *u0) {
160   int r, buf_used;
161
162   r= read(serial_fd, &serial_buf.d, sizeof(serial_buf.d) - serial_buf.l);
163   if (r==0) die("serial port - eof");
164   if (r==-1) {
165     if (errno == EWOULDBLOCK || errno == EINTR)
166       return OOP_CONTINUE;
167     diee("serial port - read error");
168   }
169   assert(r>0);
170
171   buf_used= serial_buf.l;
172   buf_used += r;
173
174   for (;;) {
175     serial_buf.l= buf_used;
176     serial_moredata(&serial_buf);
177     if (!serial_buf.l) break;
178     buf_used -= serial_buf.l;
179     memmove(serial_buf.d, serial_buf.d + serial_buf.l, buf_used);
180     if (!buf_used) break;
181   }
182   serial_buf.l= buf_used;
183   return OOP_CONTINUE;
184 }
185
186 /*---------- serial port output (not via liboop) ----------*/
187
188 void serial_transmit(const PicInsn *pi) {
189   const PicInsnInfo *pii;
190   int objnum;
191
192   if ((pi->d[0] & 0xf8) == 0x90) {
193     SegmentNum segn;
194     const SegmentInfo *segi;
195     const char *delim;
196     
197     oprintf(UPO,"picio out polarity <");
198     for (segn=0, segi=info_segments, delim="";
199          segn < info_nsegments;
200          segn++, segi++) {
201       if (!segi->invertible) continue;
202       if (!picinsn_polarity_testbit(pi,segi)) continue;
203       oprintf(UPO,"%s%s", delim, segi->pname);
204       delim= ",";
205     }
206     oprintf(UPO,">\n");
207   } else {
208     picinsn_decode(pi, pic_command_infos, &pii, &objnum);
209     if (!pii)
210       oprintf(UPO, "picio out unknown\n");
211     else
212       oupicio("out",pii,objnum);
213   }
214
215   ouhex("picioh out", pi->d, pi->l);
216
217   /* note that the serial port is still in nonblocking mode.  if
218    * we ever buffer up far enough that the kernel wants to make us
219    * block, we should die! */
220   serial_transmit_now(pi->d, pi->l);
221 }
222
223 /*---------- initialisation ----------*/
224
225 int main(int argc, const char **argv) {
226   oop_source_sys *sys_events;
227   const char *arg;
228   int r;
229
230   while ((arg=*++argv) && *arg=='-') {
231     arg++;
232     switch (*arg++) {
233     case 's': device= arg; break;
234     default: badusage("unknown option");
235     }
236   }
237
238   records_parse(argv);
239
240   sys_events= oop_sys_new();  if (!sys_events) diee("oop_sys_new");
241   events= oop_sys_source(sys_events);  massert(events);
242
243   cmdi.out.desc= (char*)"command";
244   cmdi.out.fd= 1;
245   cmdi.out.error= obc_error;
246   cmdi.doline= command_doline;
247
248   cmdin_new(&cmdi, 0);
249
250   serial_open(device);
251   r= oop_fd_nonblock(serial_fd, 1);  if (r) diee("nonblock(serial_fd,1)");
252
253   events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0);
254   events->on_fd(events, serial_fd, OOP_EXCEPTION, read_exception, 0);
255
256   sta_startup();
257   oop_sys_run(sys_events);
258   abort();
259 }
260
261 DEFINE_ERRORCODELIST_DATA