chiark / gitweb /
hostside: more length for bavarian
[trains.git] / hostside / simulate.c
1 /*
2  * realtime
3  * simulation harness
4  */
5
6 #include "realtime.h"
7
8 typedef struct SimTimeout {
9   struct { struct SimTimeout *back, *next; } l;
10   TimeoutEvent *toev;
11 } SimTimeout;
12
13 typedef void SimEventFn(void);
14
15 int simlog_full;
16 const char *simulate;
17
18 static SimEventFn se_timestamp, se_timerevent, se_command;
19 static SimEventFn se_serial, se_serial_detcompr, se_samedet, se_eof;
20
21 static SimTimeout *simtimeouts;
22
23 #define DETSAVESLN2 4
24 #define DETSAVES (1<<DETSAVESLN2) /* encoded in letters so must be <=26 ! */
25
26 static FILE *simoutput;
27 static FILE *siminput;
28 static char *sevent_buf;
29 static size_t sevent_buflen;
30 static int sevent_lno;
31 static Byte sevent_lastdet[DETSAVES][2];
32
33 static SimEventFn *sevent_type;
34 static char *sevent_data;
35 static struct timeval sevent_abst;
36
37 #define PICMSG_DETECT_01 (PICMSG_DETECT0^PICMSG_DETECT1)
38
39 /*---------- writing simulation log ----------*/
40
41 static Byte simlog_lastdet[DETSAVES][2];
42 static uint32_t simlog_detcompr_lcongx;
43
44 void simlog_ccb(char *m, size_t l, void *u) {
45   size_t r;
46   if (!simoutput || simoutput==stdout) return;
47
48   r= fwrite(m,1,l,simoutput);
49   if (r!=l) {
50     assert(ferror(simoutput));
51     diee("copy output to simulation log");
52   }
53 }
54 void simlogv(const char *fmt, va_list al) {
55   if (simoutput==stdout) ouvprintf(fmt,al);
56   else if (simoutput) vfprintf(simoutput,fmt,al);
57 }
58 void simlog(const char *fmt, ...) {
59   va_list al;
60   va_start(al,fmt);
61   simlogv(fmt,al);
62   va_end(al);
63 }
64 void simlog_flush(void) {
65   if (simoutput==stdout) obc_tryflush(&cmdi.out);
66   else if (simoutput)
67     if (ferror(simoutput) || fflush(simoutput))
68       diee("write simulation log");
69 }
70
71 void simlog_serial(const Byte *data, int length) {
72   static Byte db0, db1;
73   int is_det;
74
75   if (!length) return;
76
77   db0= data[0] & ~PICMSG_DETECT_01;
78
79   is_det= PICMSG_DETECT0_P(db0) && length==2 && !((db1=data[1]) & 0x80);
80   if (is_det) {
81     int i;
82     for (i=0; i<DETSAVES; i++)
83       if (simlog_lastdet[i][0]==db0 && simlog_lastdet[i][1]==db1) {
84         simlog("%c\n", ((data[0] & PICMSG_DETECT_01) ? 'A' : 'a') + i);
85         simlog_flush();
86         return;
87       }
88     /* not found, choose a slot to replace */
89     simlog_detcompr_lcongx += 5;     /* WP Linear_congruential_generator */
90     simlog_detcompr_lcongx *= 69069; /* values are those also used by GCC */
91     i= (simlog_detcompr_lcongx >> (30 - DETSAVESLN2)) &
92        ((1UL << DETSAVESLN2)-1);
93     simlog_lastdet[i][0]= db0;
94     simlog_lastdet[i][1]= db1;
95     simlog("picioh in detcompr%c", 'A' + i);
96   } else {
97     simlog("picioh in suppressed");
98   }
99   while (length--) simlog(" %02x",*data++);
100   simlog("\n");
101   simlog_flush();
102 }
103 void simlog_open(const char *fn) {
104   int r;
105   
106   if (!fn) fn= "+realtime.log";
107   if (!strcmp(fn,"-")) {
108     simoutput= stdout; /* we don't really use this - see vsimlog */
109   } else if (fn[0]) {
110     r= unlink(fn);
111     if (r && errno!=ENOENT && errno!=EPERM)
112       diee("unlink old simulation log %s",fn);
113     simoutput= fopen(fn,"w");
114     if (!simoutput) diee("open simulation log %s",fn);
115   }
116 }
117
118 /*---------- simulation input stream parser ----------*/
119
120 static void simbad(const char *how) __attribute__((noreturn));
121 static void simbad(const char *how) {
122   die("simulation failed (line %d, in `%s'): %s", sevent_lno, sevent_buf, how);
123 }
124
125 static void sevent(void) {
126   ssize_t gr;
127   char *p;
128
129   if (sevent_type)
130     return;
131
132   for (;;) {
133     sevent_lno++;
134
135     gr= getline(&sevent_buf,&sevent_buflen,siminput);
136     if (feof(siminput)) { sevent_type= se_eof; return; }
137     if (ferror(siminput)) diee("read simulation input failed");
138     assert(gr>0);
139     assert(sevent_buf[gr-1]=='\n');
140     sevent_buf[gr-1]= 0;
141
142     if (gr==2 && CTYPE(isalpha,sevent_buf[0])) {
143       sevent_type= se_samedet;
144       sevent_data= &sevent_buf[0];
145       return;
146     }
147
148 #define IF_ET(pfx, et)                                                  \
149     if (!strncmp(pfx, sevent_buf, sizeof(pfx)-1)                        \
150         && (sevent_type=(et), p=sevent_buf+sizeof(pfx)-1))
151
152     IF_ET("command-in ", se_command) {
153       sevent_data= p;
154       return;
155     }
156     IF_ET("picioh in detcompr", se_serial_detcompr) {
157       sevent_data= p;
158       return;
159     }
160     IF_ET("picioh in ", se_serial) {
161       p= strchr(p,' ');
162       if (!p) simbad("missing space after `in'");
163       sevent_data= p+1;
164       return;
165     }
166     IF_ET("timer-event ", se_timerevent) {
167       sevent_data= p;
168       return;
169     }
170     IF_ET("timestamp ", se_timestamp) {
171       int n, r;
172       n=-1;
173       r= sscanf(p,"%ld.%ld%n", &sevent_abst.tv_sec, &sevent_abst.tv_usec, &n);
174       if (r<2 || !(n==-1 || !p[n])) simbad("unparseable timestamp");
175       return;
176     }
177   }
178 }
179
180 /*---------- hooks for eventhelp ----------*/
181
182 void sim_toev_start(TimeoutEvent *toev) {
183   SimTimeout *s= mmalloc(sizeof(*s));
184   s->toev= toev;
185   DLIST1_PREPEND(simtimeouts,s,l);
186 }
187
188 static void sim_toev_remove(SimTimeout *s) {
189   DLIST1_REMOVE(simtimeouts,s,l);
190   free(s);
191 }
192
193 void sim_toev_stop(TimeoutEvent *toev) {
194   SimTimeout *s;
195   for (s=simtimeouts; !(s->toev==toev); s=s->l.next);
196   sim_toev_remove(s);
197 }
198
199 void sim_mgettimeofday(struct timeval *tv) {
200   sevent();
201   *tv= sevent_abst;
202   if (sevent_type==se_timestamp)
203     sevent_type= 0;
204   else
205     fprintf(stderr,"simulation warning -"
206             " mgettimeofday desynched at line %d\n", sevent_lno);
207 }
208
209 /*---------- simulation events ----------*/
210
211 static void se_timestamp(void) { }
212
213 static void se_timerevent(void) {
214   TimeoutEvent *toev;
215   SimTimeout *s;
216   char *delim;
217
218   delim= strchr(sevent_data,'.');
219   if (!delim) simbad("missing `.' in timer event name");
220   *delim= 0;
221
222   for (s=simtimeouts; s; s=simtimeouts->l.next) {
223     toev= s->toev;
224     if (!strcmp(toev->pclass, sevent_data) &&
225         !strcmp(toev->pinst, delim+1))
226       goto found;
227   }
228   *delim='.';  simbad("timeout event not found");
229   
230  found:
231   sim_toev_remove(s);
232   toev_callback(0, sevent_abst, toev);
233 }
234
235 static int unhex(const char *str, Byte *buf, int buf_len) {
236   char c[3], *ep;
237   int buf_used;
238   const char *p;
239
240   c[2]= 0;
241   for (p=str, buf_used=0;
242        ;
243        ) {
244     if (*p==' ') p++;
245     if (!(c[0]= *p++)) break;
246     if (!(c[1]= *p++)) simbad("odd number of hex digits");
247
248     if (buf_used==buf_len) simbad("serial_buf overrun");
249
250     buf[buf_used++]= strtoul(c,&ep,16);
251     if (*ep) simbad("bad hex");
252   }
253   return buf_used;
254 }
255  
256 static void se_serial(void) {
257   int buf_used;
258
259   buf_used= serial_buf.l;
260   buf_used += unhex(sevent_data, serial_buf.d + buf_used,
261                     sizeof(serial_buf.d) - buf_used);
262   serial_indata_process(buf_used);
263 }
264
265 static void serial_detevent(Byte m[2], Byte or_m0) {
266   int buf_used;
267   buf_used= serial_buf.l+2;
268   if (buf_used > sizeof(serial_buf.d)) simbad("serial_buf det overrun");
269   serial_buf.d[serial_buf.l]=   m[0] | or_m0;
270   serial_buf.d[serial_buf.l+1]= m[1];
271   serial_indata_process(buf_used);
272 }
273
274 static void se_serial_detcompr(void) {
275   int i, l;
276
277   i= *sevent_data++ - 'A';
278   if (i<0 || i>=DETSAVES) simbad("detcompr bad slot specifier");
279   l= unhex(sevent_data, sevent_lastdet[i], 2);
280   if (l!=2) simbad("detcompr wrong message length");
281   serial_detevent(sevent_lastdet[i],0);
282   sevent_lastdet[i][0] &= ~PICMSG_DETECT_01;
283 }
284 static void se_samedet(void) {
285   int i, d;
286   i= *sevent_data - 'a';
287   d= *sevent_data < 'a'; /* A is before a */
288   if (d) i -= 'A'-'a';
289   if (i<0 || i>=DETSAVES || !sevent_lastdet[i][0]) simbad("detsame bad slot");
290   serial_detevent(sevent_lastdet[i], d ? PICMSG_DETECT_01 : 0);
291 }
292
293 static void se_command(void) {
294   ParseState ps;
295   ps.remain= sevent_data;
296   ps.thisword= 0;
297   ps.lthisword= 0;
298   cmdi.doline(&ps,&cmdi);
299 }
300
301 static void se_eof(void) {
302   exit(0);
303 }
304
305 /*---------- core ----------*/
306
307 void sim_initialise(const char *logduplicate) {
308   obc_init_core(&cmdi.out);
309   serial_fd= open("/dev/null",O_WRONLY);
310   if (serial_fd<0) diee("open /dev/null for dummy serial");
311   siminput= fopen(simulate,"r");
312   if (!siminput) diee("open simulation input %s",simulate);
313   if (logduplicate)
314     simlog_open(logduplicate);
315 }
316
317 void sim_run(void) {
318   SimEventFn *fn;
319   for (;;) {
320     sevent();
321     fn= sevent_type;
322     sevent_type= 0;
323     fn();
324     obc_tryflush(&cmdi.out);
325   }
326 }