--- /dev/null
+/*
+ * Handling of points and other moveable features.
+ */
+
+#include "realtime.h"
+
+/*---------- 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
+ */
+ SomeChange *(*allocate)(int n_motions); /* always succeeds */
+ ErrorCode (*reserve)(SomeChange*, Segment *move, int maxdelay_ms);
+ ErrorCode (*request)(SomeChange*, Segment *move,
+ int n_motions, const Motion*, int maxdelay_ms);
+ void (*destroy)(SomeChange*);
+} MethodInfo;
+
+typedef struct MovPosChangeDetails {
+ const MethodInfo *mi;
+ Segment *move;
+ MovPosCallback *on_moving, *on_done;
+ void *u;
+} ChangeHeader;
+
+
+/*---------- method-independent machinery ----------*/
+
+static const MethodInfo methodinfos[]= {
+ { nomove_allocate, nomove_reserve, nomove_request, nomove_cancel }
+ { point_allocate, point_reserve, point_request, point_cancel }
+};
+
+ErrorCode
+movpos_requestchange(Segment *back, Segment *move, *Segment *fwd,
+ int maxdelay_ms, MovPosChangeRequest *req_io,
+ MovPosCallback *on_moving, MovPosCallback *on_done, *u) {
+ const SegmentInfo *movei= move->i;
+ SegPosCombInfo *pci;
+ MovPosComb tcomb, bestcomb=0;
+ int tchanges, bestchanges=INT_MAX;
+ ErrorCode ec;
+
+ for (tcomb=0, pci=movei->poscombs;
+ tcomb<movei->n_poscombs;
+ tcomb++, pci++) {
+ Segment *tback= &segments[pci->backwards.next];
+ Segment *tfwd= &segments[pci->forwards .next];
+ if (back && !(back==tback || back=tfwd)) continue;
+ if (fwd && !(fwd ==tback || fwd =tfwd)) continue;
+
+ if (movei->n_movfeats>1) {
+ /* we have to search for the one which is least effort, then */
+ for (feat=0, feati=movei->movfeats, tchanges=0;
+ feat<movei->n_movfeats;
+ feat++)
+ if ((tcomb - move->movposcomb) / feati->weight % feati->posns)
+ tchanges++;
+ if (tchanges > bestchanges)
+ continue;
+ }
+ tcomb= bestcomb;
+ tchanges= bestchanges;
+ }
+
+ if (bestchanges==INT_MAX) { ec= EC_Invalid; goto x; }
+
+ 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)
+ continue;
+ if (kind) {
+ if (feati->kind != kind) { ec= EC_MovFeatKindsCombination; goto x; }
+ kind= feati->kind;
+ }
+ motions[n_motions].i= feati;
+ motions[n_motions].posn= bestcomb / feati->weight % feati->posns;
+ n_motions++;
+ }
+
+ const MethodInfo *mi= 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;
+ }
+
+ ec= mi->request((SomeChange*)*req_io, move, n_motions, motions, maxdelay_ms);
+ if (ec) goto x;
+
+ return 0;
+
+ x:
+ movpos_cancelchange(*req_io);
+ 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) {
+ MovFeatKind kind= mfk_none;
+
+ for (feat=0; feati=movei->movfeats;
+ feat<movei->n_movfeats;
+ feat++, feati++)
+ if (kind) {
+ if (feati->kind != kind) return EC_MovFeatKindsCombination;
+ kind= feati->kind;
+ }
+
+ const MethodInfo *mi= methodinfos[kind];
+ SomeChange *chg= mi->allocate(movei->n_movfeats);
+ ec= mi->reserve(chg, move, maxdelay_ms);
+ if (ec) goto x;
+
+ *res_r= (ChangeHeader*)chg;
+ return 0;
+
+ x:
+ movpos_cancelchange((ChangeHeader*)chg);
+ return ec;
+}
+
+
+
+ }
+
+
+ }
+
+
+ (*req_io)->move= move;
+ (*req_io)->move= move;
+
+ ChangeHeader *change= method(move, n_motions, motions, *req_io);
+ if (!change)
+ return EC_Point;
+
+ change
+ change.on_moving= on_moving;
+ change.on_done= on_done;
+ change.u= u;
+
+ *req_io=
+
+
+
+ MovFeatKind tkind
+ if (tchanges
+
+ unsigned diff= (
+
+
+ / feati->weight
+
+ unsigned posold= (move->movposcomb / feati->weight) % feati->posns;
+
+ }
+
+ for (feat=0, feati=movei->movfeats, result=0;
+ feat<movei->n_movfeats;
+ feat++) {
+ unsigned posold= (move->movposcomb / feati->weight) % feati->posns;
+ for (poschange=0; poschange<feati->posns; poschange++) {
+ unsigned posnew= poschange % feati->posns;
+
+
+/*
+ * Currently there is only one queue for the one CDU.
+ *
+ * We maintain a queue of change requests. The queue can also contain
+ * reservations. We divide time into discrete slots, numbered with
+ * clock arithmetic.
+ *
+ * cslot cslot+1 cslot+2
+ *
+ * currently next in after
+ * changing line that
+ */
+
+typedef unsigned Slot;
+
+typedef struct PointChangeQueueEntry {
+ struct PointChangeQueueEntry *next, *back;
+
+ Slot latest, needs;
+ Segment *move;
+ Motion motions[];
+} QE;
+
+static Slot cslot;
+static struct { QE *head, *tail; } queue;
+
+convert incoming latest's into queue lengths as simple counts
+maintain latest count number for each
unsigned
tr_backwards:1, /* train's motion is (would be) backwards wrt track */
ho_backwards:1, /* home train has its front and rear backwards wrt track */
- movfeat_moving:1, /* feature(s) have been told to change to movposcomb */
+ points_moving:1, /* feature(s) have been told to change to movposcomb */
cm_autostop:1, /* train should stop on detection */
seg_inverted:1, /* polarity is inverted */
tr_updated:1, /* for use by safety.c:lay_train etc.; otherwise 0 */
void safety_notify_detection(Segment *seg);
/* Called by startup.c when new train detection occurs in state Run. */
+/*========== movpos.c ==========*/
+/*
+ * movpos.c manages the CDU and points and other moveable features.
+ * Only safety.c should request changes.
+ */
+
+typedef struct MovPosChangeDetails *MovPosChangeRequest;
+typedef void MovPosCallback(Segment*, MovPosChangeRequest, void *u);
+
+ErrorCode
+movpos_requestchange(Segment *back, Segment *move, Segment *fwd,
+ int maxdelay_ms, MovPosChangeRequest *req_io,
+ MovPosCallback *on_moving, MovPosCallback *on_done,
+ void *u);
+ /* back and fwd may be 0 if we don't care (and must be if there is
+ * no track in that direction. It is immaterial which is back and
+ * which fwd. *req_io should be 0 on entry, or the results of
+ * movpos_reservechange; on successful exit it is non-0. req_io may
+ * be 0. Updates seg->movposcomb and seg->points_moving. Returns
+ * EC_Invalid if there is no movposcomb of move matching back/fwd,
+ * or EC_MovPos if the maxdelay_ms could not be met; on error any
+ * reservation is destroyed and *req_io is undefined. */
+
+void movpos_cancelchange(MovPosChangeRequest /* 0 OK */);
+ /* Cancels any movements not yet done; may leave movposcomb in an
+ * unknown or invalid physical state. For a reservation, just
+ * discards it without doing anything. */
+
+ErrorCode
+movpos_reservechange(Segment *move, int maxdelay_ms,
+ MovPosChangeRequest *res_r);
+ /* Returns EC_Points if the maxdelay_ms could not be met. The resulting
+ * MovPosChangeRequest is a reservation which is guaranteed to be
+ * useable successfully later for any movpos_requestchange for the
+ * same move and a greater or equal maxdelay_ms. On successful
+ * exit *res_r is set to non-0. */
+
/*========== speedmgr.c ==========*/
void speedmanager_speedchange_request(Train *tra, long speed);