chiark / gitweb /
realtime: movpos: debug output: exclude some more stuff from the default movpos output
[trains.git] / hostside / commands.c
1 /**/
2
3 #include <setjmp.h>
4 #include <stdlib.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include <string.h>
8
9 #include "realtime.h"
10 #include "auproto-pic.h"
11
12 #define NMRA_MAX_NARGS 10
13
14 typedef struct ManualRetransmitNode ManualRetransmitNode;
15 struct ManualRetransmitNode {
16   struct { ManualRetransmitNode *back, *next; } others;
17   char *name;
18   int lname;
19   RetransmitUrgentNode rn;
20 };
21
22 #define bogus_volatile /*empty*/
23 #ifdef __GNUC__
24 #if __GNUC__ == 2
25 #undef bogus_volatile
26 #define bogus_volatile volatile
27 #endif
28 #endif
29
30
31 static void cmd_ppc(Train *tra, Segment *seg, void *pu, const char *message) {
32   const CmdInfo *ci= pu;
33   ouprintf("ack %s SignallingPredictedProblem %s %s : %s\n",
34            ci->name, tra->pname, seg ? seg->i->pname : "-", message);
35 }
36
37 #define MUSTECRPREDICT(requester) do{                                   \
38     int mustecr__ec= (requester);                                       \
39     if (mustecr__ec==EC_SignallingPredictedProblem) return EC_BadCmd;   \
40     if (mustecr__ec) return mustecr__ec;                                \
41   }while(0)
42 #define CMDPPC cmd_ppc,(void*)ci
43
44 struct NmraParseEncodeCaller {
45   ParseState *ps;
46   unsigned long arg[NMRA_MAX_NARGS];
47   jmp_buf jb;
48 };
49
50 typedef struct { const char *word; int backwards; int change; } DirectionInfo;
51 static const DirectionInfo directioninfos[]= {
52   { "forwards",   0, 0 }, /* first two must be `forwards' and `backwards' */
53   { "backwards",  1, 0 }, /*  for the benefit of cmd_speed                */
54   { "change",    -1, 1 },
55   { 0 }
56 };  
57
58 unsigned long nmra_argnumber(NmraParseEncodeCaller *pec, int argi) {
59   return pec->arg[argi];
60 }
61
62 void nmra_problem(NmraParseEncodeCaller *pec, const char *problem) {
63   badcmd(pec->ps,problem);
64   longjmp(pec->jb, 1);
65 }
66
67 static ErrorCode cmd_nmra_command(ParseState *ps, PicInsn *pi) {
68   int hex;
69   const char *cmdarg;
70   int lcmdarg;
71   bogus_volatile int argc, checksum;
72   NmraParseEncodeCaller pec;
73   Nmra nmra;
74   char *ep;
75
76   assert(ps->remain);
77   switch (ps->remain[0]) {
78   case '_':  ps->remain++; return ps_needhextoend(ps, pi->d, &pi->l);
79   case '=':  hex=1;  checksum=1;  break;
80   case ':':  hex=1;  checksum=0;  break;
81   default:   hex=0;  checksum=1;  break;
82   }
83
84   if (hex) {
85     ps->remain++;
86     nmra.l= NMRA_PACKET_MAX - checksum;
87     MUSTECR( ps_needhextoend(ps, nmra.d, &nmra.l) );
88   } else {
89     MUSTECR( ps_needword(ps) );
90     cmdarg= ps->thisword;
91     lcmdarg= ps->lthisword;
92     pec.ps= ps;
93     argc= 0;
94
95     while (ps_word(ps)>=0) {
96       if (argc >= NMRA_MAX_NARGS)
97         return badcmd(ps,"far too many nmra args");
98
99       errno=0; pec.arg[argc++]= strtoul(ps->thisword, &ep, 0);
100       if (errno || ep != ps->thisword + ps->lthisword)
101         return badcmd(ps,"bad numeric argument for nmra");
102     }
103     if (setjmp(pec.jb))
104       return EC_BadCmd;
105
106     nmra_parse_encode(&nmra, cmdarg,lcmdarg, argc, &pec);
107   }
108   if (checksum)
109     nmra_addchecksum(&nmra);
110
111   nmra_encodeforpic(&nmra, pi);
112   return 0;
113 }
114   
115 static int cmd_nmra(ParseState *ps, const CmdInfo *ci) {
116   static struct { ManualRetransmitNode *head, *tail; } mrns;
117   
118   PicInsn *pi, pi_buf;
119   ManualRetransmitNode *mrn=0;
120   void (*retrans)(RetransmitUrgentNode *urg, Nmra *n)= 0;
121   ErrorCode ec;
122   
123   if (ps->remain) {
124     if (ps->remain[0]=='*') retrans= retransmit_urgent_queue;
125     else if (ps->remain[0]=='%') retrans= retransmit_urgent_queue_relaxed;
126   }
127   if (retrans) {
128     const char *mrname;
129     int lmrname;
130
131     ps_word(ps);
132     mrname= ps->thisword+1;
133     lmrname= ps->lthisword-1;
134
135     for (mrn= mrns.head;
136          mrn &&
137          !(mrn->lname == lmrname &&
138            !memcmp(mrn->name, mrname, lmrname));
139          mrn= mrn->others.next);
140     if (mrn) {
141       retransmit_urgent_cancel(&mrn->rn);
142     } else {
143       mrn= mmalloc(sizeof(*mrn));
144       mrn->name= mmalloc(lmrname);
145       memcpy(mrn->name, mrname, lmrname);
146       mrn->lname= lmrname;
147       DLIST2_APPEND(mrns,mrn,others);
148     }
149   }
150
151   if (!ps->remain) {
152     if (!retrans) {
153       return badcmd(ps,"nmra must have slot to cancel or data to send");
154     }
155     DLIST2_REMOVE(mrns,mrn,others);
156     free(mrn->name);
157     free(mrn);
158     return 0;
159   }
160
161   pi= retrans ? &mrn->rn.pi : &pi_buf;
162
163   ec= cmd_nmra_command(ps, pi);
164   if (ec) {
165     if (retrans) { free(mrn->name); free(mrn); }
166     return ec;
167   }
168
169   if (retrans)
170     retrans(&mrn->rn, 0);
171   else
172     serial_transmit(pi);
173
174   return 0;
175 }
176
177 static int cmd_noop(ParseState *ps, const CmdInfo *ci) {
178   return 0;
179 }
180
181 static int cmd_pic(ParseState *ps, const CmdInfo *ci) {
182   const PicInsnInfo *pii;
183   PicInsn pi;
184   long obj, v;
185
186   if (ps->remain && ps->remain[0]=='=') {
187     ps->remain++;
188     pi.l= sizeof(pi.d);
189     MUSTECR( ps_needhextoend(ps, pi.d, &pi.l) );
190   } else {
191     pii= some_needword_lookup(ps,pic_command_infos,"pic command");
192     if (!pii) return EC_BadCmd;
193   
194     if (pii->argsbits) {
195       MUSTECR( ps_neednumber(ps, &obj, 0,
196                              (1L << (pii->argsbits - pii->vbits)) - 1,
197                              "pic object number") );
198     } else {
199       obj= 0;
200     }
201     if (pii->vbits) {
202       MUSTECR( ps_neednumber(ps, &v, 0, (1L << pii->vbits) - 1,
203                              "pic command value") );
204     } else {
205       v= 0;
206     }
207     MUSTECR( ps_neednoargs(ps) );
208     enco_pic_pii(&pi, pii, obj, v);
209   }
210   serial_transmit(&pi);
211   return 0;
212 }
213
214 static int ps_needsegment(ParseState *ps, Segment **seg_r,
215                           const SegmentInfo **segi_r) {
216   const SegmentInfo *segi;
217   segi= some_needword_lookup_counted(ps,info_segments,info_nsegments,
218                                      "segment");
219   if (!segi) return EC_BadCmd;
220   if (segi_r) *segi_r= segi;
221   if (seg_r) *seg_r= segments + (segi - info_segments);
222   return 0;
223 }
224
225 static int ps_needsegmentperhaps(ParseState *ps, Segment **seg_r,
226                                  const SegmentInfo **segi_r) {
227   MUSTECR( ps_needword(ps) );
228   if (!thiswordstrcmp(ps,"-")) { *seg_r=0; *segi_r=0; return 0; }
229   ps_pushbackword(ps);
230   return ps_needsegment(ps,seg_r,segi_r);
231 }
232
233 static int ps_needtrain(ParseState *ps, Train **tra_r) {
234   const Train *tra= some_needword_lookup_counted(ps,trains,n_trains,"train");
235   if (!tra) return EC_BadCmd;
236   *tra_r= (Train*)tra;
237   return 0;
238 }
239
240 static int cmd_route_movfeat(ParseState *ps, const CmdInfo *ci,
241                              Segment *move, const SegmentInfo *movei,
242                              int poscomb) {
243   long ms;
244   
245   ms= -1;
246   if (ps->remain && CIXF_FORCE)
247     MUSTECR( ps_neednumber(ps,&ms,0,100000,"milliseconds") );
248
249   MUSTECR( ps_neednoargs(ps) );
250
251   if (ci->xarg & CIXF_FORCE) {
252     if (!(sta_state < Sta_Resolving || sta_state == Sta_Manual))
253       return badcmd(ps,"movpos queueing arrangements not ready");
254     
255     MovPosChange *reservation= 0;
256     if (!move->moving && move->motion) {
257       reservation= move->motion;
258       move->motion= 0;
259     }
260     MUSTECR( movpos_change(move,poscomb,ms,reservation) );
261   } else {
262     MUSTECRPREDICT( safety_movposchange(move, poscomb, ci->xarg & CIXF_U,
263                                         CMDPPC) );
264   }
265
266   return 0;
267 }
268
269 static int cmd_movfeat(ParseState *ps, const CmdInfo *ci) {
270   Segment *move;
271   const SegmentInfo *movei;
272   const MovFeatInfo *mfi;
273   MovPosComb target;
274   long pos_l;
275
276   MUSTECR( ps_needsegment(ps,&move,&movei) );
277   mfi= some_needword_lookup_counted(ps, movei->movfeats, movei->n_movfeats,
278                                     "moveable feature name");
279   if (!mfi) return EC_BadCmd;
280
281   MUSTECR( ps_neednumber(ps,&pos_l,0,mfi->posns-1,"position number") );
282
283   if (move->motion)
284     target= movpos_change_intent(move->motion);
285   else
286     target= movpos_poscomb_actual(move);
287
288   if (target<0) {
289     if (movei->n_movfeats > 1)
290       ouprintf("info movfeat-collapsing-unknown %s :"
291                " will set previously-unknown, but not set,"
292                " feature(s) to position 0\n",
293                movei->pname);
294     target= 0;
295   }
296   target= movposcomb_update_feature(target,mfi,pos_l);
297
298   return cmd_route_movfeat(ps,ci, move,movei,target);
299 }
300
301 static int cmd_route(ParseState *ps, const CmdInfo *ci) {
302   Segment *move;
303   const SegmentInfo *movei;
304   MovPosComb poscomb;
305   long poscomb_l;
306   
307   MUSTECR( ps_needsegmentperhaps(ps,&move,&movei) );
308   MUSTECR( ps_needword(ps) );
309   if (CTYPE(isdigit,*ps->thisword)) {
310     if (!move) return badcmd(ps,"invalid movement specification");
311     ps_pushbackword(ps);
312     MUSTECR( ps_neednumber(ps,&poscomb_l,0,movei->n_poscombs,
313                            "position number") );
314     poscomb= poscomb_l;
315   } else {
316     Segment *back, *fwd;
317     back= move;
318     MUSTECR( ps_needsegment(ps,&move,&movei) );
319     MUSTECR( ps_needsegmentperhaps(ps,&fwd,0) );
320     MUSTECR( movpos_findcomb_bysegs(back,move,fwd,move->movposcomb,&poscomb) );
321   }
322
323   return cmd_route_movfeat(ps,ci, move,movei,poscomb);
324 }
325
326 static int cmd_speed(ParseState *ps, const CmdInfo *ci) {
327   long speed;
328   Train *tra;
329   const DirectionInfo *di= 0;
330   
331   MUSTECR( ps_needtrain(ps,&tra) );
332   MUSTECR( ps_neednumber(ps,&speed,0,126,"speed step") );
333   if (ps->remain) {
334     di= some_needword_lookup_counted(ps,directioninfos,2,"direction");
335     if (!di) return EC_BadCmd;
336   }
337   MUSTECR( ps_neednoargs(ps) );
338
339   if (di && di->backwards != tra->backwards) {
340     ouprintf("ack %s CommandPreconditionsViolated direction\n", ci->name);
341     return EC_BadCmd;
342   }
343
344   if (ci->xarg & CIXF_FORCE) {
345     actual_speed(tra,speed);
346   } else {
347     MUSTECRPREDICT( speedmanager_speedchange_request(tra,speed,CMDPPC) );
348   }
349
350   return 0;
351 }
352
353 static int cmd_invert(ParseState *ps, const CmdInfo *ci) {
354   Segment *seg;
355
356   actual_inversions_start();
357   while (ps->remain) {
358     MUSTECR( ps_needsegment(ps,&seg,0) );
359     seg->seg_inverted ^= 1;
360     actual_inversions_segment(seg);
361   }
362   actual_inversions_done();
363
364   return 0;
365 }
366
367 static int cmd_direction(ParseState *ps, const CmdInfo *ci) {
368   Train *tra;
369   int backwards;
370   const DirectionInfo *di;
371
372   MUSTECR( ps_needtrain(ps,&tra) );
373
374   di= some_needword_lookup(ps,directioninfos,"direction");
375   if (!di) return EC_BadCmd;
376
377   backwards= di->change ? !tra->backwards : di->backwards;
378     
379   MUSTECR( ps_neednoargs(ps) );
380   
381   MUSTECRPREDICT( safety_setdirection(tra,backwards,CMDPPC) );
382   return 0;
383 }
384
385 /*---------- general machinery and the command table ----------*/ 
386  
387 void command_doline(ParseState *ps, CommandInput *cmdi_arg) {
388   int r;
389   const char *cmdline;
390
391   simlog("command-in %s\n",ps->remain);
392   simlog_flush();
393   current_cmd= 0;
394
395   debug_count_event("command");
396
397   cmdline= ps->remain;
398   if (!cmdline[0]) return;
399   r= ps_word(ps);  assert(!r);
400   current_cmd= some_lookup(ps,toplevel_cmds);
401   if (!current_cmd) {
402     ouprintf("nak UnknownCommand\n");
403     return;
404   }
405   ouprintf("executing %s\n",cmdline);
406   if (sta_state < Sta_Run && !(current_cmd->xarg & CIXF_ANYSTA)) {
407     ouprintf("ack %s InvalidState : layout not ready\n",current_cmd->name);
408     return;
409   }
410   r= current_cmd->fn(ps,current_cmd);
411   switch (r) {
412   case 0:  ouprintf("ack %s ok\n",current_cmd->name);                  break;
413   case EC_BadCmd:                                                      break;
414   default: ouprintf("ack %s %s\n",current_cmd->name,errorcodelist[r]); break;
415   }
416 }
417
418 const CmdInfo toplevel_cmds[]= {
419   { "!pic",       cmd_pic,        CIXF_ANYSTA|CIXF_FORCE   },
420   { "!nmra",      cmd_nmra,       CIXF_ANYSTA              },
421   { "noop",       cmd_noop,       CIXF_ANYSTA              },
422   { "route",      cmd_route                                },
423   { "route+",     cmd_route,      1                        },
424   { "route++",    cmd_route,      2                        },
425   { "movfeat",    cmd_movfeat                              },
426   { "movfeat+",   cmd_movfeat,    1                        },
427   { "movfeat++",  cmd_movfeat,    2                        },
428   { "!route",     cmd_route,      CIXF_ANYSTA|CIXF_FORCE   },
429   { "!movfeat",   cmd_movfeat,    CIXF_ANYSTA|CIXF_FORCE   },
430 //{ "autopoint",  cmd_autopoint                            },
431   { "!invert",    cmd_invert,     CIXF_ANYSTA|CIXF_FORCE   },
432   { "speed",      cmd_speed                                },
433   { "!speed",     cmd_speed,      CIXF_ANYSTA|CIXF_FORCE   },
434   { "direction",  cmd_direction                            },
435   { 0 }
436 };