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