chiark / gitweb /
6388d5316e9a16c44d1c7e598e6e82f0e073d272
[trains.git] / hostside / movpos.c
1 /*
2  * Handling of points and other moveable features.
3  */
4
5 #include "realtime.h"
6
7 /*========== declarations ==========*/
8
9 typedef struct {
10   const MovFeatInfo *i;
11   Small posn;
12 } Motion;
13
14 typedef struct Method Method;
15 typedef struct Change Change;
16
17 #define PRunkx PRIx32
18 typedef uint32_t UnkMap;
19
20 typedef struct MovPosChange {
21   Segment *move;
22   /* everything beyond here is private for indep */
23   MovPosComb actualpos, target;
24   UnkMap actualunk; /* bit per movfeat, set iff actualpos contains dummy 0 */
25   int refcount;
26   int n_changes;
27   Change *changes[];
28 } Indep;
29
30 static void method_update_feature(Method*, MovPosChange*, const Motion *mo);
31   /* Called from methods' execution logic when an individual feature
32    * has been moved.  This is used by the method-independent code to
33    * compute the correct delta set of movements from the current
34    * actual position, when thinking about new plans.  It is also sent
35    * to clients and ultimately used by the UI.
36    */
37
38 static void method_change_done(Method *m, Change *chg);
39   /* Called from methods' execution logic when a whole change
40    * has been done.  The method-independent code will take care of
41    * updating move->movposcomb. etc.
42    *
43    * REENTRANCY: May be called from within a call to the method's
44    * execute().  Of course cannot legally be called from within
45    * prepare, consider, check or dispose.
46    */
47
48
49 /* Kind-independent code is responsible for determining
50  * the method, doing a bit of cleanup, and adjusting the flow
51  * slightly.  Per-kind code does the actual work and is mostly in
52  * charge - it is also responsible for updating seg->moving and ->motion.
53  */
54 /*
55  * The following states exist for each Method
56  *   S  Starting     corresponds to global states other than Sta_Run
57  *   T  Tentative    changes have been made but may yet be undone
58  *   Y  Yes          proposed changes have been checked and are OK
59  *   E  Executing    the method is executing
60  *
61  * The following states exist for each Change
62  * at points when control flow  passes between kind and indep:
63  *   U  Unallocated  no memory allocated (Change does not exist)
64  *   P  Prepared     memory allocation done, basics filled in
65  *   I  Installed    reservation or confirmation successful
66  *   D  Done         motion is complete and callback is being entered
67  *   G  Garbage      motion is complete and callback is being exited
68  *
69  * Changes may be for:
70  *   R  Reservation
71  *   C  Confirmation
72  *
73  * No Changes remain Prepared while the Method is Executing.
74  */
75
76 struct Change {                    /* valid in:  filled in by and when:      */
77   Method *meth;                    /*  PIDG       indep after prepare()      */
78   MovPosChange *indep;             /*  PID        indep after prepare()      */
79   unsigned installed:1; /* private for indep */
80   /* kind-specific data follows */ /*  varies     kind-specific code, varies */
81 };
82
83 struct Method {
84   const char *pname;
85
86   ErrorCode (*prepare)(Method *m,                                 /* TYE->T */
87                        const Segment *move,
88                        int n_motions, const Motion *motions,
89                        int ms, int confirmation,
90                        Change **chg_r,          /* 0->P; err: 0->0; may be 0 */
91                        int *cost_r /* may be 0 */);
92   void (*dispose)(Method *m,                                       /* TY->TY */
93                   Change*);                                          /* P->U */
94
95   ErrorCode (*install)(Method *m,                   /* TYE->T; err: TYE->TYY */
96                        Change *inst);                     /* P->I; err: P->P */
97       /* error: ET->T, no change to Changes */
98   void (*remove)(Method *m,                                      /* TYE->TYY */
99                  Change *remove);                                    /* I->P */
100       /* error: ET->T, no change to Changes */
101
102   ErrorCode (*check)(Method *m);                    /* TYE->Y; err: TYE->TYE */
103   void (*execute)(Method *m);                                       /* EY->E */
104   void (*all_abandon)(Method *m);                                  /* TYE->S */
105
106   unsigned needcheck, needexec; /* used by indep to track T/Y/E */
107 };
108
109 /*========== general utility functions ==========*/
110
111 const char *movpos_pname(const Segment *move, MovPosComb poscomb) {
112   return !SOMEP(poscomb) ? "?" : move->i->poscombs[poscomb].pname;
113 }
114
115 static void ouposn_moving(const MovPosChange *indep) {
116   Segment *move= indep->move;
117   ouprintf("movpos %s position %s moving\n",
118            move->i->pname,
119            indep->actualunk ? "?" : movpos_pname(move, indep->actualpos));
120 }
121 static void ouposn_stable(const Segment *move) {
122   ouprintf("movpos %s position %s stable\n",
123            move->i->pname, movpos_pname(move, move->movposcomb));
124 }
125 static void ouposn_feat(const Segment *move, const MovFeatInfo *feati,
126                         MovPosComb posn, const Method *m) {
127   ouprintf("movpos %s feat %s %d %s\n", move->i->pname,
128            feati->pname, posn, m->pname);
129 }
130
131 MovPosComb movposcomb_update_feature(MovPosComb startpoint,
132                                      const MovFeatInfo *mfi,
133                                      int featpos) {
134   MovPosComb above_weight= mfi->weight * mfi->posns;
135   MovPosComb above= startpoint / above_weight;
136   MovPosComb below= startpoint % mfi->weight;
137   return above*above_weight + featpos*mfi->weight + below;
138 }
139
140 static void ignore_all_abandon(Method *m) { }
141
142 /*========== points and other fixed timeslot movfeats ==========*/
143
144 /*
145  * We maintain two queues, one for reserved one for actually confirmed
146  *  requests where we know what we're doing.
147  *
148  * We divide time into discrete slots, numbered with clock arithmetic.
149  *
150  *     cslot         cslot+1      cslot+2
151  *
152  *     currently     next in      after
153  *     changing      line         that
154  *
155  * We increment cslot when we consider the movfeat to move;
156  * for points and relays this is when we issue the command to the PIC.
157  * In a request, the deadline represents the latest allowable value
158  * of cslot just before that increment.
159  */
160
161 typedef unsigned FsqSlot;
162 typedef int FsqSlotSigned;
163
164 #define FSQDN (~(FsqSlot)0)
165
166 typedef struct {   /* state      Prep Resv  Inst Resv  Prep Conf  Inst Conf   */
167   Change h;        /* in queue?  absent     reserved   absent     confirmed   */
168   FsqSlot deadline; /*           relative   relative   absolute   absolute    */
169   int n_motions;    /*            1          1         num undone num undone  */
170   Motion motions[]; /*  [0].i:    0          0         non-0      non-0       */
171                     /*  .posn:    undef      undef     defined    defined     */
172 } FsqReq;
173
174 #define FSQ_MAX_QUEUE  15
175
176 typedef struct {
177   int n;
178   FsqReq *l[FSQ_MAX_QUEUE];
179 } FsqQueue;
180
181 typedef struct FsqMethod FsqMethod;
182
183 typedef struct {
184   /* set by fsq's client before fsq_init */
185   int slot_ms, lag_ms;
186   void (*move)(FsqMethod *m, const MovFeatInfo *mfi, int movfeatposn);
187   /* shared */
188   int ready; /* >0 means ready; set to 0 by fsq just before move */
189   /* fsq's client must arrange that these start out at all-bits-zero */
190   FsqSlot cslot;
191   FsqQueue confirmed, reserved;
192 } FsqState;
193
194 struct FsqMethod {
195   Method m;
196   FsqState f;
197   /* client may put things here */
198 };
199
200 static void fsq_check_action(FsqMethod *m);
201 static ErrorCode fsq_check_plan(FsqMethod *m);
202
203 static FsqSlotSigned fsq_maxdelay_reldeadline(FsqMethod *m,
204                                               int maxdelay_ms, int n_motions) {
205   if (maxdelay_ms==-1) return FSQ_MAX_QUEUE*16;
206   return (maxdelay_ms - m->f.lag_ms) / m->f.slot_ms - n_motions;
207 }
208
209 static void fsq_queue_remove_index(FsqQueue *q, int index) {
210   q->n--;
211   memmove(&q->l[index], &q->l[index+1], sizeof(q->l[0]) * (q->n - index));
212 }
213
214 static int fsq_req_compar_approx(const void *av, const void *bv) {
215   FsqReq *const *a= av;
216   FsqReq *const *b= av;
217   FsqSlotSigned dldiff= (*b)->deadline - (*a)->deadline;
218   return dldiff;
219 }
220
221 static void fsq_queue_remove_item(FsqQueue *q, FsqReq *r) {
222   FsqReq **entry;
223   int i;
224   entry= bsearch(r, q->l, q->n, sizeof(q->l[0]), fsq_req_compar_approx);
225   assert(entry);
226
227 #define SEARCH_CHECK do{                                \
228     if (q->l[i] == r) goto found;                       \
229     if (q->l[i]->deadline != r->deadline) break;        \
230 }while(0)
231   for(i= entry - q->l;     i >= 0;   i--)  SEARCH_CHECK;
232   for(i= entry - q->l + 1; i < q->n; i++)  SEARCH_CHECK;
233   abort();
234
235  found:
236   fsq_queue_remove_index(q, i);
237 }
238
239 static FsqQueue *fsq_item_queue(FsqMethod *m, FsqReq *r)
240   { return r->motions[0].i ? &m->f.confirmed : &m->f.reserved; }
241
242 #define WHICH(wh)                               \
243   (whichr= wh##r,                               \
244    whichwhen= wh##when,                         \
245    wh++)
246
247 static ErrorCode fsq_check_plan(FsqMethod *m) {
248   /* Checks whether we can meet the currently queued commitments */
249   /* if this fails, indep machinery calls fsq_prepare to dequeue */
250   int future, conf, resv, whichwhen, DP;
251   FsqReq *whichr;
252
253   conf=resv=0;
254   future=0;
255
256   DPRINTF1(movpos,fsq, "%s   plan", m->m.pname);
257
258   /* If CDU isn't charged we can't do one right away */
259   if (m->f.ready<0) {
260     DPRINTF2(" +");
261     future++;
262   }
263
264   for (;;) {
265     FsqReq *confr= conf < m->f.confirmed.n ? m->f.confirmed.l[conf] : 0;
266     FsqReq *resvr= resv < m->f.reserved .n ? m->f.reserved .l[resv] : 0;
267     if (!confr && !resvr) break;
268     DPRINTF2(" %d:",future);
269     int confwhen= confr ? confr->deadline - m->f.cslot : INT_MAX;
270     int resvwhen= resvr ? resvr->deadline              : INT_MAX;
271     if (future && resvwhen < confwhen) {
272       WHICH(resv);  DPRINTF2("~");
273     } else if (confr) {
274       WHICH(conf);
275     } else {
276       future++;     DPRINTF2("-");
277       continue;
278     }
279     DPRINTF2("%s/%s[%d@t+%d]", whichr->h.indep->move->i->pname,
280             movpos_pname(whichr->h.indep->move, whichr->h.indep->target),
281             whichr->n_motions, whichwhen);
282     if (future > whichwhen) {
283       DPRINTF2("!...bad\n");
284       return EC_MovFeatTooLate;
285     }
286     future += whichr->n_motions;
287   }
288   DPRINTF2(" ok\n");
289   return 0;
290 }
291
292 #undef WHICH
293
294 static ErrorCode fsq_queue_insert_item(FsqMethod *m, FsqQueue *q, FsqReq *r) {
295   int insat;
296
297   if (q->n == FSQ_MAX_QUEUE)
298     return EC_BufferFull;
299
300   for (insat= q->n;
301        insat>0 && (FsqSlotSigned)(r->deadline - q->l[insat-1]->deadline) < 0;
302        insat--)
303     q->l[insat]= q->l[insat-1];
304   q->l[insat]= r;
305   q->n++;
306
307   return 0;
308 }
309
310 static void fsq_item_debug(FsqMethod *m, FsqReq *r, const char *opwhat) {
311   int DP;
312   Segment *move= r->h.indep->move;
313   DPRINTF1(movpos,fsq, "%s %s %s", m->m.pname, opwhat, move->i->pname);
314   if (r->motions[0].i) {
315     int i;
316     Motion *mo;
317     for (i=0, mo=r->motions; i<r->n_motions; i++, mo++)
318       DPRINTF2("/%s%d", mo->i->pname, (int)mo->posn);
319     DPRINTF2("\n");
320   } else {
321     DPRINTF2("(%d)\n", r->n_motions);
322   }
323 }
324  
325 static ErrorCode fsq_enqueue(FsqMethod *m, FsqReq *r) { /* P->I; err: P->P */
326   if (!r) return 0;
327   fsq_item_debug(m,r,"enqueue");
328   return fsq_queue_insert_item(m, fsq_item_queue(m,r), r);
329 }
330
331 static void fsq_dequeue(FsqMethod *m, FsqReq *r) { /* I->P */
332   if (!r) return;
333   fsq_item_debug(m,r,"dequeue");
334   fsq_queue_remove_item(fsq_item_queue(m,r), r);
335 }
336
337 /*---------- method entrypoints ----------*/
338
339 static ErrorCode fsq_prepare(Method *mm, const Segment *move,
340                              int n_motions, const Motion *motions,
341                              int ms, int confirmation,
342                              Change **chg_r, int *cost_r) {
343   FsqMethod *m= (void*)mm;
344
345   assert(n_motions > 0);
346
347   FsqSlotSigned reldeadline= fsq_maxdelay_reldeadline(m, ms, n_motions);
348   if (reldeadline <= 0) return EC_MovFeatTooLate;
349
350   if (chg_r) {
351     int alloc_motions= confirmation ? n_motions : 1;
352       /* we need at least one motion in the table so we can tell
353        *  the difference between the states by looking at motions[0].i */
354     FsqReq *r= mmalloc(sizeof(*r) + alloc_motions * sizeof(r->motions[0]));
355     r->n_motions= n_motions;
356     if (confirmation) {
357       r->deadline= reldeadline + m->f.cslot;
358       memcpy(r->motions, motions, sizeof(*r->motions)*n_motions);
359     } else {
360       r->deadline= reldeadline;
361       r->motions[0].i= 0;
362     }
363     *chg_r= &r->h;
364   }
365   if (cost_r) {
366     *cost_r= n_motions * m->f.slot_ms;
367   }
368
369   return 0;
370 }
371
372 static void fsq_dispose(Method *mm, Change *chg) {
373   FsqReq *r= (FsqReq*)chg;
374   free(r);
375 }
376
377 static void fsq_remove(Method *mm, Change *remvchg) {
378   FsqMethod *m= (void*)mm;
379   FsqReq *remv= (FsqReq*)remvchg;
380
381   fsq_dequeue(m, remv);
382 }
383
384 static ErrorCode fsq_install(Method *mm, Change *instchg) {
385   FsqMethod *m= (void*)mm;
386   FsqReq *inst= (FsqReq*)instchg;
387
388   return fsq_enqueue(m, inst);
389 }
390
391 static ErrorCode fsq_check(Method *mm) { 
392   FsqMethod *m= (void*)mm;
393   ErrorCode ec= fsq_check_plan(m);
394   return ec;
395 }
396
397 static void fsq_execute(Method *mm) {
398   FsqMethod *m= (void*)mm;
399   fsq_check_action(m);
400 }
401
402 /*---------- something to do ? ----------*/
403
404 static void fsq_check_action(FsqMethod *m) {
405   /* client should call this after it sets ready */
406   ErrorCode ec;
407
408   if (!m->f.confirmed.n) {
409     if (sta_state == Sta_Finalising) resolve_motioncheck();
410     return;
411   }
412
413   FsqReq *r= m->f.confirmed.l[0];
414
415   if (r->n_motions && m->f.ready>0) {
416     /* look for something to move */
417     Motion *mo= &r->motions[--r->n_motions];
418     assert(mo->posn < mo->i->posns);
419     m->f.ready= 0;
420     m->f.move(m, mo->i, mo->posn);
421     m->f.cslot++;
422
423     method_update_feature(&m->m, r->h.indep, mo);
424   }
425
426   if (!r->n_motions) {
427     fsq_queue_remove_index(&m->f.confirmed, 0);
428     method_change_done(&m->m, &r->h);
429     m->m.dispose(&m->m, &r->h);
430
431     ec= fsq_check_plan(m);  assert(!ec);
432     fsq_check_action(m);
433   }
434 }
435
436 /*========== points ==========*/
437
438 /*
439  * CDU and point queue states:
440  *
441  *
442  *    ____________                              conf'd
443  *   /   points_  \                 ready         .n
444  *   |   all_      |
445  *   |   abandon   |
446  *   |             V
447  *   |from       INACTIVE               -1      0
448  *   |any       <=Sta_Settling
449  *  ^^^^^^^^     (start)
450  *                 |
451  *     ___________ |turning
452  *    /           \| _on
453  *   |             V
454  *   |           CHARGING               0       any
455  *   |          >=Sta_Resolving
456  *   |             |
457  *   |             |on_pic
458  *   |             |_charged
459  *   |             V
460  *   ^           READY                  1       any
461  *   |             |
462  *   |             |fsq_check_action
463  *   |             | calls point_move which fires a point
464  *    \___________/
465  *
466  */
467
468 #define CDU_RECHARGE   350 /*ms*/
469 #define POINT_MOVEMENT  50 /*ms*/
470
471 static ErrorCode point_prepare(Method *mm, const Segment *move,
472                                int n_motions, const Motion *motions,
473                                int ms, int confirmation,
474                                Change **chg_r, int *cost_r) {
475   FsqMethod *m= (void*)mm;
476   assert(m->f.ready>=0);
477   return fsq_prepare(mm,move, n_motions,motions,
478                      ms,confirmation, chg_r,cost_r);
479  }
480
481 static void point_move(FsqMethod *m, const MovFeatInfo *mfi, int posn) {
482   /* actually firing points, yay! */
483   PicInsn piob;
484   enco_pic_point(&piob, mfi->boob[posn]);
485   serial_transmit(&piob);
486 }
487
488 static void points_all_abandon(Method *mm) {
489   FsqMethod *m= (void*)mm;
490   m->f.ready= -1;
491 }
492
493 static FsqMethod points_method;
494
495 void points_turning_on(void) {
496   FsqMethod *m= &points_method;
497   m->f.ready= 0;
498 }
499 void on_pic_charged(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
500   FsqMethod *m= &points_method;
501   if (m->f.ready<0) return;
502   m->f.ready= 1;
503   fsq_check_action(m);
504 }
505
506 static FsqMethod points_method= {
507   { "point",
508     point_prepare, fsq_dispose,
509     fsq_install, fsq_remove,
510     fsq_check, fsq_execute, points_all_abandon },
511   { .lag_ms= POINT_MOVEMENT, .slot_ms= CDU_RECHARGE, .move= point_move }
512 };
513
514 /*========== relays ==========*/
515
516 /*
517  * Waggler states:
518  *
519  *    ____________                              conf'd
520  *   /  wagglers_ \                 ready         .n
521  *   |   all_      |
522  *   |   abandon   |
523  *   |             V
524  *   |from       UNKNOWN                -1      0
525  *   |any       <=Sta_Settling
526  *  ^^^^^^^^     (start)
527  *                 |
528  *     ___________ |turning
529  *    /           \| _on
530  *   |             V
531  *   |           CHARGING               0       any
532  *   |          >=Sta_Resolving
533  *   |             |
534  *   |             |on_pic
535  *   |             |_charged
536  *   |             V
537  *   ^           READY                  1       any
538  *   |             |
539  *   |             |fsq_check_action
540  *   |             | calls waggle_do which switches a relay
541  *    \___________/
542  *
543  */
544
545 static FsqMethod waggle;
546
547 static void waggle_do(FsqMethod *m, const MovFeatInfo *mfi, int posn) {
548   /* actually setting relays */
549   PicInsn piob;
550   enco_pic_waggle(&piob, mfi->boob[0], posn);
551   serial_transmit(&piob);
552 }
553
554 static SegmentNum waggle_settle_seg;
555 static int waggle_settle_feat;
556
557 static void waggle_settle_check(void) {
558   for (;;) {
559     if (waggle_settle_seg >= info_nsegments) return;
560
561     Segment *seg= &segments[waggle_settle_seg];
562     if (waggle_settle_feat >= seg->i->n_movfeats) {
563       waggle_settle_seg++; waggle_settle_feat=0; continue;
564     }
565
566     const MovFeatInfo *feati= &seg->i->movfeats[waggle_settle_feat];
567     if (feati->kind != mfk_relay) {
568       waggle_settle_feat++; continue;
569     }
570
571     waggle.f.ready= 0;
572     waggle_do(&waggle, feati, (seg->movposcomb / feati->weight) & 1);
573     waggle_settle_feat++;
574   }
575 }
576
577 void waggle_startup_manual(void) {
578   waggle.f.ready= 1;
579 }
580
581 void waggle_settle(void) {
582   waggle_settle_seg= 0;
583   waggle_settle_feat= 0;
584   waggle.f.ready= 1;
585   waggle_settle_check();
586 }
587
588 void on_pic_waggled(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
589   if (sta_state == Sta_Settling) {
590     waggle.f.ready= 1;
591     waggle_settle_check();
592   } else if (sta_state >= Sta_Resolving || sta_state == Sta_Manual) {
593     waggle.f.ready= 1;
594     fsq_check_action(&waggle);
595     return;
596   }
597 }
598
599 static FsqMethod waggle= {
600   { "relay",
601     fsq_prepare, fsq_dispose,
602     fsq_install, fsq_remove,
603     fsq_check, fsq_execute, ignore_all_abandon },
604   { .lag_ms= 5, .slot_ms= 50, .move= waggle_do }
605 };
606
607 /*========== dummy `nomove' kind ==========*/
608
609 typedef struct NomoveChange {
610   Change h;
611   DLIST_NODE(struct NomoveChange) inqueue;
612   int n_motions; /* 0 for reservations */
613   Motion motions[];
614 } NomoveChange;
615
616 typedef struct {
617   Method m;
618   NomoveChange *queuehead; /* contains confirmations only */
619 } NomoveMethod;
620
621 static ErrorCode nomove_prepare(Method *meth_in, const Segment *move,
622                                 int n_motions, const Motion *motions,
623                                 int ms, int confirming,
624                                 Change **chg_r, int *cost_r) {
625   if (chg_r) {
626     NomoveChange *chg;
627     assert(n_motions>0);
628     if (!confirming) n_motions= 0;
629     chg= mmalloc(sizeof(*chg) + sizeof(Motion)*n_motions);
630     chg->n_motions= n_motions;
631     memcpy(chg->motions, motions, sizeof(Motion)*n_motions);
632   }
633   if (cost_r) {
634     *cost_r= 0;
635   }
636   return 0;
637 }
638 static void nomove_dispose(Method *mm, Change *remvchg) {
639   NomoveChange *remv= (void*)remvchg;
640   free(remv);
641 }
642
643 static ErrorCode nomove_install(Method *mm, Change *instchg) {
644   NomoveMethod *meth= (void*)mm;
645   NomoveChange *inst= (void*)instchg;
646   if (inst->n_motions)
647     DLIST1_PREPEND(meth->queuehead, inst, inqueue);
648   return 0;
649 }
650 static void nomove_remove(Method *mm, Change *remvchg) {
651   NomoveMethod *meth= (void*)mm;
652   NomoveChange *remv= (void*)remvchg;
653   if (remv->n_motions)
654     DLIST1_REMOVE(meth->queuehead, remv, inqueue);
655 }
656
657 static ErrorCode nomove_check(Method *mm) { return 0; }
658
659 static void nomove_execute(Method *mm) {
660   NomoveMethod *meth= (void*)mm;
661   NomoveChange *done;
662
663   while ((done= meth->queuehead)) {
664     assert(done->n_motions);
665     int i;
666     for (i=0; i<done->n_motions; i++)
667       method_update_feature(&meth->m, done->h.indep, &done->motions[i]);
668     method_change_done(&meth->m, &done->h);
669     DLIST1_REMOVE(meth->queuehead, done, inqueue);
670     nomove_dispose(&meth->m, &done->h);
671   }
672 }
673
674 static Method nomove_method= {
675   "nomove",
676   nomove_prepare, nomove_dispose,
677   nomove_install, nomove_remove,
678   nomove_check, nomove_execute, ignore_all_abandon
679 };
680
681 /*========== method-independent machinery ==========*/
682
683 #define INDEP_DBG_FMT "<%p:%s/%s[%d]>"
684 #define INDEP_DBG_ARGS(in) (in),                                        \
685     ((in)->move->i->pname), (movpos_pname((in)->move, (in)->target)),   \
686     ((in)->n_changes)
687
688 #define INDEP_DPFX_FMT "movpos " INDEP_DBG_FMT " "
689 #define INDEP_DPFX_ARGS(in) INDEP_DBG_ARGS((in))
690
691 #define METH_DPFX_FMT "%s " INDEP_DBG_FMT " "
692 #define METH_DPFX_ARGS(indep, meth) ((meth).pname), INDEP_DBG_ARGS((indep))
693
694 static void indep_indep_done(Indep *indep);
695
696 static Method *methods[]= {
697   [mfk_none] = (Method*)&nomove_method,
698   [mfk_point] = (Method*)&points_method,
699   [mfk_relay] = (Method*)&waggle,
700   0
701 };
702
703 /*---------- entrypoints from methods ----------*/
704
705 static void method_update_feature(Method *m, MovPosChange *indep,
706                                   const Motion *mo) {
707   int featix= mo->i - indep->move->i->movfeats;
708   assert(featix >= 0 && featix < indep->move->i->n_movfeats);
709   ouposn_feat(indep->move, mo->i, mo->posn, m);
710   indep->actualpos=
711     movposcomb_update_feature(indep->actualpos, mo->i, mo->posn);
712   indep->actualunk &= (~(UnkMap)1 << featix);
713   ouposn_moving(indep);
714 }
715
716 static void method_change_done(Method *m, Change *chg) {
717   Indep *indep= chg->indep;
718   Change **search;
719
720   DPRINTF(movpos,meth, METH_DPFX_FMT "method_change_done...\n",
721           METH_DPFX_ARGS(indep,*m));
722
723   for (search=indep->changes; *search; search++)
724     if ((*search) == chg) goto found;
725   assert(!"change in indep");
726
727  found:
728   indep->n_changes--;
729   if (indep->n_changes) {
730     *search= indep->changes[indep->n_changes];
731     return;
732   }
733
734   if (!indep->refcount)
735     indep_indep_done(indep);
736 }
737
738 static void indep_indep_done(Indep *indep) {
739   /* all done */
740   Segment *move= indep->move;
741   move->moving= 0;
742   move->motion= 0;
743   move->movposcomb= indep->target;
744   ouposn_stable(move);
745   free(indep);
746 }
747
748 /*---------- internal core machinery ----------*/
749
750 static Method *feature_method(const MovFeatInfo *feati) {
751   assert(feati->kind >= 0);
752   assert(feati->kind < ARRAY_SIZE(methods));
753   Method *meth= methods[feati->kind];
754   assert(meth);
755   return meth;
756 }
757
758 static int change_needed(int featix, const MovFeatInfo *feati,
759                          MovPosComb startpointpos, UnkMap startpointunk,
760                          MovPosComb target) {
761   int r;
762
763   r= (startpointunk & ((UnkMap)1 << featix)) ||
764     (target / feati->weight) % feati->posns -
765     (startpointpos / feati->weight) % feati->posns;
766
767   if (DEBUGP(movpos,eval))
768     DPRINTFA(" { %s:%s(%d*%d) %d..%d => %d }",
769              feature_method(feati)->pname, feati->pname,
770              feati->posns, feati->weight,
771              startpointpos, target, r);
772   return r;
773 }
774
775 static void indep_dispose(MovPosChange *indep) {
776   if (!indep) return;
777
778   DPRINTF(movpos,intern, INDEP_DPFX_FMT "dispose...\n",
779           INDEP_DPFX_ARGS(indep));
780
781   int changei;
782   for (changei=0; changei<indep->n_changes; changei++) {
783       Change *chg= indep->changes[changei];
784       if (!chg) continue;
785
786       Method *meth= chg->meth;
787       DPRINTF(movpos,meth, METH_DPFX_FMT "dispose...\n",
788               METH_DPFX_ARGS(indep,*meth));
789       meth->dispose(meth, chg);
790   }
791   free(indep);
792 }
793
794 #define EVAL_MAX_METHODS 2
795 #define EVAL_MAX_MOTIONS 2
796
797 static ErrorCode indep_prepare(Segment *move, MovPosComb target,
798                                MovPosComb startpointpos,
799                                int ms, int confirming,
800                                MovPosChange **indep_r /* 0 ok */,
801                                int *cost_r /* 0 ok */) {
802   static int n_meths;
803   static Method *meths[EVAL_MAX_METHODS];
804   static int n_motions[EVAL_MAX_METHODS];
805   static Motion motions[EVAL_MAX_METHODS][EVAL_MAX_MOTIONS];
806
807   const SegmentInfo *movei= move->i;
808   int feat, DP;
809   UnkMap startpointunk;
810
811   MovPosChange *indep=0;
812
813   DPRINTF1(movpos,eval, "movpos prepare %s/%s <-%s", move->i->pname,
814            movpos_pname(move,target), movpos_pname(move,startpointpos));
815
816   if (SOMEP(startpointpos)) {
817     startpointunk= 0;
818   } else {
819     if (move->moving) {
820       startpointpos= move->motion->actualpos;
821       startpointunk= move->motion->actualunk;
822     } else if (SOMEP(move->movposcomb)) {
823       startpointpos= move->movposcomb;
824       startpointunk= 0;
825     } else {
826       startpointpos= 0;
827       startpointunk= ((UnkMap)1 << move->i->n_movfeats) - 1;
828     }
829     DPRINTF2(" actual <-%s&%"PRunkx, movpos_pname(move,startpointpos),
830              startpointunk);
831   }
832
833   n_meths= 0;
834
835   for (feat=0; feat<movei->n_movfeats; feat++) {
836     const MovFeatInfo *feati= &movei->movfeats[feat];
837     if (!change_needed(feat,feati,
838                        startpointpos,startpointunk,
839                        target))
840       continue;
841     MovPosComb posn= target / feati->weight % feati->posns;
842     Method *meth= feature_method(feati);
843
844     int methi;
845     for (methi=0; methi<n_meths; methi++)
846       if (meths[methi] == meth) goto found_method;
847     /* need a new method */
848     methi= n_meths++;
849     if (methi >= EVAL_MAX_METHODS) {
850       DPRINTF2(" MovFeatTooManyMethods methi=%d\n",methi);
851       return EC_MovFeatTooManyMethods;
852     }
853     meths[methi]= meth;
854     n_motions[methi]= 0;
855     DPRINTF2(" meths[%d]=%s", methi,meth->pname);
856
857   found_method:;
858     int motioni= n_motions[methi]++;
859     if (motioni >= EVAL_MAX_MOTIONS) {
860       DPRINTF2(" MovFeatTooManyMotions motioni=%d\n",motioni);
861       return EC_MovFeatTooManyMotions;
862     }
863     DPRINTF2(" motion[%d][%d]=%s%d", methi, motioni, feati->pname,posn);
864     motions[methi][motioni].i= feati;
865     motions[methi][motioni].posn= posn;
866   }
867
868   if (indep_r) {
869     DPRINTF2(" alloc");
870     indep= mmalloc(sizeof(*indep) + sizeof(Change*) * n_meths);
871     indep->move= move;
872     indep->actualpos= startpointpos;
873     indep->actualunk= startpointunk;
874     indep->target= target;
875     indep->n_changes= n_meths;
876     indep->refcount= 0;
877     memset(indep->changes, 0, sizeof(Change*) * n_meths);
878   }
879   DPRINTF2("\n");
880
881   int totalcost= 0;
882   int changei;
883   ErrorCode ec;
884
885   for (changei=0; changei<n_meths; changei++) {
886     Method *meth= meths[changei];
887     int thiscost= 0;
888     meth->needcheck= 1;
889     meth->needexec= 1;
890
891     if (indep_r)
892       DPRINTF(movpos,meth, METH_DPFX_FMT "prepare n_motions=%d...\n",
893               METH_DPFX_ARGS(indep,*meth), n_motions[changei]);
894     else
895       DPRINTF(movpos,meth, "%s prepare (costing) n_motions=%d...\n",
896               meth->pname, n_motions[changei]);
897
898     ec= meth->prepare(meth,move,
899                       n_motions[changei],motions[changei],
900                       ms, confirming,
901                       indep ? &indep->changes[changei] : 0,
902                       &thiscost);
903     if (ec) goto x;
904     if (indep) {
905       Change *chg= indep->changes[changei];
906       chg->meth= meth;
907       chg->indep= indep;
908       chg->installed= 0;
909     }
910     totalcost += thiscost;
911   }
912
913   if (indep_r) *indep_r= indep;
914   if (cost_r) *cost_r= totalcost;
915
916   if (indep_r)
917     DPRINTF(movpos,eval, INDEP_DPFX_FMT "prepare cost=%d ok\n",
918             INDEP_DPFX_ARGS(indep), totalcost);
919   else
920     DPRINTF(movpos,eval, "movpos prepare %s/%s cost=%d ok\n",
921             move->i->pname, movpos_pname(move,target), totalcost);
922   return 0;
923
924  x:
925   indep_dispose(indep);
926   DPRINTF(movpos,entry, "movpos prepare %s/%s err=%s\n", move->i->pname,
927           movpos_pname(move,target), ec2str(ec));
928   return ec;
929 }
930
931 static void indep_remove(MovPosChange *remv) {
932   if (!remv) return;
933   
934   DPRINTF(movpos,intern, INDEP_DPFX_FMT "remove...\n",
935           INDEP_DPFX_ARGS(remv));
936
937   int i;
938   for (i=0; i<remv->n_changes; i++) {
939     Change *chg= remv->changes[i];
940     if (!chg->installed) continue;
941     Method *meth= chg->meth;
942     meth->needexec= 1;
943
944     DPRINTF(movpos,meth, METH_DPFX_FMT "remove...\n",
945             METH_DPFX_ARGS(remv,*meth));
946     meth->remove(meth, chg);
947     chg->installed= 0;
948   }
949 }
950
951 static ErrorCode
952 indep_install(MovPosChange *inst, int checknow) {
953   /* if this fails, inst may be left partially installed */
954   if (!inst) return 0;
955   
956   DPRINTF(movpos,intern, INDEP_DPFX_FMT "install checknow=%d...\n",
957           INDEP_DPFX_ARGS(inst), checknow);
958
959   int i;
960   ErrorCode ec;
961   for (i=0; i<inst->n_changes; i++) {
962     Change *chg= inst->changes[i];
963     assert(!chg->installed);
964     Method *meth= chg->meth;
965
966     meth->needexec= 1;
967     ec= meth->install(meth, chg);
968     DPRINTF(movpos,meth, METH_DPFX_FMT "install=%s\n",
969             METH_DPFX_ARGS(inst,*meth), ec2str(ec));
970     if (ec) goto x;
971     chg->installed= 1;
972     meth->needcheck= 1;
973
974     if (checknow) {
975       ec= meth->check(meth);
976       DPRINTF(movpos,meth, METH_DPFX_FMT "check=%s\n",
977               METH_DPFX_ARGS(inst,*meth), ec2str(ec));
978       if (ec) goto x;
979       meth->needcheck= 0;
980     }
981   }
982   return 0;
983
984  x:
985   return ec;
986 }
987
988 static void indep_check_execute(void) {
989   DPRINTF(movpos,intern, "movpos indep_check_execute\n");
990
991   Method **methwalk, *meth;
992   for (methwalk= methods; (meth= *methwalk); methwalk++) {
993     if (meth->needcheck) {
994       ErrorCode ec= meth->check(meth);
995       DPRINTF(movpos,meth, "%s check=%s\n", meth->pname, ec2str(ec));
996       assert(!ec);
997       meth->needcheck= 0;
998     }
999     if (meth->needexec) {
1000       meth->needexec= 0;
1001       DPRINTF(movpos,meth, "%s execute...\n", meth->pname);
1002       meth->execute(meth);
1003     }
1004   }
1005 }
1006
1007 /*---------- entrypoints from rest of program ----------*/
1008
1009 ErrorCode
1010 movpos_reserve(Segment *move, int maxdelay_ms, MovPosChange **res_r,
1011                MovPosComb target, MovPosComb startpoint /*as for findcomb*/) {
1012   ErrorCode ec;
1013   MovPosChange *indep= 0;
1014
1015   DPRINTF(movpos,entry, "movpos reserve %s/%s maxdelay=%dms startpoint=%s\n",
1016           move->i->pname, movpos_pname(move,target),
1017           maxdelay_ms, movpos_pname(move,startpoint));
1018
1019   ec= indep_prepare(move,target, startpoint,
1020                     maxdelay_ms,0,
1021                     &indep, 0);
1022   if (ec) return ec;
1023
1024   ec= indep_install(indep, 1);
1025   if (ec) goto x;
1026
1027   indep_check_execute();
1028
1029   DPRINTF(movpos,entry, "movpos reserve %s/%s ok\n",
1030           move->i->pname, movpos_pname(move,target));
1031   *res_r= indep;
1032   return 0;
1033
1034  x:
1035   indep_remove(indep);
1036   indep_dispose(indep);
1037   indep_check_execute();
1038
1039   DPRINTF(movpos,entry, "movpos reserve %s/%s err=%s\n",
1040           move->i->pname, movpos_pname(move,target), ec2str(ec));
1041   return ec;
1042 }
1043
1044 ErrorCode movpos_findcomb_bysegs(Segment *back, Segment *move, Segment *fwd,
1045                                  MovPosComb startpoint, MovPosComb *chosen_r) {
1046   const SegmentInfo *movei= move->i;
1047   MovPosComb tcomb, bestcomb=-1;
1048   int tcost, bestcost=INT_MAX;
1049   const SegPosCombInfo *pci;
1050
1051   DPRINTF(movpos,eval, "movpos_findcomb_bysegs %s-%s-%s <-%s\n",
1052           back ? back->i->pname : "*", move->i->pname,
1053           fwd  ? fwd ->i->pname : "*", movpos_pname(move, startpoint));
1054
1055   for (tcomb=0, pci=movei->poscombs;
1056        tcomb<movei->n_poscombs;
1057        tcomb++, pci++) {
1058     /* these next assignments may generate segments[-1] but we don't
1059      * care because that won't compare equal to back or fwd */
1060     Segment *tback= &segments[pci->link[1].next];
1061     Segment *tfwd=  &segments[pci->link[0].next];
1062
1063     DPRINTF(movpos,intern, "movpos_findcomb_bysegs  ... %s : %s-%s-%s\n",
1064             movpos_pname(move, tcomb),
1065             SOMEP(pci->link[1].next) ? tback->i->pname : "#",
1066             move->i->pname,
1067             SOMEP(pci->link[0].next) ? tfwd->i->pname : "#");       
1068
1069     if (back && !(back==tback || back==tfwd)) continue;
1070     if (fwd  && !(fwd ==tback || fwd ==tfwd)) continue;
1071
1072     /* we have to search for the one which is least effort */
1073     ErrorCode ec= indep_prepare(move,tcomb,startpoint, -1,0, 0,&tcost);
1074     if (ec) return ec;
1075
1076     if (tcost >= bestcost) /* prefer low-numbered movposcombs */
1077       continue;
1078
1079     bestcomb= tcomb;
1080     bestcost= tcost;
1081   }
1082   DPRINTF(movpos,entry, "movpos_findcomb_bysegs %s..%s..%s <-%s => %s/%s\n",
1083           back ? back->i->pname : "-", move->i->pname,
1084           fwd  ? fwd ->i->pname : "-", movpos_pname(move, startpoint),
1085           move->i->pname, movpos_pname(move,bestcomb));
1086
1087   if (chosen_r) *chosen_r= bestcomb;
1088   return
1089     bestcost==INT_MAX ? EC_MovFeatRouteNotFound :
1090     0;
1091 }
1092
1093 ErrorCode movpos_change(Segment *move, MovPosComb target,
1094                         int maxdelay_ms, MovPosChange *resv) {
1095   int DP;
1096   ErrorCode ec;
1097
1098   DPRINTF1(movpos,entry, "movpos change %s/%s maxdelay=%dms",
1099            move->i->pname, movpos_pname(move, target), maxdelay_ms);
1100   if (resv) DPRINTF2(" resv=%s/%s",
1101                      resv->move->i->pname,
1102                      movpos_pname(resv->move, resv->target));
1103   if (move->motion) DPRINTF2(" oldmotion=/%s",
1104                              movpos_pname(move, move->motion->target));
1105   DPRINTF2("\n");
1106
1107   MovPosChange *inst= 0;
1108
1109   ec= indep_prepare(move,target, NOTA(MovPosComb),
1110                     maxdelay_ms,1,
1111                     &inst, 0);
1112   if (ec) goto x;
1113
1114   indep_remove(resv);
1115   indep_remove(move->motion);;
1116
1117   ec= indep_install(inst, 1);
1118   if (ec) goto x;
1119
1120   indep_dispose(resv);
1121   indep_dispose(move->motion);
1122
1123   move->motion= inst;
1124   move->moving= 1;
1125
1126   inst->refcount++; /* prevents method_change_done from destroying it */
1127
1128   ouposn_moving(inst);
1129   indep_check_execute();
1130
1131   inst->refcount--;
1132   if (!inst->n_changes)
1133     /* oh! */
1134     indep_indep_done(inst);
1135
1136   DPRINTF(movpos,entry, "movpos change %s/%s ok\n",
1137           move->i->pname, movpos_pname(move, target));
1138   return 0;
1139
1140  x:
1141   indep_remove(inst);
1142   indep_dispose(inst);
1143   ec= indep_install(move->motion, 0);  assert(!ec);
1144   ec= indep_install(resv, 0);          assert(!ec);
1145   indep_check_execute();
1146
1147   DPRINTF(movpos,entry, "movpos change %s/%s err=%s\n",
1148           move->i->pname, movpos_pname(move, target), ec2str(ec));
1149   return ec;
1150 }
1151
1152 void movpos_unreserve(MovPosChange *resv) {
1153   if (!resv) return;
1154   DPRINTF(movpos,entry, "movpos unreserve %s/%s...\n",
1155           resv->move->i->pname, movpos_pname(resv->move, resv->target));
1156   indep_remove(resv);
1157   indep_dispose(resv);
1158   indep_check_execute();
1159 }
1160
1161 MovPosComb movpos_change_intent(MovPosChange *indep) {
1162   return indep->target;
1163 }
1164
1165 void motions_all_abandon(void) {
1166   Method **meth;
1167   SEG_IV;
1168   DPRINTF(movpos,entry, "movpos motions_all_abandon...\n");
1169   FOR_SEG {
1170     if (!seg->moving) continue;
1171
1172     MovPosChange *abandon= seg->motion;
1173     indep_remove(abandon);
1174     seg->movposcomb= abandon->actualunk ? NOTA(MovPosComb) : abandon->actualpos;
1175     seg->moving= 0;
1176     seg->motion= 0;
1177     indep_dispose(abandon);
1178   }
1179   for (meth=methods; *meth; meth++)
1180     (*meth)->all_abandon(*meth);
1181 }
1182
1183 void movpos_reportall(void) {
1184   SEG_IV;
1185   int feat;
1186   
1187   FOR_SEG {
1188     assert(!seg->moving);
1189     if (seg->i->n_poscombs <= 1) continue;
1190     ouposn_stable(seg);
1191     if (SOMEP(seg->movposcomb)) {
1192       for (feat=0; feat<seg->i->n_movfeats; feat++) {
1193         const MovFeatInfo *feati= &seg->i->movfeats[feat];
1194         MovPosComb posn= (seg->movposcomb / feati->weight) % feati->posns;
1195         ouposn_feat(seg, feati, posn, feature_method(feati));
1196       }
1197     }
1198   }
1199 }