/*========== declarations ==========*/
-typedef struct SomeChange SomeChange;
-
typedef struct {
MoveFeatInfo *i;
Small posn;
} Motion;
-typedef struct {
- /* call flow is: allocate [[reserve] request] cancel
- * error from reserve or request causes call to destroy
+typedef struct KindInfo KindInfo;
+
+/* Kind-independent code is responsible for determining
+ * the method, doing a bit of cleanup, and adjusting the flow
+ * slightly. Per-kind code does the actual work and is mostly in
+ * charge - it is also responsible for updating seg->moving.
+ */
+/* The following states exist for each MovPosChange
+ * at points when control flow passes between kind and indep:
+ * U Unallocated no memory allocated (MovPosChange does not exist)
+ * A Allocated memory allocation done
+ * R Reserved reservation was successful
+ * C Confirmed motion queued and will occur
+ * E Erroneous indep must call destroy straight away
+ */
+
+typedef struct MovPosChange { /* valid in: filled in by and when: */
+ const KindInfo *ki; /* ARCE indep after allocate() */
+ Segment *move; /* ARCE indep after allocate() */
+ MovPosCallback *on_done; /* C indep before confirm() */
+ void *u; /* C indep before confirm() */
+ MovPosComb actual; /* C see below */
+ /* kind-specific data follows */ /* varies kind-specific code, varies */
+} Change;
+ /* `actual' contains the kind's record of the physical state. It is
+ * initialised by indep (just before confirm) from move->moving->actual
+ * or move->movposcomb as the case may be. It should be updated
+ * by the kind, since it is used by indep for calculating the number
+ * and identities of the features which may need to change when a
+ * new move request is intended to replace an existing one - ie, the
+ * contents of the motions[] provided to a subsequent confirm().
+ * So while a change is Confirmed, the physical state is recorded
+ * only in the relevant change, and not in the segment's movposcomb.
+ * Once a change goes to Confirmed, the indep code never untangles it
+ * so the kind can manage the proper transition.
*/
- SomeChange *(*allocate)(int alloc_motions); /* always succeeds */
- ErrorCode (*reserve)(SomeChange*, Segment *move, int maxdelay_ms);
- /* indep machinery guarantees alloc_motions >= move->i->n_motions */
- ErrorCode (*request)(SomeChange*, Segment *move,
- int n_motions, const Motion*, int maxdelay_ms);
- /* indep machinery guarantees alloc_motions >= n_motions */
- void (*destroy)(SomeChange*);
-} MethodInfo;
-
-typedef struct MovPosChangeDetails {
- const MethodInfo *mi;
- Segment *move;
- MovPosCallback *on_moving, *on_done;
- void *u;
-} ChangeHeader;
+
+struct KindInfo {
+ Change *(*allocate)(int alloc_motions); /* U->A (always succeeds) */
+ ErrorCode (*reserve)(Change*, Segment*, int ms); /* A->R; error: A->E */
+ ErrorCode (*confirm)(Change*, Segment*, int n_motions,
+ const Motion*, int ms); /* [AR]->C; error; [AR]->E */
+ void (*destroy)(Change*); /* [ARCE]->U */
+ /* indep guarantees that
+ * alloc_motions >= move->i->n_motions on reserve
+ * alloc_motions >= n_motions on confirm
+ * and that if on entry to reserve move->moving is non-0,
+ * it is of the same kind
+ */
+};
/*========== points ==========*/
typedef unsigned PtSlot;
typedef int PtSlotSigned;
-typedef struct { /* Allocated Reserved Confirmed */
- /* in queue? absent reserved confirmed */
- ChangeHeader h; these two */
- Slot deadline; /* ~0 relative absolute <- `statedet' */
- int n_motions; /* alloc'd alloc'd undone fields */
- Motion motions[];/* [0].i: 0 0 non-0 <- indicate */
- /* [..].i: undef undef non-0 state */
- /* .posn: undef undef defined */
+/* We think there are three states: Allocated, Reserved and Confirmed.
+ * (plus of course Unallocated where we don't have a request at all).
+ * These correspond to the indep code as follows:
+ *
+ * indep state pt state queues checked and plan viable
+ * Unallocated n/a yes
+ * Allocated Allocated yes
+ * Reserved Reserved yes
+ * Confirmed Confirmed yes
+ * Erroneous A/R/C no
+ *
+ * Erroneous exists only after a failed reserve() or confirm() so it's
+ * not that confusing to have this slightly malleable terminology.
+ */
+
+typedef struct { /* Allocated Reserved Confirmed */
+ /* in queue? absent reserved confirmed */
+ ChangeHeader h;
+ Slot deadline; /* ~0 relative absolute <- */
+ int n_motions; /* alloc'd alloc'd undone */
+ Motion motions[]; /* [0].i: 0 0 non-0 <- */
+ /* [..].i: undef undef non-0 */
+ /* .posn: undef undef defined */
} PointReq;
- /* We abbreviate these states as A R C
+ /* We can determine the the state by looking at the two
+ * `statedet' fields, marked <- above.
* There are also intermediate states where the req's
* statedet fields do not agree with the queue it's on.
* We write these as, for example,
#define CDU_RECHARGE 250 /*ms*/
#define POINT_MOVEMENT 50 /*ms*/
-#define PT_MAX_QUEUE 15
-
-static PtSlot pt_cslot;
-static int pt_cdu_charged;
+typedef struct {
+ int n;
+ PointReq *l[PT_MAX_QUEUE];
+} PointQueue;
todo
actually fire points: use item, and remove it, etc.
write check routine
-typedef struct {
- int n;
- PointReq *l[PT_MAX_QUEUE];
-} PointQueue;
+#define PT_MAX_QUEUE 15
-static PointQueue pt_confirmed, pt_reserved;
+/*
+ * CDU and point queue states:
+ *
+ *
+ * ____________ pt_cdu_ conf'd conf'd pt_
+ * / points_ \ charged .n .l[0]-> motion
+ * | all_ | n_motions timeout
+ * | abaondon |
+ * | V
+ * |from INACTIVE -1 0 n/a Idle
+ * |any <=Sta_Settling
+ * ^^^^^^^^ (start)
+ * |
+ * ____________ |turning
+ * / \| _on
+ * | V
+ * | CHARGING 0 any any or n/a Idle
+ * | >=Sta_Resolving
+ * | _________ |
+ * | / \|
+ * | | |on_pic
+ * | | |_charged
+ * | | V
+ * ^ ^ READY 1 any any or n/a Idle
+ * | | |
+ * | | |pt_check_action
+ * | ^ |fires a point
+ * |\___________/|
+ * | more |last motion
+ * | ^ motions |
+ * | | |
+ * | | V
+ * | | MOVING 0 >0 0 Running
+ * | \_________/|
+ * | |
+ * \____________/
+ * motion_done
+ */
-#define PT_DEL ((const MovFeatInfo*)info_segments) /* some random struct */
+static PtSlot pt_cslot;
+static int pt_cdu_charged;
+static PointQueue pt_confirmed, pt_reserved;
+static TimeoutEvent pt_motion_timeout;
-static pt__maxdelay_reldeadline(int maxdelay_ms) {
+static pt_maxdelay_reldeadline(int maxdelay_ms) {
return (maxdelay_ms - POINT_MOVEMENT + CDU_RECHARGE) / CDU_RECHARGE;
}
-static void pt__queue_remove_index(PointQueue *q, int index) {
+static void pt_queue_remove_index(PointQueue *q, int index) {
q->n--;
memmove(&q->l[index], &q->l[index+1], sizeof(q->l[0]) * (q->n - index));
}
-static void pt__req_compar(const void *av, const void *bv) {
+static void pt_req_compar(const void *av, const void *bv) {
PointQueue *const *a= av;
PointQueue *const *b= av;
return (PtSlotSigned)((*b)->deadline - (*a)->deadline);
}
-static void pt__queue_remove_item(PointQueue *q, PointReq *r) {
+static void pt_queue_remove_item(PointQueue *q, PointReq *r) {
PointQueue **entry;
- entry= bsearch(r, q->l, q->n, sizeof(q->l[0]), pt__req_compar);
+ entry= bsearch(r, q->l, q->n, sizeof(q->l[0]), pt_req_compar);
assert(entry);
- pt__queue_remove_index(q, entry - q->l);
+ pt_queue_remove_index(q, entry - q->l);
}
-static void pt__dequeue(PointReq *r) { /* X->XA */
+static void pt_dequeue(PointReq *r) { /* X->XA */
if (r->motions[0].i) {
- pt__queue_remove_item(&pt_confirmed, r);
+ pt_queue_remove_item(&pt_confirmed, r);
} else if (~r->deadline) {
- pt__queue_remove_item(&pt_reserved, r);
+ pt_queue_remove_item(&pt_reserved, r);
}
}
-static void pt_destroy(SomeChange *chg) { /* X->XA and then free it */
- PointReq *r= (PointReq*)chg;
- pt__dequeue(r);
- free(r);
-}
-
-static void pt__mark_as_allocated(PointReq *r) { /* AX->X */
+static void pt_mark_as_allocated(PointReq *r) { /* AX->X */
/* Sets statedet fields for Allocated */
r->deadline= ~(PtSlot)0;
r->motions[0].i=0;
}
-static ErrorCode pt__check(void) {
+static ErrorCode pt_check_plan(void) {
/* Checks whether we can meet the currently queued commitments */
int future, conf, resv;
future++;
}
return 0;
-}
-
- ;
-
-
-
-
- for (conf=0;
- conf <
-
-
}
-static ErrorCode pt__enqueue(PointQueue *q, PointReq *r) { /* XA -> X */
+static ErrorCode pt_enqueue(PointQueue *q, PointReq *r) { /* XA -> X */
ErrorCode ec; /* ... where X is R or C and corresponds to q */
/* or on error, XA -> A */
-
+
if (q->n == PT_MAX_QUEUE) {
return EC_BufferFull;
}
q->l[insat]= r;
q->n++;
- return pt__check();
+ return pt_check();
/* if this fails, indep machinery calls pt_destroy which dequeues */
}
+/*---------- kind method entrypoints ----------*/
+
static SomeChange pt_allocate(int alloc_motions) {
PointReq= *r;
+ assert(pt_cdu_charged>=0);
+ if (!alloc_motions)
+ /* we need at least one motion in the table so we can tell
+ * the difference between the states by looking at motions[0].i */
+ alloc_motions= 1;
+
r= mmalloc(sizeof(*r) + alloc_motions * sizeof(r->motions[0]));
r->deadline= ~(PtSlot)0;
r->n_motions= alloc_motions;
return (SomeChange*)r;
}
-static ErrorCode pt_reserve(SomeChange *chg, Segment *move, int maxdelay_ms) {
+static ErrorCode point_reserve(SomeChange *chg, Segment *move,
+ int maxdelay_ms) {
PointReq *r= (PointReq*)chg;
r->deadline= pt_reldeadline(maxdelay_ms);
- if (!r->deadline) { pt__mark_as_allocated(r); return EC_Points; }
+ if (!r->deadline) { pt_mark_as_allocated(r); return EC_Points; }
return pt_queue_add(&pt_reserved, r);
}
-static ErrorCode pt_request(SomeChange *chg, Segment *move,
- int n_motions, const Motion *motions,
- int maxdelay_ms) {
- /* [AR]->C; on error: [AR]->X */
+static ErrorCode point_confirm(SomeChange *chg, Segment *move,
+ int n_motions, const Motion *motions,
+ int maxdelay_ms) {
PointReq *r= (PointReq*)chg;
PtSlot newdeadline;
int allow_failure;
+ /* If the segment is moving, these motions are already based on the
+ * actual physical position which is stored in the existing request.
+ * So we try removing the existing request from the queue and put
+ * it back if it doesn't work.
+ */
+
assert(n_motions <= r->n_motions);
newdeadline= pt_reldeadline(maxdelay_ms) + cslot;
allow_failure= newdeadline < r->deadline;
- /* state [AR] */
- pt__dequeue(r);
- /* state [AR]A */
- assert(n_motions>0 && motions[0].i);
+ /* state A or R */
+ pt_dequeue(r);
+ /* states of existing: */
+ PointReq *existing= move->moving; /* U or C */
+ if (existing) pt_dequeue(existing); /* U or CA */
+
+ /* state A or RA */
memcpy(r->motions, motions, sizeof(r->motions[0])*n_motions);
+ if (!n_motions) motions[0].i= move->i->movfeats;
+ assert(motions[0].i);
r->n_motions= n_motions;
r->deadline= newdeadline + pt_cslot;
+
/* state CA */
- ec= pt__enqueue(&pt_confirmed, r);
+ ec= pt_enqueue(&pt_confirmed, r);
assert(allow_failure || !ec);
+
+ if (existing) { /* CA */
+ if (ec) { /* state C but bad */
+ pt_dequeue(r); /* state CA */
+ pt_mark_as_allocated(r); /* state A */
+ ErrorCode ec_putback= pt_enqueue(&pt_confirmed, existing);
+ assert(!ec_putback); /* C */
+ } else { /* state C and good */
+ free(existing); /* U */
+ }
+ }
+ /* either ec=0 state C U
+ * or ec!=0 state A C
+ * or ec!=0 state C but bad C
+ */
+
+ if (!ec) pt_check_action();
+
return ec;
}
+static void pt_destroy(Change *chg) { /* X->XA and then free it */
+ PointReq *r= (PointReq*)chg;
+ pt_dequeue(r);
+ free(r);
+}
+
+/*---------- actually firing points, yay! ----------*/
+
+static void pt_check_action(void) {
+ PicInsn piob;
+
+ if (!pt_confirmed.n) {
+ toev_stop(&pt_motion_timeout);
+ return;
+ }
+
+ PointReq *r= pt_confirmed.l[0];
+
+ if (r->n_motions) {
+ if (!pt_cdu_charged) {
+
+
+ Motion *m= &r->motions[--r->n_motions];
+ assert(m->posn < m->i->posns);
+ enco_pic_point(&piob, m->i->boob[m->posn]);
+ serial_transmit(&piob);
+ pt_cdu_charged= 0;
+
+ MovPosComb above_weight= m->i->weight * m->i->posns;
+ MovPosComb above= r->actual / above_weight;
+ MovPosComb below= r->actual % m->i->weight;
+ r->actual= above*above_weight + m->posn*m->i->weight + below;
+ }
+
+ if (!r->n_motions)
+ toev_start(&pt_motion_timeout);
+}
+
+static void pt_motion_done(TimeoutEvent *toev) {
+ assert(pt_confirmed.n);
+ PointReq *r= pt_confirmed.l[0];
+ assert(!r->n_motions);
+ Segment *move= r->h.move;
+ assert(move->moving == r);
+ move->movposcomb= r->actual;
+ move->moving= 0;
+ pt_queue_remove_index(&pt_confirmed,0);
+ r->h.on_done(move, r->h.u);
+ free(r);
+
+ pt_check_action();
+}
+
/*---------- entrypoints from rest of program ----------*/
+static void points_all_abandon(void) {
+ int i;
+
+ assert(!pt_reserved.n);
+
+ for (i=0; i<pt_confirmed.n; i++) {
+ PointReq *r= pt_confirmed.l[i];
+ Segment *move= r->h.move;
+ assert(move->moving == (PointReq*)r);
+ move->moving= 0;
+ move->movposcomb= r->h.actual;
+ free(r);
+ }
+ pt_confirmed.n= 0;
+ pt_cdu_charged= -1;
+ toev_stop(&pt_motion_timeout);
+}
+
static void points_turning_on(void) {
pt_cdu_charged= 0;
+ pt_motion_timeout.duration= POINT_MOVEMENT;
+ pt_motion_timeout.callback= pt_motion_done;
+ assert(!pt_motion_timeout.running);
}
void on_pic_charged(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
+ if (pt_cdu_charged<0) return;
pt_cdu_charged= 1;
- if (sta_state <= Sta_Settling) return;
- fixme check fault arrangements wrt cdu and points etc.
- fixme do something here
+ if (pt_motion_timeout.running) {
+ toev_stop(&pt_motion_timeout);
+ pt_motion_done(0);
+ }
+ pt_check_action();
}
-
/*========== method-independent machinery ==========*/
-static const MethodInfo methodinfos[]= {
- { nomove_allocate, nomove_reserve, nomove_request, nomove_cancel }
- { point_allocate, point_reserve, point_request, point_cancel }
+static const KindInfo methodinfos[]= {
+ { nomove_allocate, nomove_reserve, nomove_request, nomove_cancel },
+ { point_allocate, point_reserve, point_request, point_cancel },
+ 0
};
+ChangeHeader *mp_allocate(const KindInfo *ki, Segment *move,
+ int alloc_motions) {
+ assert(sta_state >= Sta_Resolving);
+ chg= (ChangeHeader*)ki->allocate(alloc_motions);
+ chg->ki= ki;
+ chg->move= move;
+ return chg;
+}
+
ErrorCode
-movpos_requestchange(Segment *back, Segment *move, *Segment *fwd,
- int maxdelay_ms, MovPosChangeRequest *req_io,
- MovPosCallback *on_moving, MovPosCallback *on_done, *u) {
+movpos_change(Segment *back, Segment *move, *Segment *fwd,
+ int maxdelay_ms, MovPosChange chg,
+ MovPosCallback *on_done, *u) {
const SegmentInfo *movei= move->i;
SegPosCombInfo *pci;
- MovPosComb tcomb, bestcomb=0;
+ MovPosComb actual, tcomb, bestcomb=0;
int tchanges, bestchanges=INT_MAX;
ErrorCode ec;
+ MovFeatKind kind= mfk_none;
+
+ if (move->moving) {
+ kind= move->moving->ki - methodinfos;
+ actual= move->moving->actual;
+ } else {
+ actual= move->movposcomb;
+ }
+
for (tcomb=0, pci=movei->poscombs;
tcomb<movei->n_poscombs;
tcomb++, pci++) {
for (feat=0, feati=movei->movfeats, tchanges=0;
feat<movei->n_movfeats;
feat++)
- if ((tcomb - move->movposcomb) / feati->weight % feati->posns)
+ if ((tcomb - actual) / feati->weight % feati->posns)
tchanges++;
if (tchanges > bestchanges)
continue;
int n_motions=0;
Motion motions[movei->n_movfeats];
- MovFeatKind kind= mfk_none;
for (feat=0, feati=movei->movfeats;
feat<movei->n_movfeats;
feat++, feati++) {
- if ((bestcomb - move->movposcomb) / feati->weight % feati->posns)
+ if ((bestcomb - actual) / feati->weight % feati->posns)
continue;
if (kind) {
if (feati->kind != kind) { ec= EC_MovFeatKindsCombination; goto x; }
n_motions++;
}
- const MethodInfo *mi= methodinfos[kind];
+ const KindInfo *ki= methodinfos[kind];
- if (!*req_io) {
- (*req_io)= (ChangeHeader*)mi->allocate(n_motions);
- (*req_io)->mi= mi;
- (*req_io)->move= move;
- (*req_io)->on_moving= on_moving;
- (*req_io)->on_done= on_done;
- (*req_io)->u= u;
+ if (chg) {
+ assert(move == chg->move);
} else {
- if (move != (*req_io)->move) { ec= EC_Invalid; return; }
+ chg= mp_allocate(ki,move,n_motions);
}
+ chg->actual= actual;
+ chg->on_done= on_done;
+ chg->u= u;
- ec= mi->request((SomeChange*)*req_io, move, n_motions, motions, maxdelay_ms);
+ ec= ki->request(chg, move, n_motions, motions, maxdelay_ms);
if (ec) goto x;
-
return 0;
x:
- movpos_cancelchange(*req_io);
+ movpos_unreserve(chg);
return ec;
}
-void movpos_cancelchange(MovPosChangeRequest *req) {
- if (!req) return;
- req->mi->cancel((SomeChange*)req);
-}
-
ErrorCode
-movpos_reservechange(Segment *move, int maxdelay_ms,
- MovPosChangeRequest *res_r) {
+movpos_reservechange(Segment *move, int maxdelay_ms, MovPosChange *res_r) {
MovFeatKind kind= mfk_none;
for (feat=0; feati=movei->movfeats;
kind= feati->kind;
}
- const MethodInfo *mi= methodinfos[kind];
- SomeChange *chg= mi->allocate(movei->n_movfeats);
- ec= mi->reserve(chg, move, maxdelay_ms);
+ const KindInfo *ki= methodinfos[kind];
+ ChangeHeader *chg= mp_allocate(ki, move, movei->n_movfeats);
+ ec= ki->reserve(chg, move, maxdelay_ms);
if (ec) goto x;
- *res_r= (ChangeHeader*)chg;
+ *res_r= chg;
return 0;
x:
- movpos_cancelchange((ChangeHeader*)chg);
+ movpos_unreserve(chg);
return ec;
}
+
+void movpos_unreserve(MovPosChange *res) {
+ if (!res) return;
+ req->ki->destroy(req);
+}