chiark / gitweb /
realtime: movpos: debug output: exclude some more stuff from the default movpos output
[trains.git] / hostside / record.c
1 /*
2  * ASCII-format record and config file reader/writer
3  *
4  * File format:
5  *
6  *  max-trains num
7  *  train <trainpn> is <addr> <head>+<detectable>+<tail>
8  *  train <trainpn> home [-]<segpn> [-]<segpn> [-]<segpn>
9  *  train <trainpn> step <step>=<speed>
10  *  train <trainpn> stops <step> at <distance> after <milliseconds>
11  *  seg <segpn> has [-]<ownerpn> [inverted]
12  *  seg <segpn> at <movposcomb>
13  *  adjunct <trainoradjpn> <adjpn> is <addr> <nmranum>
14  *  adjunct <trainoradjpn> <adjpn> is <addr> step [-]<nmranum>
15  *  adjunct - is <addr> <nmranum>             } prevents enabling adjunct;
16  *  adjunct - is <addr> step [-]<nmranum>     }  xmits turning it off
17  *
18  * speed is floating point in m/s
19  */
20
21 #include <sys/mman.h>
22
23 #include "record-i.h"
24 #include "record-l.h"
25
26 int n_adjtargs, n_adjaddrs;
27 AdjunctsTarget *adjtargs;
28 AdjunctsAddr **adjaddrs;
29
30 /*---------- input and error handling ----------*/
31
32 static const char *filename;
33
34 void record_yyerror(const char *m) {
35   die("config: %s:%d: %s", filename, record_yylineno, m);
36 }
37
38 /*---------- pname lookup (also handles train counting) ----------*/
39
40 static char **train_pnames;
41
42 Train *record_pname2train(const char *pname) {
43   int tran;
44   char **trap;
45   
46   for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
47     if (!strcmp(*trap, pname)) goto found;
48   }
49   if (trains) record_yyerror("number of trains changed between passes!");
50   tran= n_trains++;
51   train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
52   train_pnames[tran]= mstrdup(pname);
53
54 found:
55   if (!trains) return 0;
56   return trains + tran;
57 }
58
59 Segment *record_pname2seg(const char *pname) {
60   SEG_IV;
61
62   if (!segments) return 0;
63
64   FOR_SEG
65     if (!strcmp(segi->pname, pname))
66       return seg;
67   
68   return 0; /* silently discard data for segments no longer in the layout */
69 }
70
71 #define SLIST_FIND_OR_INSERT(head,search,node,found,fillin) do{ \
72     for ((search)= &(head);                                     \
73          ((node)=*(search)) && !(found);                        \
74          (search)= &(node)->next);                              \
75     if (!(node)) {                                              \
76       *(search)= (node)= mmalloc(sizeof(*(node)));              \
77       fillin;                                                   \
78     }                                                           \
79   }while(0)
80
81 #define ARY_FIND_OR_APPEND(things,n_things,i,entry,found,fillin) do{    \
82     for ((i)=0, (entry)=(things);                                       \
83          (i)<(n_things) && !(found);                                    \
84          (i)++, (entry)++);                                             \
85     if ((i) >= (n_things)) {                                            \
86       (n_things)++;                                                     \
87       (things)= mrealloc((things), sizeof(*things) * (n_things));       \
88       entry= &(things)[(i)];                                            \
89       fillin;                                                           \
90     }                                                                   \
91   }while(0)
92
93 AdjunctsAddr *record_adjaddr(int num) {
94   AdjunctsAddr **search;
95   int ix;
96   
97   if (num<1 || num>0x3ff) record_yyerror("adjunct address out of range");
98
99   ARY_FIND_OR_APPEND
100     (adjaddrs,n_adjaddrs, ix,search, (*search)->addr == num, ({
101       int i;
102       AdjunctsAddr *item= mmalloc(sizeof(*item));
103       item->next= 0;
104       item->addr= num;
105       item->speedstep= 0;
106       item->current= item->permit= item->all= 0;
107       for (i=0; i<4; i++) item->rn[i].pi.l= 0;
108       *search= item;
109     }));
110   
111   return *search;
112 }
113
114 AdjunctsAdjunct *record_pname2adjunct(const char *targpn, const char *adjpn) {
115   int i;
116   AdjunctsTarget *targ;
117   AdjunctsAdjunct *adj;
118
119   ARY_FIND_OR_APPEND
120     (adjtargs,n_adjtargs,i,targ, !strcmp(targ->pname, targpn), ({
121       targ->pname= mstrdup(targpn);
122       targ->n_adjs= 0;
123       targ->adjs= 0;
124     }));
125
126   ARY_FIND_OR_APPEND
127     (targ->adjs,targ->n_adjs,i,adj, !strcmp(adj->pname, adjpn), ({
128       adj->pname= mstrdup(adjpn);
129       adj->a= 0;
130       adj->bits= 0;
131     }));
132
133   return adj;
134 }
135
136 /*---------- zone allocator for strings ----------*/
137
138 typedef struct TempZoneBlock TempZoneBlock;
139 struct TempZoneBlock {
140   TempZoneBlock *next; /* for deallocation */
141   char buf[1];
142 };
143
144 static TempZoneBlock *tempzones_head;
145 static char *tempzone_ptr;
146 static int tempzone_remain, tempzone_head_len;
147
148 char *record_tempzone_strdup(const char *s) {
149   int l;
150   char *r;
151   
152   l= strlen(s) + 1;
153   if (l > tempzone_remain) {
154     TempZoneBlock *new;
155     assert(l < INT_MAX/20);
156     tempzone_head_len= tempzone_remain= l*10 + 65536;
157     new= mmalloc(sizeof(new) + tempzone_remain);
158     tempzone_ptr= (char*)new + sizeof(new);
159     new->next= tempzones_head;
160     tempzones_head= new;
161   }
162   r= tempzone_ptr;
163   tempzone_remain -= l;
164   tempzone_ptr += l;
165   memcpy(r,s,l);
166   return r;
167 }
168
169 void record_tempzone_clear(void) {
170   TempZoneBlock *clear, *clear_next;
171   if (!tempzones_head) return;
172
173   clear= tempzones_head->next;
174   while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
175   tempzones_head->next= 0;
176   tempzone_remain= tempzone_head_len;
177   tempzone_ptr= tempzones_head->buf;
178 }
179
180 static void record_tempzone_freeall(void) {
181   record_tempzone_clear();
182   free(tempzones_head);
183   tempzones_head= 0;
184   tempzone_ptr= 0;
185   tempzone_remain= tempzone_head_len= 0;
186 }
187
188 /*---------- semantic actions ----------*/
189 /*
190  * These are all called only during the second pass, when trains is non-0.
191  * Also, all Train* and Segment* arguments are known to be non-0.
192  */
193
194 void record_train_is(Train *tra, int addr, int head, int det, int tail) {
195   tra->addr= addr;
196   tra->head= head;
197   tra->detectable= det;
198   tra->tail= tail;
199 }
200
201 void record_train_home(Train *tra, int backw, Segment *seg) {
202   if (!tra) return;
203   seg->home= tra;
204   seg->ho_backwards= backw;
205 }
206   
207 void record_train_step_speed(Train *tra, int step, double speed) {
208   if (step<1 || step>SPEEDSTEPS)
209     record_yyerror("speed step out of range 0..126");
210   tra->speedcurve[step]= speed;
211 }
212   
213 void record_seg_has(Segment *seg, int backw, Train *tra, int inverted) {
214   seg->owner= tra;
215   seg->tr_backwards= backw;
216   seg->seg_inverted= inverted;
217 }
218
219 void record_seg_at(Segment *seg, const char *movposcombpname) {
220   const SegPosCombInfo *spci;
221   int poscomb;
222   
223   for (poscomb=0, spci=seg->i->poscombs;
224        poscomb < seg->i->n_poscombs;
225        poscomb++, spci++)
226     if (!strcmp(spci->pname, movposcombpname))
227       goto found;
228   return;
229   
230 found:
231   seg->movposcomb= poscomb;
232 }
233
234 /*---------- adjuncts ----------*/
235
236 static void record_adjunct_bits(AdjunctsAdjunct *adj, AdjunctsAddr *addr,
237                                 unsigned bits) {
238   if (!adj) /* `-' adjunct */
239     return;
240   if (adj->a && adj->a != addr)
241     record_yyerror("adjunct includes multiple decoder addresses");
242   adj->a= addr;
243   adj->bits |= bits;
244   addr->permit |= bits;
245   addr->all |= bits;
246 }
247
248 void record_adjunct_nmrafunc(AdjunctsAdjunct *adj, AdjunctsAddr *addr,
249                              int num) {
250   int bitval;
251
252   if (num<0 || num>12) record_yyerror("NMRA function out of range 0..12");
253   bitval= 1<<num;
254
255   if (addr->all & bitval)
256     record_yyerror("adjunct bit respecified");
257   
258   record_adjunct_bits(adj,addr,bitval);
259 }
260
261 void record_adjunct_motor(AdjunctsAdjunct *adj, AdjunctsAddr *addr,
262                           int speed) {
263   if (speed<-126 || speed>126) record_yyerror("motor speed out of range");
264   if (!speed) record_yyerror("zero motor speed?!");
265   if (addr->speedstep) record_yyerror("adjunct specifies multiple speeds");
266
267   record_adjunct_bits(adj,addr,ADJS_SPEEDSTEP_BIT);
268
269   addr->speedstep= speed;
270   if (addr->speedstep<0) {
271     addr->all |= ADJS_SPEEDSTEP_REVERSE;
272     addr->speedstep= -addr->speedstep;
273   }
274 }
275
276 static int pname1st_compar(const void *av, const void *bv) {
277   const char *const *a= av;
278   const char *const *b= bv;
279   return strcmp(*a,*b);
280 }
281
282 static int adjaddrp_compar(const void *av, const void *bv) {
283   const AdjunctsAddr *const *a= av;
284   const AdjunctsAddr *const *b= bv;
285   return (*a)->addr - (*b)->addr;
286 }
287
288 static void adjuncts_postprocess(void) {
289   int i;
290   AdjunctsTarget *targ;
291   
292   qsort(adjtargs, n_adjtargs, sizeof(*adjtargs), pname1st_compar);
293   qsort(adjaddrs, n_adjaddrs, sizeof(*adjaddrs), adjaddrp_compar);
294
295   for (i=0, targ=adjtargs;
296        i<n_adjtargs;
297        i++, targ++)
298     qsort(targ->adjs, targ->n_adjs, sizeof(*targ->adjs), pname1st_compar);
299 }
300
301 /*---------- speed curves ----------*/
302
303 static SpeedRange *rangebuf;
304 static int rangebufsz, rangebufused;
305
306 void record_train_stopregime_count(void) {
307   rangebufsz++;
308 }
309
310 void record_train_stopregime(Train *tra, int step, int xs, int ts) {
311   Train *other;
312   SpeedRange *new;
313   int i;
314
315   if (step<1 || step>126) record_yyerror("stop regime step out of range");
316
317   if (rangebufused >= rangebufsz)
318     record_yyerror("more speed points on 2nd pass!");
319
320   if (!tra->speedregimes) {
321     tra->speedregimes= rangebuf + rangebufused;
322   }
323
324   /* Make room for an extra speed step point for this train,
325    * by moving all of the other trains up.  First move the data: */
326   memmove(tra->speedregimes + tra->n_speedregimes + 1,
327           tra->speedregimes + tra->n_speedregimes,
328           (char*)(rangebuf + rangebufused)
329           - (char*)(tra->speedregimes + tra->n_speedregimes));
330   /* Then adjust everyone's pointers: */
331   for (i=0, other=trains;
332        i<n_trains;
333        i++, other++)
334     if (other != tra && other->speedregimes &&
335         other->speedregimes >= tra->speedregimes)
336       other->speedregimes++;
337   /* ... that whole thing is O(n^2) if the regimes aren't sorted by
338    * train; we hope they are, in which case we are always adding to
339    * the last train in the list and there is then no data to copy,
340    * which makes it O(n*t) where n is the number of regimes and t is
341    * the number of trains.  If we cared we could optimise away the
342    * loop in the common case where this train is right at the end but
343    * a special case seems like asking for a bug when we do have out of
344    * order speed points. */
345
346   new= &tra->speedregimes[tra->n_speedregimes++];
347   rangebufused++;
348
349   new->speed= step;
350   new->xs= xs;
351   new->ts= ts;
352 }
353
354 static int speedregime_compare(const void *av, const void *bv) {
355   const SpeedRange *a= av, *b= bv;
356   if (a->speed == b->speed)
357     record_yyerror("multiple identical speed regimes");
358   return a->speed > b->speed ? 1 : -1;
359 }
360
361 static void speeds_postprocess(void) {
362   TRA_IV;
363   int step, i;
364
365   FOR_TRA {
366     if (!tra->speedregimes || tra->speedcurve[1]<0) {
367       tra->addr= -1;
368       continue;
369     }
370     if (tra->n_speedregimes < 1)
371       die("config: speed curve too short for %s", tra->pname);
372
373     qsort(tra->speedregimes, tra->n_speedregimes,
374           sizeof(*tra->speedregimes), speedregime_compare);
375     if (tra->speedregimes[tra->n_speedregimes-1].speed < 126)
376       die("config: speed curve missing top point for %s", tra->pname);
377
378     for (step=1; step<=SPEEDSTEPS; step++)
379       if (tra->speedcurve[step] < 0)
380         die("config: speed curve for %s missing step %d", tra->pname, step);
381     tra->speedcurve[0]= 0;
382
383     for (i=0; i<tra->n_speedregimes; i++) {
384       step= tra->speedregimes[i].speed;
385       tra->speedregimes[i].speed= tra->speedcurve[step];
386     }
387   }
388 }
389
390 /*---------- persistent data file layout ----------*/
391
392 static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
393   void *r;
394   size_t totalsz;
395   
396   while (*offset % sz) {
397     if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
398     (*offset)++;
399   }
400   totalsz= sz*count;
401   if (mapbase) {
402     r= (Byte*)mapbase + *offset;
403     memset(r,0,totalsz);
404   } else {
405     r= 0;
406   }
407   *offset += totalsz;
408   return r;
409 }
410
411 static void alloc(void) {
412   TRA_IV;
413   SEG_IV;
414   void *mapbase=0;
415   char **trap;
416   int phase, offset, datalen=0, step;
417
418 #define ALLOC(array,count) \
419   ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
420
421   for (phase=0; ; phase++) {
422     /* phase 0: count up how much space will be needed
423      * phase 1: fill in most of the details, leaving header blank
424      * phase 2: fill in the header, and then exit before doing rest
425      */
426     offset= 0;
427
428     if (phase==1)
429       mapbase= record_allocate(datalen);
430
431 #define PHI_SAVE(x) {                           \
432       typeof(x) *p;                             \
433       ALLOC(p,1);                               \
434       if (phase==2)                             \
435         memcpy(p, &(x), sizeof(x));             \
436     }
437
438     DO_PERSIST_HEADER_ITEMS(PHI_SAVE, PHI_SAVE, PHI_SAVE)
439     
440     if (phase==2)
441       break;
442
443     ALLOC(trains, n_trains);
444     FOR_TRAIN(tra, trap=train_pnames, trap++) {
445       char *pname;
446       ALLOC(pname, strlen(*trap)+1);
447       if (phase) {
448         strcpy(pname, *trap);
449         free(*trap);
450         tra->pname= *trap= pname;
451         tra->addr= -1;
452         tra->foredetect= 0;
453         tra->uncertainty= tra->maxinto= 0;
454         tra->backwards= 0;
455         tra->autopoint= 1;
456         for (step=0; step<=SPEEDSTEPS; step++)
457           tra->speedcurve[step]= -1;
458       } 
459     }
460
461     ALLOC(segments, info_nsegments);
462     if (phase)
463       FOR_SEG {
464         seg->owner= 0;
465         seg->seg_inverted= 0;
466         seg->res_movposset= 0;
467         seg->movposcomb= -1;
468         seg->moving= 0;
469         seg->motion= seg->motion_newplan= 0;
470         seg->i= segi;
471         seg->autopoint= 1;
472       }
473
474     if (phase==0)
475       datalen= offset;
476   }
477
478   rangebuf= mmalloc(sizeof(*rangebuf) * rangebufsz);
479 }
480   
481 /*---------- entrypoint from main, and its subroutines ----------*/
482
483 static void parse_file(const char *why) {
484   FILE *f;
485   int r;
486
487   f= fopen(filename,"r");
488   if (!f) diee("config: cannot open %s: %s", why, filename);
489   record_yyrestart(f);
490   record_yylineno= 1;
491   r= record_yyparse();
492   assert(!r); /* we're supposed to call yyerror which dies */
493   fclose(f);
494 }
495
496 static void parse_pass(const char **argv) {
497   while ((filename= *argv++))
498     parse_file("commandline-specified record file");
499
500   filename= persist_record_converted;
501   if (filename)
502     parse_file("converted persistent data file");
503 }
504
505 void records_parse(const char **argv) {
506   parse_pass(argv); /* trains==0: counts trains and speed ranges. */
507   alloc();
508   parse_pass(argv); /* trains!=0: populates data area */
509   record_tempzone_clear();
510   speeds_postprocess();
511   adjuncts_postprocess();
512   record_tempzone_freeall();
513   record_yylex_destroy();
514 }