* At each new track location for our front end we hope to find a
* segment which no-one currently owns and which is currently set for
* us. (We check this before updating our tail trackloc as below.)
- * In that case we update the segment's until_here.
+ * In that case we update the segment's until.) (We use the value
+ * `elapsed' which is the minimum time until the current nose
+ * prediction; if we've only just detected the train at foredetect
+ * we can count the time for the
*
* However it may be a segment which we currently own, or perhaps it
* may be set against us. (If it's owned by anyone else or is not
* owning the segment, and if we fail
* we garbage collect it
*
- * det_ignore we expect interference detection here due to a
- * crossing (unowned segment), or
- * this is part of the body of a train which we've
- * already detected here (owned segment)
+ * det_ignore this is part of the body of a train which we've
+ * already detected here
*
* det_expected this is the next place we expect owner train
* to arrive
*
* iff either of these is set, this train is going
* to own the segment when we're finished
+ *
+ * until time until the we will arrive (pred_present), or
+ * depart (pred_vacated), valid only if
+ * one of those is set
+ * value is as for u->elapsed
*/
+/*========== prediction machinery ==========*/
+
#define pred_present mark0
#define pred_vacated mark1
-void update_head(Train *tra, Segment *seg) {
- tra->foredetect= seg;
- if (tra->foredetect->movposcomb < 0)
- safety_panic(tra,seg, "track route not set and train has arrived");
-}
-
typedef void PredictionProblemCallback(void *pu, Train *tra, TrackSegment *seg,
const char *fmt, va_list al);
accelerating:1;
TrackLocation nose, fdet, tail;
TrackAdvanceContext nosec, tailc, fdetc;
- TimeInterval elapsed;
+ TimeInterval elapsed; /* from now, minimum */
Distance was_distance;
PredictionProblemCallback *problem_callback;
int noninv_tally[2];
} PredictUserContext;
+/*---------- prediction problem reporting ----------*/
+
static ErrorCode predict_vproblem(PredicUserContext *u, TrackSegment *seg,
const char *fmt, va_list al) {
u->problem_callback(u->train,seg,fmt,al);
return ec;
}
+/*---------- prediction trivial callbacks ----------*/
+
+static int pred_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
+ MovPosComb *use_io) {
+ PredictUserContext *u= c->u;
+ if (t->seg->motion) *use_io= movpos_change_intent(t->seg->motion);
+ if (*use_io<0) return panic(tra,seg, "track route unexpectedly not known");
+}
+
+static int pred_trackend(TrackLocation *t, TrackAdvanceContext *c) {
+ return predict_problem(t,c,"end of track");
+}
+
+static int pred_trackend_panic(TrackLocation *t, TrackAdvanceContext *c) {
+ PredictUserContext *u= c->u;
+ return safety_panic(u->train,t->seg,"unexpected end of track");
+}
+
static int initpresent_nextseg(TrackLocation *t, TrackAdvanceContext *c,
MovPosComb *mpc_io, Segment *before) {
PredictUserContext *u= c->u;
next->pred_present= 1;
+ next->until= 0;
adjust_tally(t,c,1);
}
+/*---------- prediction nose advancement ----------*/
+
static int nose_nextseg(TrackLocation *t, TrackAdvanceContext *c,
MovPosComb *mpc_io, Segment *before) {
PredictUserContext *u= c->u;
/* Is it empty ? */
if (u->train->sigstopping && t->seg->owner != u->train)
- return EC_SignalStopHorizonReached;
+ return EC_SignalStopHorizonReached;
if (t->seg->owner) {
if (t->seg->owner != u->train)
if (t->seg->pred_present)
return predict_problem(u, t->seg, "will collide with itself!");
+ interferer= interferes(c,t->seg);
+ if (interferer) {
+ if (interferer->owner && interferer->owner != u->train)
+ return predict_problem(u, t->seg, "impeded by %s @%s",
+ interferer->owner->pname, interferer->i->pname);
+ if (interferer->pred_present)
+ return predict_problem(u, t->seg, "will collide with itself @%s!",
+ interferer->i->pname);
+ }
+
/* What about the route ? */
if (t->seg->i->n_poscombs==1)
return 0; /* yay! */
}
+/*---------- prediction tail advancement ----------*/
+
static int tail_nextseg(TrackLocation *t, TrackAdvanceContext *c,
MovPosComb *mpc_io, Segment *leaving) {
PredictUserContext *u= c->u;
- if (t->seg->motion)
- *mpc_io= movpos_change_intent(t->seg->motion);
-
u->noninv_tally[leaving->tr_backwards]--;
if (t->seg->pred_vacated) return 0; /* only vacate once */
return 0;
}
+/*---------- prediction foredetect advancement ----------*/
+
static int fdet_nextseg(TrackLocation *t, TrackAdvanceContext *c,
MovPosComb *mpc_io, Segment *before) {
PredictUserContext *u= c->u;
int advanced;
- if (t->seg->motion)
- *mpc_io= movpos_change_intent(t->seg->motion);
-
advanced= u->was_distance - c->distance;
+ if (u->count_time) {
+ u->elapsed += advanced / speedmanager_speed_maxestimate(u->train);
+ }
+
u->nosec.distance= advanced;
- r= trackloc_advance(&u->nose,&u->nosec); if (r) return r;
+ r= trackloc_advance(&u->nose,&u->nosec);
+
+ if (r==EC_SignalHorizonReached) {
+ /* stop our prediction now */
+ advanced= was_distance= t->distance= 0;
+ r= 0;
+ }
+
+ if (r) return r;
if (!t->seg->invertible) {
u->noninv_tally[t->backwards]++;
c->distance= speedmanager_stoppingdistance(u->train);
u->was_distance= c->distance;
+ u->count_time= 1;
return 0;
}
-static ErrorCode predict_confirm(Train *tra, int accelerate,
+/*---------- prediction entrypoint ----------*/
+
+static ErrorCode predict_confirm(Train *tra, int accelerate, int justdetected,
PredictionProblemCallback *ppc, void *ppcu) {
/* Caller must call this with different situations until it succeeds! */
PredictUserContext u;
+ FOR_SEG {
+ seg->pred_present= seg->pred_vacated= 0;
+ }
+
memset(&u,0,sizeof(u));
u.accelerating= accelerate;
+ u.count_time= justdetected; /* if just detected we have whole fdet segment */
u.problem_callback= ppc;
u.problem_callback_u= ppcu;
!tra->foredetect->tr_backwards);
/* find the train's tail and mark it present */
-
- u.tail.seg->pred_present= 1;
u.tailc.distance= tra->detectable + (tra->backwards ? tra->head : tra->tail);
u.tailc.nextseg= initpresent_nextseg;
- u.tailc.trackend= 0;
- u.tailc.getmovpos= 0;
+ u.tailc.getmovpos= pred_getmovpos;
+ u.tailc.trackend= pred_trackend_panic;
u.tailc.u= &u;
+ r= initpresent_nextseg(&u.tail,&u.tailc,0,0); assert(!r);
r= trackloc_advance(&u.tail,&u.tailc); assert(!r);
trackloc_reverse_exact(&u.tail,0);
u.nose= fdet;
u.nosec.distance= tra->detectable + (tra->backwards ? tra->tail : tra->head);
u.nosec.nextseg= nose_nextseg;
+ u.nosec.getmovpos= pred_getmovpos;
u.nosec.trackend= pred_trackend;
- u.nosec.getmovpos= 0;
u.nosec.u= &u;
r= trackloc_advance(&u.nose,&u.nosec);
u.fdetc.distance= u.was_distance= INT_MAX;
u.fdetc.nextseg= fdet_nextseg;
+ u.fdetc.getmovpos= pred_getmovpos;
u.fdetc.trackend= pred_trackend;
- u.fdetc.getmovpos= 0;
u.fdetc.u= &u;
u.tailc.nextseg= tail_nextseg;
r= trackloc_advance(&fdet,&fdetc);
if (r) return r;
- /* commit here */
+ /*----- commit to the plan -----*/
+
FOR_SEG {
- intr= interferes(seg);
if (seg->owner == tra) {
- seg->det_ignore= 0;
- seg->det_expect= 0;
+ seg->det_ignore= seg->det_expect= 0;
seg->owner= 0;
- if (intr) {
- assert(!intr->owner || intr->owner==tra);
- intr->det_ignore= 0;
- }
}
if (seg->pred_present || seg->pred_vacated) {
seg->owner= tra;
- if (seg->until)
+ if (!seg->until)
+ seg->det_ignore= 1;
+ }
+
predict the future
fdet.seg= tra->foredetect;
tail.into= 0;
tail.backwards=
+void update_head(Train *tra, Segment *seg) {
+ tra->foredetect= seg;
+ if (tra->foredetect->movposcomb < 0)
+ safety_panic(tra,seg, "track route not set and train has arrived");
+}
+
void safety_notify_detection(Segment *seg) {
if (seg->det_ignore) return;
if (!seg->det_expected)
Train *tra= seg->owner;
- seg_clear_marks();
update_head(tra,seg);
predict_and_provide(tra);
Segment *invert_forcer; /* the unswitchable which forces */
} LayTrainState;
-#define INTERFERING_MOVPOSCOMB(seg,segi) \
- ((seg)->moving || \
- (seg)->movposcomb < 0 || \
- (segi)->interferes_movposcomb_map & (1u << (seg)->movposcomb))
-
-static Segment *interferes(Segment *base) {
- const SegmentInfo *basei, *interi;
- SegmentNum intern;
- Segment *inter;
-
- basei= base->i;
- intern= basei->interferes;
-
- if (!SOMEP(intern)) return 0;
- if (!INTERFERING_MOVPOSCOMB(base,basei)) return 0;
-
- inter= &segments[intern];
- interi= &info_segments[intern];
-
- assert(basei == &info_segments[interi->interferes]);
- if (!INTERFERING_MOVPOSCOMB(inter,interi))
- return 0;
-
- return inter;
-}
-
static void lay_train_check_clash(LayTrainState *l, Segment *check,
Segment *report) {
const char *exi1="", *exi2="";
typedef struct { /* all set by caller, only distance modified by trackloc: */
int distance;
+ /* All event callbacks are optional; if not supplied we pretend
+ * that they were a no-op which returned 0. */
int nextseg(TrackLocation *t, TrackAdvanceContext *c,
Segment *next, MovPosComb *mpc_io);
/* On entry *mpc_io is from next->movposcomb. nextseg may modify
* it if it feels like it in which case the modified value will
- * be used instead. If on 0-return it is still -1, getmovpos is used */
+ * be used instead. If on 0-return it is still -1, getmovpos is used.
+ * t->remain is not valid on entry and should not be touched. */
+ void getmovpos(TrackLocation *t, TrackAdvanceContext *c, MovPosComb *use_io);
+ /* Will be called *use_io on entry is
+ * - at the start of trackloc_advance value from segment
+ * - after nextseg value left by nextseg
+ * - by trackloc_getlink getlink's mpc argument
+ * - by interfering_segment base->movposcomb
+ * May modify *use_io if it wishes. It is OK to leave it as
+ * -1 in which case advance will return whatever getmovpos did
+ * (even if 0). When called by interfering_segment, non-0 returns
+ * will be treated as an indication that the movposcomb is unknown. */
int trackend(TrackLocation *t, TrackAdvanceContext *c);
- int getmovpos(TrackLocation *t, TrackAdvanceContext *c, MovPosComb *use_r);
- /* May be called
- * - at the start of trackloc_advance if current segment position unknown
- * - after nextseg has failed to determine position of next segment
- * - by trackloc_getlink
- * it _must_ either return non-0 or set *use_r to a valid value
- */
+ /* trackloc_advance stops even if this returns 0. */
void *u;
} TrackAdvanceContext;
int trackloc_getlink(TrackLocation *t, TrackAdvanceContext *c,
const SegPosCombInfo **pci_r /* 0 ok */,
const SegmentLinkInfo **link_r /* 0 ok */,
- MovPosComb mpc /* -1: get from segment or getmovpos */);
- /* Of c, only c->getmovpos is used. c may be 0 in which case we
- * pretend getmovpos returned -1. */
+ MovPosComb mpc /* -1: get from segment */);
+ /* Of c, only c->getmovpos is used.
+ * c may be 0 which works just like c->getmovpos==0. */
-int trackloc_advance(TrackLocation *t, TrackAdvanceContext *c);
- /* Advances t by dist until either some callback returns non-0
- * or distance becomes 0. nextseg and getmovpos callbacks may be 0.
- * If trackend callback is 0
- */
+Segment *segment_interferes(TrackAdvanceContext *c, Segment *base);
+ /* 0 means we know there is no interference; if we can't determine
+ * the relevant movposcombs we return the interfering segment.
+ * c is used as for trackloc_getlink. */
-
-int trackloc_further(TrackLocation *tloc, long *remain_io);
- /* Advances tloc, decrementing *remain_io, until one of the
- * following happens: err set to
- * tloc->segn changes returns +1 unchanged
- * *remain_io becomes zero returns 0 unchanged
- * unknown movpos found returns -1 next segment
- * end of track reached returns -2 seg
- * current track segment unknown movpos! returns -3 seg
- * Does _not_ check that the movposcomb of the segment we
- * are moving into is correct.
- */
-
-void trackloc_reverse(TrackLocation *tloc);
- /* Reverses tloc without changing its actual location. */
-
-const SegPosCombInfo *trackloc_segposcomb(const TrackLocation *tloc);
-const SegmentLinkInfo *trackloc_segmentlink(const TrackLocation *tloc,
- const SegPosCombInfo *pci,
- unsigned far);
+int trackloc_advance(TrackLocation *t, TrackAdvanceContext *c);
+ /* Advances t by dist until distance becomes 0, some callback
+ * returns non-0, or sometimes 0 if we cannot continue and
+ * the relevant callback is missing or returned 0. */
/*========== useful macros and declarations ==========*/
int trackloc_getlink(TrackLocation *t, TrackAdvanceContext *c,
const SegPosCombInfo **pci_r,
const SegmentLinkInfo **link_r,
- MovPosComb mpc, int backend) {
+ MovPosComb mpc) {
const SegPosCombInfo *pci;
MovPosComb mpc;
- if (mpc<0) {
+ if (mpc<0)
mpc= t->movposcomb;
- }
- if (mpc<0 && c) {
- if (c) {
- if (!c->getmovpos)
- c->panic(t,c,"unexpected lack of definite track route");
-
- r= c->getmovpos(t,c, &mpc);
- if (r) return r;
- }
+
+ if (c && c->getmovpos) {
+ r= c->getmovpos(t,c,&mpc);
+ if (r) return r;
}
if (mpc<0) {
*pci_r=0;
*link_r=0;
- return -1;
+ return 0;
}
assert(mpc < seg->i->n_poscombs);
pci= &seg->i->poscombs[seg->movposcomb];
if (*pci_r) *pci_r= pci;
- if (*link_r) *link_r= (tloc->backwards ^ backend)
- ? &pci->backwards : &pci->forwards;
+ if (*link_r) *link_r= tloc->backwards ? &pci->backwards : &pci->forwards;
return 0;
}
int trackloc_advance(TrackLocation *t, TrackAdvanceContext *c) {
Segment *next, *save;
- const SegPosCombInfo *pci;
const SegmentLinkInfo *link;
MovPosComb mpc_nego;
+ int leaving_back;
- r= getsegmentlink(t,c, &pci,&link, t->seg.movposcomb);
- if (r) return r;
+ r= trackloc_getlink(t,c, 0,&link, t->seg.movposcomb);
+ if (r || !link) return r;
for (;;) {
if (!c->distance) return 0;
next= link->next;
if (!SOMEP(next)) {
- if (!c->trackend) c->panic(t,c,"unexpected end of track");
- return c->trackend(t,c);
+ if (c->trackend) return c->trackend(t,c);
+ return 0;
}
leaving= t->seg;
+ leaving_back= t->backwards;
t->seg= next;
t->backwards ^= link->next_backwards;
mpc_nego= next->movposcomb;
if (r) return r;
}
- r= getsegmentlink(t,c, &pci,&link, mpc_nego);
- if (r) { t->seg=save; return r; }
+ r= trackloc_getlink(t,c, 0,&link, mpc_nego);
+ if (r || !link) {
+ t->seg=leaving;
+ t->seg->backwards= leaving_back;
+ return r;
+ }
t->remain= link->dist;
}
}
}
+static int interfering_movposcomb(TrackAdvanceContext *c, Segment *seg) {
+ MovPosComb mpc;
+
+ mpc= t->movposcomb;
+ if (c && c->getmovpos) {
+ r= c->getmovpos(t,c,&mpc);
+ if (r) return 1;
+ }
+
+ if (mpc < 0) return 1;
+ return seg->i->interferes_movposcomb_map & (1u << mpc);
+}
+
+Segment *segment_interferes(TrackAdvanceContext *c, Segment *base) {
+ SegmentNum intern;
+ Segment *inter;
+
+ intern= base->i->interferes;
+
+ if (!SOMEP(intern)) return 0;
+ if (!interfering_movposcomb(c,base)) return 0;
+
+ inter= &segments[intern];
+
+ assert(base->i == &info_segments[inter->i->interferes]);
+ if (!interfering_movposcomb(c,inter)) return 0;
+
+ return inter;
+}
+
/* ============ OLD CODE ============= */
+void trackloc_reverse(TrackLocation *tloc);
+ /* Reverses tloc without changing its actual location. */
+
+const SegPosCombInfo *trackloc_segposcomb(const TrackLocation *tloc);
+const SegmentLinkInfo *trackloc_segmentlink(const TrackLocation *tloc,
+ const SegPosCombInfo *pci,
+ unsigned far);
+
xxx long trackloc_remaininseg(const TrackLocation *tloc) {
const SegPosCombInfo *pci;
long segment_len;