2 * Handling of points and other moveable features.
7 /*========== declarations ==========*/
14 typedef struct KindInfo KindInfo;
16 /* Kind-independent code is responsible for determining
17 * the method, doing a bit of cleanup, and adjusting the flow
18 * slightly. Per-kind code does the actual work and is mostly in
19 * charge - it is also responsible for updating seg->moving.
21 /* The following states exist for each MovPosChange
22 * at points when control flow passes between kind and indep:
23 * U Unallocated no memory allocated (MovPosChange does not exist)
24 * A Allocated memory allocation done
25 * R Reserved reservation was successful
26 * C Confirmed motion queued and will occur
27 * D Done motion is complete and callback just needs to be made
28 * E Erroneous indep must call destroy straight away
29 * seg->moving is in one of the states UC
32 typedef struct MovPosChange { /* valid in: filled in by and when: */
33 const KindInfo *ki; /* ARCDE indep after allocate() */
34 Segment *move; /* ARCDE indep after allocate() */
35 MovPosComb actual; /* CD see below */
36 MovPosComb intent; /* RCD indep after allocate() */
37 /* kind-specific data follows */ /* varies kind-specific code, varies */
39 /* `actual' contains the kind's public opinion about the physical
40 * state. It is initialised by indep (just before confirm) from
41 * move->moving->actual or move->movposcomb as the case may be. It
42 * should be updated by the kind, since it is used by indep for
43 * calculating the number and identities of the features which may
44 * need to change when a new move request is intended to replace an
45 * existing one - ie, the contents of the motions[] provided to a
46 * subsequent confirm(). So while a change is Confirmed, the
47 * physical state is recorded only in the relevant change, and not
48 * in the segment's movposcomb. Once a change goes to Confirmed,
49 * the indep code never untangles it so the kind can manage the
50 * proper transition. */
53 Change *(*allocate)(int alloc_motions); /* U->A (always succeeds) */
54 ErrorCode (*reserve)(Change*, Segment*, int ms); /* A->R; error: A->E */
55 ErrorCode (*confirm)(Change*, Segment*, int n_motions,
56 const Motion*, int ms); /* [AR]->C; error; [AR]->E */
57 void (*destroy)(Change*); /* [ARCE]->U */
58 /* indep guarantees that
59 * alloc_motions >= move->i->n_motions on reserve
60 * alloc_motions >= n_motions on confirm
61 * and that if on entry to reserve move->moving is non-0,
62 * it is of the same kind
66 /*========== points ==========*/
69 * We maintain two queues, one for reserved one for actually confirmed
70 * requests where we know what we're doing.
72 * We divide time into discrete slots, numbered with clock arithmetic.
74 * cslot cslot+1 cslot+2
76 * currently next in after
79 * We increment cslot when we issue a POINT command to the PIC.
80 * In a request, the deadline represents the latest allowable value
81 * of cslot just before that increment.
84 typedef unsigned PtSlot;
85 typedef int PtSlotSigned;
87 /* We think there are three states: Allocated, Reserved and Confirmed.
88 * (plus of course Unallocated where we don't have a request at all).
89 * These correspond to the indep code as follows:
91 * indep state pt state queues checked and plan viable
93 * Allocated Allocated yes
94 * Reserved Reserved yes
95 * Confirmed Confirmed yes
98 * Erroneous exists only after a failed reserve() or confirm() so it's
99 * not that confusing to have this slightly malleable terminology.
102 typedef struct { /* Allocated Reserved Confirmed */
103 /* in queue? absent reserved confirmed */
105 PtSlot deadline; /* ~0 relative absolute <- */
106 MovPosComb actual; /* undef undef see below */
107 int n_motions; /* alloc'd alloc'd undone */
108 Motion motions[]; /* [0].i: 0 0 non-0 <- */
109 /* [..].i: undef undef non-0 */
110 /* .posn: undef undef defined */
112 /* We can determine the the state by looking at the two
113 * `statedet' fields, marked <- above.
114 * There are also intermediate states where the req's
115 * statedet fields do not agree with the queue it's on.
116 * We write these as, for example,
117 * AR to mean statedet says Allocated, but queued on pt_reserved
118 * A? to mean statedet says Allocated, but may be queued
119 * etc. They are only allowed while we are in a pt_... method function.
121 /* PointReq.actual is subtly differnet to MovPosChange.actual,
123 * in MovPosChange in PointReq
124 * Position unknown -1 0
125 * Position partly known -1 unknown feats are 0
126 * Position completely known exact exact
128 * The partial knowledge positions can only occur in requests that
129 * are confirmed with as many motions as features, so we know that
130 * if we complete a request we know that we can copy actual out
133 * If we abandon a half-done change to a multi-feat segment
134 * we lose the partial knowledge.
137 #define CDU_RECHARGE 250 /*ms*/
138 #define POINT_MOVEMENT 50 /*ms*/
139 #define PT_MAX_QUEUE 15
143 PointReq *l[PT_MAX_QUEUE];
147 * CDU and point queue states:
150 * ____________ pt_cdu_ conf'd
151 * / points_ \ charged .n
155 * |from INACTIVE -1 0
156 * |any <=Sta_Settling
159 * ___________ |turning
176 static PtSlot pt_cslot;
177 static int pt_cdu_charged;
178 static PointQueue pt_confirmed, pt_reserved;
180 static void pt_check_action(void);
182 static PtSlot pt_maxdelay_reldeadline(int maxdelay_ms) {
183 return (maxdelay_ms - POINT_MOVEMENT + CDU_RECHARGE) / CDU_RECHARGE;
186 static void pt_queue_remove_index(PointQueue *q, int index) {
188 memmove(&q->l[index], &q->l[index+1], sizeof(q->l[0]) * (q->n - index));
191 static int pt_req_compar(const void *av, const void *bv) {
192 PointReq *const *a= av;
193 PointReq *const *b= av;
194 return (PtSlotSigned)((*b)->deadline - (*a)->deadline);
197 static void pt_queue_remove_item(PointQueue *q, PointReq *r) {
199 entry= bsearch(r, q->l, q->n, sizeof(q->l[0]), pt_req_compar);
201 pt_queue_remove_index(q, entry - q->l);
204 static void pt_dequeue(PointReq *r) { /* X->XA */
205 if (r->motions[0].i) {
206 pt_queue_remove_item(&pt_confirmed, r);
207 } else if (~r->deadline) {
208 pt_queue_remove_item(&pt_reserved, r);
212 static void pt_mark_as_allocated(PointReq *r) { /* AX->X */
213 /* Sets statedet fields for Allocated */
214 r->deadline= ~(PtSlot)0;
218 static ErrorCode pt_check_plan(void) {
219 /* Checks whether we can meet the currently queued commitments */
220 int future, conf, resv, usewhen;
224 /* If CDU is charged we can do one thing right away */
225 while (conf < pt_confirmed.n &&
226 pt_confirmed.l[0]->deadline==pt_cslot) {
227 if (!pt_cdu_charged) return EC_MovFeatTooLate;
228 if (conf) return EC_MovFeatTooLate;
234 PointReq *confr= conf < pt_confirmed.n ? pt_confirmed.l[conf] : 0;
235 PointReq *resvr= resv < pt_reserved .n ? pt_reserved .l[conf] : 0;
236 if (!confr && !resvr) break;
237 int confwhen= confr ? confr->deadline - pt_cslot : INT_MAX;
238 int resvwhen= resvr ? resvr->deadline : INT_MAX;
239 if (resvwhen < confwhen) {
246 if (usewhen > future) return EC_MovFeatTooLate;
252 static ErrorCode pt_enqueue(PointQueue *q, PointReq *r) { /* XA -> X */
253 int insat; /* ... where X is R or C and corresponds to q */
254 /* or on error, XA -> A */
256 if (q->n == PT_MAX_QUEUE) {
257 return EC_BufferFull;
260 fprintf(stderr," pt_enqueue\n");
262 insat>0 && (PtSlotSigned)(r->deadline - q->l[insat-1]->deadline) < 0;
264 q->l[insat]= q->l[insat-1];
268 return pt_check_plan();
269 /* if this fails, indep machinery calls pt_destroy which dequeues */
272 /*---------- kind method entrypoints ----------*/
274 static Change *point_allocate(int alloc_motions) {
277 fprintf(stderr," point allocate %d\n",alloc_motions);
278 assert(pt_cdu_charged>=0);
280 /* we need at least one motion in the table so we can tell
281 * the difference between the states by looking at motions[0].i */
284 r= mmalloc(sizeof(*r) + alloc_motions * sizeof(r->motions[0]));
285 r->deadline= ~(PtSlot)0;
286 r->n_motions= alloc_motions;
291 static ErrorCode point_reserve(Change *chg, Segment *move,
293 PointReq *r= (PointReq*)chg;
294 r->deadline= pt_maxdelay_reldeadline(maxdelay_ms);
295 if (!r->deadline) { pt_mark_as_allocated(r); return EC_MovFeatTooLate; }
296 return pt_enqueue(&pt_reserved, r);
299 static ErrorCode point_confirm(Change *chg, Segment *move,
300 int n_motions, const Motion *motions,
302 PointReq *r= (PointReq*)chg;
307 fprintf(stderr," point confirm\n");
309 /* If the segment is moving, these motions are already based on the
310 * actual physical position which is stored in the existing request.
311 * So we try removing the existing request from the queue and put
312 * it back if it doesn't work.
315 if (n_motions >= r->n_motions)
316 return EC_MovFeatReservationInapplicable;
317 assert(n_motions <= r->n_motions);
318 if (maxdelay_ms == -1) {
319 newdeadline= r->deadline;
321 newdeadline= pt_maxdelay_reldeadline(maxdelay_ms);
323 allow_failure= newdeadline < r->deadline;
324 newdeadline += pt_cslot;
328 /* states of existing: */
329 PointReq *existing= (PointReq*)move->moving; /* U or C */
330 if (existing) pt_dequeue(existing); /* U or CA */
333 memcpy(r->motions, motions, sizeof(r->motions[0])*n_motions);
334 if (!n_motions) r->motions[0].i= move->i->movfeats;
335 assert(r->motions[0].i);
336 r->n_motions= n_motions;
337 r->deadline= newdeadline + pt_cslot;
339 if (n_motions == move->i->n_movfeats)
342 r->actual= chg->actual;
343 assert(r->actual >= 0);
346 ec= pt_enqueue(&pt_confirmed, r);
347 assert(allow_failure || !ec);
349 if (existing) { /* CA */
350 if (ec) { /* state C but bad */
351 pt_dequeue(r); /* state CA */
352 pt_mark_as_allocated(r); /* state A */
353 ErrorCode ec_putback= pt_enqueue(&pt_confirmed, existing);
354 assert(!ec_putback); /* C */
355 } else { /* state C and good */
356 free(existing); /* U */
359 /* either ec=0 state C U
361 * or ec!=0 state C but bad C
366 move->movposcomb= -1;
373 static void point_destroy(Change *chg) { /* X->XA and then free it */
374 PointReq *r= (PointReq*)chg;
379 /*---------- actually firing points, yay! ----------*/
381 static void pt_check_action(void) {
384 if (!pt_confirmed.n) {
385 if (sta_state == Sta_Finalising) resolve_motioncheck();
389 PointReq *r= pt_confirmed.l[0];
391 if (r->n_motions && pt_cdu_charged) {
392 /* look for something to fire */
393 Motion *m= &r->motions[--r->n_motions];
394 assert(m->posn < m->i->posns);
395 enco_pic_point(&piob, m->i->boob[m->posn]);
396 serial_transmit(&piob);
399 MovPosComb above_weight= m->i->weight * m->i->posns;
400 MovPosComb above= r->actual / above_weight;
401 MovPosComb below= r->actual % m->i->weight;
402 r->actual= above*above_weight + m->posn*m->i->weight + below;
403 if (r->h.actual >= 0 || !r->n_motions)
404 r->h.actual= r->actual;
408 /* look for something to report
409 * we can get things here other than from the above
410 * eg if we are asked to move the
412 Segment *move= r->h.move;
413 assert(move->moving == (Change*)r);
414 pt_queue_remove_index(&pt_confirmed,0);
415 pt_mark_as_allocated(r); /* now state A aka Done */
416 move->movposcomb= r->h.actual;
423 /*---------- entrypoints from rest of program ----------*/
425 void points_all_abandon(void) {
428 assert(!pt_reserved.n);
430 for (i=0; i<pt_confirmed.n; i++) {
431 PointReq *r= pt_confirmed.l[i];
432 Segment *move= r->h.move;
433 assert(move->moving == (Change*)r);
435 move->movposcomb= r->h.actual;
442 void points_turning_on(void) {
446 void on_pic_charged(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
447 if (pt_cdu_charged<0) return;
452 /*========== dummy `nomove' kind ==========*/
454 static Change *nomove_allocate(int alloc_motions) {
455 fprintf(stderr," nomove allocate %d\n",alloc_motions);
456 return mmalloc(sizeof(Change));
458 static void nomove_destroy(Change *chg) {
462 static ErrorCode nomove_reserve(Change *chg, Segment *move, int ms) {
463 fprintf(stderr," nomove reserve\n");
466 static ErrorCode nomove_confirm(Change *chg, Segment *move, int n_motions,
467 const Motion *motions, int ms) {
468 fprintf(stderr," nomove confirm\n");
473 /*========== method-independent machinery ==========*/
475 static const KindInfo methodinfos[]= {
476 { nomove_allocate, nomove_reserve, nomove_confirm, nomove_destroy },
477 { point_allocate, point_reserve, point_confirm, point_destroy },
481 static Change *mp_allocate(const KindInfo *ki, Segment *move,
482 int alloc_motions, MovPosComb target) {
483 assert(sta_state >= Sta_Resolving);
484 Change *chg= ki->allocate(alloc_motions);
491 static int change_needed(const MovFeatInfo *feati, MovPosComb target,
492 MovPosComb startpoint) {
495 (target - startpoint) / feati->weight % feati->posns;
498 static int evaluate_target(Segment *move, MovPosComb target,
499 MovPosComb startpoint, MovFeatKind *kind_r) {
500 /* returns number of features which have to change to reach target,
501 * or -1 for mixed kinds. kind_r may be 0. */
502 const SegmentInfo *movei= move->i;
504 const MovFeatInfo *feati;
507 if (startpoint<0) startpoint= movpos_poscomb_actual(move);
509 for (feat=0, feati=movei->movfeats, tchanges=0, kind= mfk_none;;
510 feat<movei->n_movfeats;
512 if (!change_needed(feati,target,startpoint)) continue;
514 if (kind && feati->kind != kind) return -1;
518 if (kind_r) *kind_r= kind;
522 ErrorCode movpos_findcomb_bysegs(Segment *back, Segment *move, Segment *fwd,
523 MovPosComb startpoint, MovPosComb *chosen_r) {
524 const SegmentInfo *movei= move->i;
525 MovPosComb tcomb, bestcomb=-1;
526 int tchanges, bestchanges=INT_MAX;
527 const SegPosCombInfo *pci;
529 for (tcomb=0, pci=movei->poscombs;
530 tcomb<movei->n_poscombs;
532 Segment *tback= &segments[pci->backwards.next];
533 Segment *tfwd= &segments[pci->forwards .next];
534 if (back && !(back==tback || back==tfwd)) continue;
535 if (fwd && !(fwd ==tback || fwd ==tfwd)) continue;
537 if (movei->n_movfeats>1) {
538 //fprintf(stderr," several feats\n");
539 /* we have to search for the one which is least effort, then */
540 tchanges= evaluate_target(move,tcomb,startpoint,0);
541 if (tchanges >= bestchanges) /* prefer low-numbered movposcombs */
545 /* fall through and update */
551 bestchanges= tchanges;
553 if (*chosen_r) *chosen_r= bestcomb;
555 tchanges==INT_MAX ? EC_Invalid :
556 tchanges==INT_MAX-1 ? EC_MovFeatKindsCombination :
560 ErrorCode movpos_change(Segment *move, MovPosComb target,
561 int maxdelay_ms, MovPosChange *chg) {
562 const SegmentInfo *movei= move->i;
563 const MovFeatInfo *feati;
567 MovFeatKind kind= mfk_none;
570 actual= move->movposcomb;
571 assert(!move->motion);
573 kind= move->motion->ki - methodinfos;
574 actual= move->motion->actual;
579 Motion motions[movei->n_movfeats];
581 fprintf(stderr," motions... best=%lu actual=%ld\n",target,actual);
582 for (feat=0, feati=movei->movfeats;
583 feat<movei->n_movfeats;
585 fprintf(stderr," checking %s w=%lu posns=%d\n",
586 feati->pname,feati->weight,(int)feati->posns);
587 if (!change_needed(feati,actual,target))
589 MovPosComb posn= target / feati->weight % feati->posns;
590 fprintf(stderr," motion %s %lu kind=%d\n",feati->pname,posn,kind);
592 if (feati->kind != kind) { ec= EC_MovFeatKindsCombination; goto x; }
596 motions[n_motions].i= feati;
597 motions[n_motions].posn= posn;
601 const KindInfo *ki= &methodinfos[kind];
606 chg->intent != intent)
607 return EC_MovFeatReservationInapplicable;
609 chg= mp_allocate(ki,move,n_motions,target);
613 fprintf(stderr," confirming %d motions...\n",n_motions);
614 ec= ki->confirm(chg, move, n_motions, motions, maxdelay_ms);
615 fprintf(stderr," confirming gave %s\n",errorcodelist[ec]);
621 movpos_unreserve(chg);
626 movpos_reserve(Segment *move, int maxdelay_ms, MovPosChange **res_r,
627 MovPosComb target, MovPosComb startpoint /*as for findcomb*/) {
628 MovFeatKind kind= mfk_none;
629 const MovFeatInfo *feati;
633 nchanges= evaluate_target(move,target,startpoint,&kind);
634 if (nchanges=-1) return EC_MovFeatKindsCombination;
636 const KindInfo *ki= &methodinfos[kind];
637 Change *chg= mp_allocate(ki, move, move->i->n_movfeats, target);
638 ec= ki->reserve(chg, move, maxdelay_ms);
645 movpos_unreserve(chg);
649 void movpos_unreserve(MovPosChange *res) {
651 res->ki->destroy(res);
654 MovPosComb movpos_poscomb_actual(Segment *seg) {
655 return seg->moving ? seg->moving->actual : seg->movposcomb;
658 MovPosComb movpos_change_intent(MovPosChange *chg) {