From: ian Date: Sat, 5 Apr 2008 19:01:44 +0000 (+0000) Subject: before remove special moving state which delays after point move X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ijackson/git?a=commitdiff_plain;h=83a432614169444c462caf5a219d0807f5a6f5d3;p=trains.git before remove special moving state which delays after point move --- diff --git a/hostside/movpos.c b/hostside/movpos.c index 7722f17..b450da0 100644 --- a/hostside/movpos.c +++ b/hostside/movpos.c @@ -6,32 +6,61 @@ /*========== 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 ==========*/ @@ -54,16 +83,32 @@ typedef struct MovPosChangeDetails { 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, @@ -75,67 +120,98 @@ typedef struct { /* Allocated Reserved Confirmed */ #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; @@ -167,23 +243,12 @@ static ErrorCode pt__check(void) { 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; } @@ -195,13 +260,21 @@ static ErrorCode pt__enqueue(PointQueue *q, PointReq *r) { /* XA -> X */ 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; @@ -209,69 +282,194 @@ static SomeChange pt_allocate(int 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; ih.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; tcombn_poscombs; tcomb++, pci++) { @@ -285,7 +483,7 @@ movpos_requestchange(Segment *back, Segment *move, *Segment *fwd, for (feat=0, feati=movei->movfeats, tchanges=0; featn_movfeats; feat++) - if ((tcomb - move->movposcomb) / feati->weight % feati->posns) + if ((tcomb - actual) / feati->weight % feati->posns) tchanges++; if (tchanges > bestchanges) continue; @@ -298,12 +496,11 @@ movpos_requestchange(Segment *back, Segment *move, *Segment *fwd, int n_motions=0; Motion motions[movei->n_movfeats]; - MovFeatKind kind= mfk_none; for (feat=0, feati=movei->movfeats; featn_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; } @@ -314,37 +511,28 @@ movpos_requestchange(Segment *back, Segment *move, *Segment *fwd, 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; @@ -355,15 +543,20 @@ movpos_reservechange(Segment *move, int maxdelay_ms, 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); +}