#define pred_vacated mark2
#define will_polarise mark3
-typedef void PredictionProblemCallback(void *pu, Train *tra, TrackSegment *seg,
- const char *fmt, va_list al);
+typedef void PredictionProblemCallback(Train *tra, TrackSegment *seg,
+ void *pu, const char *message);
typedef struct {
unsigned
accelerating:1,
walk_compute_polarise:1, /* nose_nextseg still needs to worry */
need_polarise:1, /* when we commit */
- train_polarity_inverted:1, /* right now */
+ train_polarity_inverted:1, /* right now, or if know_best, the best */
know_best_polarity; /* longest-lasting into the future */
TrackLocation nose, fdet, tail;
TrackAdvanceContext nosec, tailc, fdetc;
static ErrorCode predict_vproblem(PredicUserContext *u, TrackSegment *seg,
const char *fmt, va_list al) {
- u->problem_callback(u->train,seg,fmt,al);
+ int l;
+ char *message;
+
+ l= vasprintf(&message, fmt, al); if (l <= 0) diem();
+
+ if (!u->problem_callback)
+ safety_panic(u->train, seg, "unexpected problem predicted",
+ " (context: %s): %s", u->problem_callback_u, message);
+ else
+ u->problem_callback(u->train, seg, u->problem_callback_u, message);
+
return EC_ProblemPredicted;
}
static ErrorCode predict_problem(PredicUserContext *u, TrackSegment *seg,
interferer->i->pname);
}
- /* Check polarity */
-
- if (u->walk_compute_polarise) {
- if (!u->need_polarise) {
- /* this is the very next segment as we only do this once: */
- u->walk_compute_polarise= u->need_polarise=
- (t->seg->seg_inverted ^ t->seg->tr_backwards ^
- u->train_polarity_inverted);
- }
- if (t->seg->will_polarise)
- /* this is our 2nd visit, stop now */
- u->walk_compute_polarise= 0;
-
- if (u->walk_compute_polarise)
- seg->will_polarise= 1;
-
- if (!u->know_best_polarity && !t->seg->i->invertible) {
- u->know_best_polarity= 1;
- u->
-
-
- if (
-
- int train_inverted_here=
-
- }
-
/* What about the route ? */
if (t->seg->i->n_poscombs==1)
* actually use it ourselves and trackloc_advance will call
* getmovpos. */
+ /* Check polarity */
+
if (!t->seg->invertible) {
u->noninv_tally[t->backwards]++;
if (u->noninv_tally[0] && u->noninv_tally[1])
return predict_problem(u, t->seg, "cannot set track polarity");
}
+ if (u->was_distance == INT_MAX) {
+ t->seg->det_expected= 1;
+
+ u->walk_compute_polarise= u->need_polarise=
+ (t->seg->seg_inverted ^ t->seg->tr_backwards ^
+ u->train_polarity_inverted);
+ }
+
+ if (u->walk_compute_polarise) {
+ if (t->seg->will_polarise)
+ /* this is our 2nd visit, stop now */
+ u->walk_compute_polarise= 0;
+ }
+ if (u->walk_compute_polarise) {
+ if (!u->know_best_polarity) {
+ if (u->noninv_tally[0]) {
+ u->train_polarity_inverted= 0;
+ u->know_best_polarity= 1;
+ } else if (u->noninv_tally[1]) {
+ u->train_polarity_inverted= 1;
+ u->know_best_polarity= 1;
+ }
+ }
+ if (u->know_best_polarity &&
+ !t->seg->i->invertible
+ u->noninv_tally[ t->backwards ^ u->train_polarity_inverted ]) {
+ /* incompatible, stop now */
+ u->walk_compute_polarise= 0;
+ }
+ }
+ if (u->walk_compute_polarise) {
+ seg->will_polarise= 1;
+ }
+
+ /* Advance the tail */
+
u->tailc.distance= advanced;
r= trackloc_advance(&u->tail,&u->tailc); assert(!r);
+ /* Final adjustments, prepare for next iteration */
+
if (u->was_distance == INT_MAX)
c->distance= speedmanager_stoppingdistance(u->train);
/* Caller must call this with different situations until it succeeds! */
PredictUserContext u;
Segment *foredet;
+ SEG_IV;
FOR_SEG {
seg->now_preset= seg->pred_present=
memset(&u,0,sizeof(u));
u.accelerating= accelerate;
u.walk_will_polarise= 1;
- u.need_polarise= 0;
u.train_polarity_inverted= foredet->seg_inverted ^ foredet->tr_backwards;
u.problem_callback= ppc;
u.problem_callback_u= ppcu;
u.count_time= 0;
r= trackloc_advance(&u.nose,&u.nosec);
- if (r) return r;
+ if (r) goto xproblem;
/* predict the future */
u.tailc.nextseg= tail_nextseg;
r= trackloc_advance(&fdet,&fdetc);
- if (r) return r;
+ if (r) goto xproblem;
/*----- commit to the plan -----*/
r= trackloc_advance(&fdet,&fdetc);
+ if (u.need_polarise)
+ actual_inversions_start();
+
FOR_SEG {
if (seg->owner == tra) {
seg->det_ignore= seg->det_expected= 0;
ec= movpos_change(seg, target, -1, reservation);
assert(!ec);
}
-
- movpos_unreserve(seg->motion);
- seg->motion= 0;
- } else if (seg->pred_vacated
-
- }
-}
-
-predict the future
- fdet.seg= foredet;
-
- fdet.into= 0;
-
- tail.seg= foredet;
- 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)
- safety_panic(0,seg, "unexpected detection");
-
- Train *tra= seg->owner;
-
- update_head(tra,seg);
- predict_and_provide(tra);
-
-
-
- /* ============ OLD CODE ========== */
-
-static void seg_clear_stale(Segment *seg) {
- seg->owner= 0;
- seg->until_here= seg->until_detect= 0;
-}
-
-typedef struct {
- /* constant inputs */
- Train *tra;
- /* modified in place by lay_train_pass: */
- ErrorCode ec;
- int invert_count[2]; /* count of (switchable) segments,
- * invert_count[0]: inverted from train's pov
- * iff train is backwards (ie, train not inverted)
- * invert_count[1]: train is inverted
- * set to -1 if any unswitchable is the other way */
- Segment *invert_forcer; /* the unswitchable which forces */
-} LayTrainState;
-
-static void lay_train_check_clash(LayTrainState *l, Segment *check,
- Segment *report) {
- const char *exi1="", *exi2="";
- if (check!=report) { exi1= " at "; exi2= check->i->pname; }
-
- if (check->tr_updated) {
- l->ec= safety_problem(l->tra, report, "self-collision%s%s", exi1,exi2);
- return;
- }
- if (check->owner) {
- if (check->owner != l->tra) {
- l->ec= safety_problem(l->tra, report, "collision with %s"
- "%s%s", check->owner->pname, exi1,exi2);
- return;
+ if (u.need_polarise) {
+ if (seg->will_polarise)
+ seg->seg_inverted= seg->tr_backwards ^ u.train_polarity_inverted;
+ actual_inversions_segment(seg);
}
+ seg->now_present= seg->pred_present=
+ seg->pred_vacated= seg->will_polarise= 0;
}
-}
-
-static void lay_train_pass(LayTrainState *l,
- TrackLocation tloc, long advance,
- long speed, unsigned backwards,
- unsigned check_clash) {
- Segment *seg, *inter;
- long overall, remain, dist_until, time_until;
- int *invert_likehere, train_inverted_here;
- Train *tra= l->tra;
- const SegPosCombInfo *pci;
- const SegmentLinkInfo *link;
-
- if (l->ec) return;
-
- seg= tra->foredetect;
-
- remain= overall= advance + speed * SPEED_CLEAR_MULT;
-
-fprintf(stderr,"lay_train_pass %s @%s+%ld check_clash=%d"
- " advance=%ld speed=%ld remain=%ld backwards=%u\n",
- tra->pname, seg->i->pname, tloc.into,
- check_clash, advance,speed,remain,
- backwards);
-
- for (;;) {
- seg= tloc.seg;
-
-fprintf(stderr,"lay_train_pass loop @%s remain=%ld\n",
- seg->i->pname, remain);
-
- if (check_clash) {
- lay_train_check_clash(l,seg,seg);
- inter= interferes(seg);
- if (inter) lay_train_check_clash(l,inter,seg);
- }
-
- seg->owner= l->tra;
- seg->tr_backwards= tloc.backwards ^ backwards;
- seg->tr_updated= 1;
-
- if (backwards)
- seg->until_detect= 0;
- train_inverted_here= seg->seg_inverted ^ seg->tr_backwards;
- invert_likehere= &l->invert_count[train_inverted_here];
+ if (u.need_polarise)
+ actual_inversions_done();
- if (seg->i->invertible) {
- if (*invert_likehere >= 0)
- (*invert_likehere)++;
- } else {
- if (*invert_likehere < 0) {
- l->ec= safety_problem(l->tra,0, "train requires"
- " noninvertible segments with opposite polarity:"
- " @%s, @%s", seg->i->pname,
- l->invert_forcer->i->pname);
- return;
- }
- assert(!seg->seg_inverted);
- (*invert_likehere)++;
- l->invert_count[!train_inverted_here]= -1;
- l->invert_forcer= seg;
- }
-
- dist_until= (overall - remain) - advance;
- time_until= !speed ? 0 : SPEED_CALC_TIME(speed, dist_until, DOWN);
- *(check_clash ? &seg->until_here : &seg->until_detect)= time_until;
-
- if (!remain) break;
- int r= trackloc_further(&tloc, &remain);
-
-fprintf(stderr,"lay_train_pass further=%d tloc=@%s+%ld remain=%ld\n",
- r, tloc.seg->i->pname, tloc.into, remain);
-
- switch (r) {
- case -2:
- l->ec= safety_problem(l->tra, seg, "end of track");
- return;
- case -1:
- l->ec= safety_problem(l->tra, tloc.err, "track route not set");
- return;
- case +1:
- pci= trackloc_segposcomb(&tloc);
- link= trackloc_segmentlink(&tloc,pci,0);
- if (link->next+segments != seg) {
- l->ec= safety_problem(l->tra, link->next+segments,
- "track route set against us");
- return;
- }
- break;
- case 0:
- return;
- default:
- abort();
- }
- }
-}
-
-static void lay_train_inversions(LayTrainState *l) {
- SEG_IV;
- int train_be_inverted, seg_be_inverted;
-
- if (l->ec) return;
-
- train_be_inverted= l->invert_count[1] > l->invert_count[0];
- assert(l->invert_count[train_be_inverted] >= 0);
-
- actual_inversions_start();
+ return 0;
+ xproblem:
FOR_SEG {
- if (!seg->tr_updated) continue;
- assert(seg->owner == l->tra);
- seg_be_inverted= train_be_inverted ^ seg->tr_backwards;
- assert(!(seg_be_inverted && !segi->invertible));
- if (!segi->invertible)
- continue;
- seg->seg_inverted= seg_be_inverted;
- actual_inversions_segment(seg);
- }
- actual_inversions_done();
-}
-
-static void lay_train_done(LayTrainState *l) {
- SEG_IV;
+ seg->now_present= seg->pred_present=
+ seg->pred_vacated= seg->will_polarise= 0;
- FOR_SEG {
- if (seg->owner == l->tra) {
- if (!seg->tr_updated) seg_clear_stale(seg);
- seg->tr_updated= 0;
+ if (!seg->owner && !seg->moving && seg->motion) {
+ movpos_unreserve(seg->motion);
+ seg->motion= 0;
}
- assert(!seg->tr_updated);
- assert(seg->until_detect >= seg->until_here);
}
}
-static ErrorCode lay_train(Train *tra, long added_slop) {
- Segment *seg;
- TrackLocation tloc;
- long head, headslop, tail, taildet;
- LayTrainState l;
-
- seg= tra->foredetect;
-
- tloc.seg= seg;
- tloc.into= tra->maxinto;
- tloc.backwards= seg->tr_backwards;
-
-fprintf(stderr,"lay_train %s @%s added_slop=%ld maxinto=%ld "
- "tra->backwards=%d tloc.backwards=%d\n",
- seg->i->pname, tra->pname, added_slop, (long)tra->maxinto,
- tra->backwards, tloc.backwards);
-
- l.tra= tra;
- l.ec= 0;
- l.invert_count[0]= l.invert_count[1]= 0;
-
- head= tra->backwards ? tra->tail : tra->head;
- headslop= head + added_slop;
-
- /* 1st pass:
- * update actual train location (train's head and future locations).
- *
- * 2nd pass:
- * update detection (future locations) only.
- *
- * 3rd pass:
- * train location (current location and tail)
- *
- * Clash checking is done in passes 1 and 3, including checking
- * whether the train clashes with itself (ie, fail if we find
- * segment with same train and tr_updated set).
- */
- lay_train_pass(&l, tloc, headslop, tra->speed, 0, 1);
- lay_train_pass(&l, tloc, 0, tra->speed, 0, 0);
-
- trackloc_reverse(&tloc);
- seg->tr_updated= 0; /* we're about to do this one again */
- tail= tra->backwards ? tra->head : tra->tail;
- taildet= tra->detectable + tail + tra->maxinto;
-
- lay_train_pass(&l, tloc, taildet, 0, 1, 1);
+/*========== entrypoints from rest of the program ==========*/
- lay_train_inversions(&l);
- lay_train_done(&l);
-
- return l.ec;
+static void detection_report_problem(Train *tra, TrackSegment *seg,
+ void *pu, const char *message) {
+ logmsg("SignallingProblemPredicted",tra,seg->i,
+ "re detection @%s: %s", tra->foredetect->i->pname, message);
}
void safety_notify_detection(Segment *seg) {
- Train *tra= seg->owner;
- ErrorCode ec;
- TrackLocation tloc;
-
- if (!seg->until_detect)
- return;
-
- tloc.seg= seg;
- tloc.into= 0;
- tloc.backwards= seg->tr_backwards;
+ Train *tra;
+
+ if (seg->det_ignore) return;
+ if (!seg->det_expected)
+ safety_panic(0,seg, "unexpected detection");
+ tra= seg->owner;
tra->foredetect= seg;
- tra->maxinto= trackloc_remaininseg(&tloc);
-
- if (seg->cm_autostop) {
- seg->cm_autostop= 0;
- if (!tra->estopping) {
- speedmanager_autostop(tra);
- if (!tra->speed && tra->maxinto > AUTOSTOP_UNCERTAINTY)
- /* At some point we may need to allow more slop when the
- * next segment is points than when this is the last segment
- * (ie, just buffers next). */
- tra->maxinto= AUTOSTOP_UNCERTAINTY;
- }
- }
- tra->uncertainty= tra->maxinto;
-
- if (!tra->estopping) {
- ec= lay_train(tra, 0);
- if (ec) {
- logmsg(ec,tra,seg->i, "emergency stop on reaching here");
- safety_emergencystop(tra);
- }
- }
-}
-
-void safety_emergencystop(Train *tra) {
- ErrorCode ec;
+ if (tra->foredetect->movposcomb < 0)
+ safety_panic(tra,seg, "track route not set and train has arrived");
- if (tra->estopping) return;
+ ec= predict_confirm(tra, 0, detection_report_problem, 0);
+ if (!ec) return;
+
+ assert(ec == EC_SignallingProblemPredicted);
- ec= lay_train(tra, ESTOP_UNCERTAINTY);
- if (ec) safety_panic(tra,0, "emergency stop forbidden!");
- speedmanager_emergencystop(tra);
+ tra->sigstopping= 1;
+ predict_confirm(tra, 0, 0,"detection");
+ assert(!ec);
}
ErrorCode safety_checkmovposchange(Segment *seg) {
ErrorCode ec;
if (seg->owner) {
- ec= safety_problem(seg->owner, seg, "must not unset route train is using");
- logmsg(ec,seg->owner,seg->i,"countermanded point change");
- return ec;
+ oprintf(UPO,"ack SignallingProblemPredicted @%s %s:"
+ " route set for approaching train",
+ seg->i->pname, seg->owner->pname);
+ return EC_BadCmd;
}
return 0;
}
+ /* ============ OLD CODE ========== */
+
ErrorCode safety_requestspeed(Train *tra, long newspeed) {
long oldspeed;
ErrorCode ec;