const KindInfo *ki; /* ARCDE indep after allocate() */
Segment *move; /* ARCDE indep after allocate() */
MovPosComb actual; /* CD see below */
+ MovPosComb intent; /* RCD indep after allocate() */
/* kind-specific data follows */ /* varies kind-specific code, varies */
} Change;
/* `actual' contains the kind's public opinion about the physical
* it back if it doesn't work.
*/
+ if (n_motions >= r->n_motions)
+ return EC_MovFeatReservationInapplicable;
assert(n_motions <= r->n_motions);
newdeadline= pt_maxdelay_reldeadline(maxdelay_ms) + pt_cslot;
allow_failure= newdeadline < r->deadline;
};
static Change *mp_allocate(const KindInfo *ki, Segment *move,
- int alloc_motions) {
+ int alloc_motions, MovPosComb target) {
assert(sta_state >= Sta_Resolving);
Change *chg= ki->allocate(alloc_motions);
chg->ki= ki;
chg->move= move;
+ chg->intent= target;
return chg;
}
-static int movpos__evaluate_target(Segment *move, MovPosComb target) {
- /* returns number of features which have to change to reach target */
+static int change_needed(const MovFeatInfo *feati, MovPosComb target,
+ MovPosComb startpoint) {
+ return
+ startpoint<0 ||
+ (target - startpoint) / feati->weight % feati->posns;
+}
+
+static int evaluate_target(Segment *move, MovPosComb target,
+ MovPosComb startpoint, MovFeatKind *kind_r) {
+ /* returns number of features which have to change to reach target,
+ * or -1 for mixed kinds. kind_r may be 0. */
const SegmentInfo *movei= move->i;
int feat, tchanges;
- MovPosComb actual;
const MovFeatInfo *feati;
+ MovFeatKind kind;
- actual= movpos_poscomb_actual(move);
+ if (startpoint<0) startpoint= movpos_poscomb_actual(move);
- for (feat=0, feati=movei->movfeats, tchanges=0;
+ for (feat=0, feati=movei->movfeats, tchanges=0, kind= mfk_none;;
feat<movei->n_movfeats;
- feat++)
- if (actual<0 || (target - actual) / feati->weight % feati->posns)
- tchanges++;
+ feat++, feati++) {
+ if (!change_needed(feati,target,startpoint)) continue;
+ tchanges++;
+ if (kind && feati->kind != kind) return -1;
+ kind= feati->kind;
+ }
+
+ if (kind_r) *kind_r= kind;
return tchanges;
}
-ErrorCode
-movpos_change_bysegs(Segment *back, Segment *move, Segment *fwd,
- int maxdelay_ms, MovPosChange *chg) {
+ErrorCode movpos_findcomb_bysegs(Segment *back, Segment *move, Segment *fwd,
+ MovPosComb startpoint, MovPosComb *chosen_r) {
const SegmentInfo *movei= move->i;
- MovPosComb tcomb, bestcomb=0;
+ MovPosComb tcomb, bestcomb=-1;
int tchanges, bestchanges=INT_MAX;
const SegPosCombInfo *pci;
- //fprintf(stderr,"moving %s\n",move->i->pname);
for (tcomb=0, pci=movei->poscombs;
tcomb<movei->n_poscombs;
tcomb++, pci++) {
- //fprintf(stderr," tcomb %lu\n",tcomb);
Segment *tback= &segments[pci->backwards.next];
Segment *tfwd= &segments[pci->forwards .next];
- //fprintf(stderr," tback %s tfwd %s\n",
- // tback?tback->i->pname:"-",
- // tfwd?tfwd->i->pname:"-"
- // );
if (back && !(back==tback || back==tfwd)) continue;
if (fwd && !(fwd ==tback || fwd ==tfwd)) continue;
if (movei->n_movfeats>1) {
//fprintf(stderr," several feats\n");
/* we have to search for the one which is least effort, then */
- tchanges= movpos__evaluate_target(move, tcomb);
+ tchanges= evaluate_target(move,tcomb,startpoint,0);
if (tchanges >= bestchanges) /* prefer low-numbered movposcombs */
continue;
+ if (tchanges==-1) {
+ tchanges= INT_MAX-1;
+ /* fall through and update */
+ }
} else {
tchanges= 1;
}
- //fprintf(stderr," best %lu changes %d\n",tcomb,tchanges);
bestcomb= tcomb;
bestchanges= tchanges;
}
-
- //fprintf(stderr," best %lu changes %d\n",bestcomb,bestchanges);
- if (bestchanges==INT_MAX) { movpos_unreserve(chg); return EC_Invalid; }
-
- return movpos_change(move, tcomb, maxdelay_ms, chg);
+ if (*chosen_r) *chosen_r= bestcomb;
+ return
+ tchanges==INT_MAX ? EC_Invalid :
+ tchanges==INT_MAX-1 ? EC_MovFeatKindsCombination :
+ 0;
}
ErrorCode movpos_change(Segment *move, MovPosComb target,
ErrorCode ec;
MovFeatKind kind= mfk_none;
- if (move->moving) {
- kind= move->moving->ki - methodinfos;
- actual= move->moving->actual;
- } else {
+ if (!move->moving) {
actual= move->movposcomb;
+ assert(!move->motion);
+ } else {
+ kind= move->motion->ki - methodinfos;
+ actual= move->motion->actual;
}
{
feat++, feati++) {
fprintf(stderr," checking %s w=%lu posns=%d\n",
feati->pname,feati->weight,(int)feati->posns);
- if (actual>=0 && !((target - actual) / feati->weight % feati->posns))
+ if (!change_needed(feati,actual,target))
continue;
MovPosComb posn= target / feati->weight % feati->posns;
fprintf(stderr," motion %s %lu kind=%d\n",feati->pname,posn,kind);
const KindInfo *ki= &methodinfos[kind];
if (chg) {
- assert(move == chg->move);
+ if (chg->ki != ki ||
+ chg->move != move ||
+ chg->intent != intent)
+ return EC_MovFeatReservationInapplicable;
} else {
- chg= mp_allocate(ki,move,n_motions);
+ chg= mp_allocate(ki,move,n_motions,target);
}
chg->actual= actual;
}
ErrorCode
-movpos_reserve(Segment *move, int maxdelay_ms, MovPosChange **res_r) {
+movpos_reserve(Segment *move, int maxdelay_ms, MovPosChange **res_r,
+ MovPosComb target, MovPosComb startpoint /*as for findcomb*/) {
MovFeatKind kind= mfk_none;
const MovFeatInfo *feati;
ErrorCode ec;
- int feat;
+ int feat, nchanges;
- for (feat=0, feati=move->i->movfeats;
- feat<move->i->n_movfeats;
- feat++, feati++)
- if (kind) {
- if (feati->kind != kind) return EC_MovFeatKindsCombination;
- kind= feati->kind;
- }
+ nchanges= evaluate_target(move,target,startpoint,&kind);
+ if (nchanges=-1) return EC_MovFeatKindsCombination;
const KindInfo *ki= &methodinfos[kind];
- Change *chg= mp_allocate(ki, move, move->i->n_movfeats);
+ Change *chg= mp_allocate(ki, move, move->i->n_movfeats, target);
ec= ki->reserve(chg, move, maxdelay_ms);
if (ec) goto x;
MovPosComb movpos_poscomb_actual(Segment *seg) {
return seg->moving ? seg->moving->actual : seg->movposcomb;
}
+
+MovPosComb movpos_change_intent(MovPosChange *chg) {
+ return chg->intent;
+}
* If we _have_ claimed it then it had better be predicted to be
* vacant by the time we get there (pred_present clear, see below).
*
- * If it is currently moving then that must be OK because we checked
- * it before (except that if we're trying to increase our speed that
- * may not be OK and we must reject the speed increase because we
- * can't easily check whether the movement would happen in time).
+ * Regarding moveable features, we continuously develop our plan,
+ * as represented in route_plan and route_reservation.
*
- * If it is not currently moving and is set against us, then we need
- * to arrange that we set it appropriately: if we've just claimed the
- * segment then we can ask movfeat to set it for us (using any
- * reservation which we may have stashed away) (and fail if it
- * declines); otherwise we ask movfeat to give us a reservation valid
- * for the time interval between the predicted vacation and present
- * times.
+ * If the feature is currently moving and owned by us then that must
+ * be OK because we checked it before (except that if we're trying to
+ * increase our speed that may not be OK and we must replan the speed
+ * increase to check whether the movement will happen in time).
+ *
+ * If it is not currently moving and is set against us, then we plan
+ * to set it. If there is already a plan (ie, a reservation) and
+ * we're not accelerating then that's fine. Otherwise we make one: we
+ * determine the desired movposcomb make a reservation to move it in
+ * time. (If the segment is already owned by us that means a
+ * reservation to move it between when the tail leaves and the nose
+ * enters.)
*
* When predicted foredetect enters a new segment we add the new
* segment to the tally of segments of the particular kind and
*
* And, when predicted foredetect enters a new segment we advance the
* predicted tail (as described above). We set pred_vacated and
- * until_here, and remove the departing segments from the kind and
- * train-relative orientation tallies.
+ * until, and remove the departing segments from tallies.
*
* If all of this works then pred_present and pred_vacated constitute
- * the new ownership.
+ * the new ownership and motion constitutes the new plan. We commit
+ * to it by rewriting owner and det_* and actually requesting any
+ * movfeat changes.
*
* If not then we issue an signal stop command (we'd better not be
- * doing that already!) or countermand the speed increase or whatever */
+ * doing that already!) or countermand the speed increase or whatever.
+ */
/*
* Here is how we use the stuff in the Segment:
*
* movposcomb in an owned segment, if moving, means that
* & we have a promise that the feature
* moving will be OK by the time we get there
- * at our current speed
- *
- * move_reservation 0 for unowned segments (although during prediction
- * may be non-0 for a bit, in which case
- * we garbage collect it in a bit)
- *
- * for an owned segment is our reservation which
+ * & at our current speed
+ * motion if not moving then motion is our reservation which
* allows us to know that we are going to
* be able to change this segment between
* the tail leaving it and the head arriving
+ * in each case movpos_change_intent tells us the plan
+ *
+ * during prediction, motion can be non-0 for
+ * unowned segments as we make our plan.
+ * If we succeed we will end up
+ * 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
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);
+
typedef struct {
unsigned
accelerating:1;
TrackAdvanceContext nosec, tailc, fdetc;
TimeInterval elapsed;
Distance was_distance;
+
+ PredictionProblemCallback *problem_callback;
+ void *problem_callback_u;
/* tally counts segments which have been entered in fdet_nextseg but
* not left in tail_nextseg (ie not the same ones as pred_present),
int noninv_tally[2];
} PredictUserContext;
+static ErrorCode predict_vproblem(PredicUserContext *u, TrackSegment *seg,
+ const char *fmt, va_list al) {
+ u->problem_callback(u->train,seg,fmt,al);
+ return EC_ProblemPredicted;
+}
+static ErrorCode predict_problem(PredicUserContext *u, TrackSegment *seg,
+ const char *fmt, ...) {
+ ErrorCode ec;
+ va_list al;
+ va_start(al,fmt);
+ ec= predict_vproblem(u,seg,fmt,al);
+ va_end(al);
+ return ec;
+}
+
static int initpresent_nextseg(TrackLocation *t, TrackAdvanceContext *c,
MovPosComb *mpc_io, Segment *before) {
PredictUserContext *u= c->u;
static int nose_nextseg(TrackLocation *t, TrackAdvanceContext *c,
MovPosComb *mpc_io, Segment *before) {
PredictUserContext *u= c->u;
+ MovPosComb route_plan;
+ MovPosChange *route_change;
/* Is it empty ? */
+ if (u->train->sigstopping && t->seg->owner != u->train)
+ return EC_SignalStopHorizonReached;
+
if (t->seg->owner) {
if (t->seg->owner != u->train)
- return safety_problem(u->train, t->seg, "impeded by %s",
- t->seg->owner->pname);
+ return predict_problem(u, t->seg, "impeded by %s",
+ t->seg->owner->pname);
}
if (t->seg->pred_present)
- return safety_problem(u->train, t->seg, "will collide with itself!");
+ return predict_problem(u, t->seg, "will collide with itself!");
- /* Is it set for us ? */
+ /* What about the route ? */
if (t->seg->i->n_poscombs==1)
goto movement_ok;
if (t->seg->moving) {
if (!t->seg->owner)
- return safety_problem(u->train, t->seg, "route not yet set");
-no good if we are trying to accelerate - may get there too soon
- goto movement_ok;
+ return predict_problem(u, t->seg, "route not yet set");
+ if (!u->accelerating) {
+ *mpc_io= movpos_change_intent(t->seg->motion);
+ goto movement_ok;
+ }
}
- /* Maybe we can set it apropriately, or plan to. */
-
- if (t->seg->pred_vacated) {
- /* We're just predicting that we'll have it vacated. */
- MovPosChange *newres;
-
- if (t->seg->move_reservation && !u->accelerating)
+ if (t->seg->motion) {
+ /* We already have a plan. */
+ route_plan= movpos_change_intent(t->seg->motion);
+ if (!u->accelerating) {
+ *mpc_io= route_plan
goto movement_ok;
-
-need to calculate new intended movposcomb and remember it
-so we get right answer next time
- ec= movpos_reserve(t->seg, u->elapsed - t->seg->until, &newres);
- if (ec) return safety_problem(u->train, t->seg, "cannot promise to"
- " set route in time: %s", ec2str(ec));
- movpos_unreserve(t->seg->move_reservation);
- t->seg->move_reservation= newres;
+ }
} else {
- /* Hah! We can change it! */
- /* This also copes with the case where in fact it was set right
- * all along. In that case movpos's minimisation algorithm will
- * chose to implement our request without any actual motions. */
- ec= movpos_change_bysegs(before,t->seg,0, t->seg->until,
- t->seg->move_reservation);
-stash intended movposcomb in new field in Segment
-need to make us owner now as well
-ownership should last until it stops moving at least
- no actually this is still wrong
- t->seg->move_reservation= 0;
- if (ec) return safety_problem(u->train, t->seg, "cannot"
- " set route in time: %s", ec2str(ec));
+ /* Extend the plan. */
+ ec= movpos_findcomb_bysegs(before,t->seg,0,*mpc_io, &route_plan);
+ assert(!ec); /* there must be _some_ route since
+ we're moving into t->seg */
}
+ if (route_plan == *mpc_io && !t->seg->moving) {
+ /* Well, that's all fine then. */
+ movpos_unreserve(t->seg->motion); /* we don't need this */
+ t->seg->motion= 0;
+ goto movement_ok;
+ }
+ /* We have a plan but it involves some motion: */
+ ec= movpos_reserve(t->seg,
+ !t->seg->pred_vacated ? u->elapsed
+ : u->elapsed - t->seg->until,
+ &route_reservation, route_plan, *mpc_io);
+ if (ec) return predict_problem(u, t->seg, "cannot promise to"
+ " set route: %s", ec2str(ec));
+
+ movpos_unreserve(t->seg->motion);
+ t->seg->motion= route_reservation;
+ *mpc_io= route_plan;
movement_ok:
+ /* Now we definitely have a plan which sets a good route at t->seg. */
+ t->seg->tr_backwards= t->backwards;
t->seg->pred_present= 1;
t->seg->until= u->elapsed;
+ return 0; /* yay! */
+}
- adjust
-
- &
-
-
- const MovPosCombInfo *pci;
- const SegmentLinkInfo *link;
-
- r= trackloc_getlink(t,0,0,&link,-1,1);
- if (!r && link->next==before)
- goto movement_ok;
-
-
-
- pci= t->seg->i->poscombs[
- link= t->backwards ? &tryi->forwards : &tryi->backwards;
-
- MovPosComb only_ok= -1, try;
- for (try=0, tryi=t->seg->i->poscombs;
- try < t->seg->i->n_poscombs; try++, tryi++) {
- link= t->backwards ? &tryi->forwards : &tryi->backwards;
- if (link->next != before) continue;
- if (only_ok >= 0) { only_ok=-1; goto several_combs_ok; }
- only_ok= try;
- }
- assert(only_ok >= 0); /* otherwise graph is full of lies */
- several_combs_ok:
-
- if (t->seg->movposcomb < 0 || t->seg->movposcomb != only_ok) {
-
- else if (t->seg->movposcomb < 0) {
-
+static int tail_nextseg(TrackLocation *t, TrackAdvanceContext *c,
+ MovPosComb *mpc_io, Segment *leaving) {
+ PredictUserContext *u= c->u;
- return safety_problem(
+ if (t->seg->motion)
+ *mpc_io= movpos_change_intent(t->seg->motion);
- t->seg->pred_present= 1;
+ u->noninv_tally[leaving->tr_backwards]--;
+ if (t->seg->pred_vacated) return 0; /* only vacate once */
+ t->seg->pred_present= 0;
+ t->seg->pred_vacated= 1;
+ t->seg->until= u->elapsed;
- safety_problem
+ return 0;
+}
static int fdet_nextseg(TrackLocation *t, TrackAdvanceContext *c,
- Segment *next, MovPosComb *mpc_io) {
+ MovPosComb *mpc_io, Segment *before) {
PredictUserContext *u= c->u;
int advanced;
- advanced= c->u->was_distance - c->distance;
+ if (t->seg->motion)
+ *mpc_io= movpos_change_intent(t->seg->motion);
+
+ advanced= u->was_distance - c->distance;
u->nosec.distance= advanced;
r= trackloc_advance(&u->nose,&u->nosec); if (r) return r;
-tally add new segment
+ 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");
+ }
u->tailc.distance= advanced;
- r=
+ r= trackloc_advance(&u->tail,&u->tailc); assert(!r);
-void predict_and_provide(Train *tra) {
+ if (u->was_distance == INT_MAX)
+ c->distance= speedmanager_stoppingdistance(u->train);
+
+ u->was_distance= c->distance;
+ return 0;
+}
+
+static ErrorCode predict_confirm(Train *tra, int accelerate,
+ PredictionProblemCallback *ppc, void *ppcu) {
+ /* Caller must call this with different situations until it succeeds! */
PredictUserContext u;
memset(&u,0,sizeof(u));
+ u.accelerating= accelerate;
+ u.problem_callback= ppc;
+ u.problem_callback_u= ppcu;
trackloc_set_maxinto(&u.fdet, tra->foredetect,
tra->foredetect->tr_backwards);
u.nosec.distance= tra->detectable + (tra->backwards ? tra->tail : tra->head);
u.nosec.nextseg= nose_nextseg;
u.nosec.trackend= pred_trackend;
- u.nosec.getmovpos= pred_getmovpos;
+ u.nosec.getmovpos= 0;
u.nosec.u= &u;
r= trackloc_advance(&u.nose,&u.nosec);
/* predict the future */
- fdetc.distance= u.was_distance= INT_MAX;
- fdetc.nextseg= fdet_nextseg;
- fdetc.trackend= pred_trackend;
- fdetc.getmovpos= pred_getmovpos;
- fdetc.u= &u;
+ u.fdetc.distance= u.was_distance= INT_MAX;
+ u.fdetc.nextseg= fdet_nextseg;
+ 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 */
+ FOR_SEG {
+ intr= interferes(seg);
+ if (seg->owner == tra) {
+ seg->det_ignore= 0;
+ 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)
+
predict the future
fdet.seg= tra->foredetect;
backwards:1, /* train is moving backwards wrt its own front and back */
/* Speed: */
- estopping:1, /* set and cleared by speed.c */
+ sigstopping:1, /* set and cleared by speed.c */
/* Startup resolution (resolve.c): */
resolution:2;
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 */
- cm_autostop:1, /* train should stop on detection */
seg_inverted:1, /* polarity is inverted */
det_ignore:1, det_expected:1, /* safety.c */
+ moving:1, /* feature(s) have been told to change */
mark0,mark1,mark2,mark3, /* for temporary private uses */
res_detect:1; /* detection noticed here during resolution */
- TimeInterval until_here; /* ) nonnegative; */ /* ) always valid but */
- /* ) 0 if already */ /* ) meaningful iff owner */
- MovPosComb movposcomb; /* -1 means not known or moving */
- MovPosChange *moving, /* non-0 iff feature(s) have been told to change */
- *move_reservation; /* see safety.c */
+ MovPosComb movposcomb, /* -1 means not known or moving */
+ MovPosChange *motion; /* if ->moving, owned by movpos, otherwise by safety */
+ TimeInterval until; /* for use by safety.c */
const SegmentInfo *i;
};
* reservation should be 0, or the results of movpos_reservechange
* on the same move. It is always consumed, even on error.
*
- * On successful exit, tomove->moving is set non-0; from then on
- * until ->moving becomes 0, movposcomb may not reflect the real
- * physical state of the layout; instead it gives only information
- * about the target configuration. (Choreographers are allowed to
- * know implementation details and may know what movposcomb means
- * for certain segments depending on the movement requested.)
+ * One of the following must be true of tomove on entry:
+ * moving==1 motion already owned by movpos, see above
+ * motion==0 motion empty, will become owned by movpos
*
- * Returns EC_Invalid if there is no movposcomb of tomove matching
- * back/fwd, or EC_MovFeatTooLate if the maxdelay_ms could not be
- * met because of lack of ability to change points.
+ * On successful exit, tomove->moving=1 and tomove->motion!=0; from
+ * then on until ->moving becomes 0, movposcomb may not reflect the
+ * real physical state of the layout; instead it gives only
+ * information about the target configuration. (Choreographers are
+ * allowed to know implementation details and may know what
+ * movposcomb means for certain segments depending on the movement
+ * requested.)
*/
-ErrorCode
-movpos_change_bysegs(Segment *back, Segment *tomove, Segment *fwd,
- int maxdelay_ms, MovPosChange *reservation);
- /* 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.
+ErrorCode movpos_findcomb_bysegs(Segment *back, Segment *move, Segment *fwd,
+ MovPosComb startpoint, MovPosComb *chosen_r);
+ /* Looks for a movposcomb of move where the adjacent segment in one
+ * direction is back and the other fwd. 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.
+ *
+ * The returned movposcomb is the `best' one which is the one
+ * closest to the starting point (and if that is not unique, the
+ * lowest-numbered).
+ *
+ * startpoint=-1 means starting with current state. chosen_r may be 0.
*/
ErrorCode
-movpos_reserve(Segment *move, int maxdelay_ms, MovPosChange **res_r);
- /* Returns EC_MovFeatTooLate if the maxdelay_ms could not be met.
- * The resulting MovPosChange is a reservation which is guaranteed
- * to be useable successfully later for any movpos_change for
- * the same move and a greater or equal maxdelay_ms. On successful
- * exit *res_r is set to non-0. On transition out of Sta_Run,
- * all unconfirmed reservations must be unreserved before
- * points_all_abandon is called (see startup.c);
+movpos_reserve(Segment *move, int maxdelay_ms, MovPosChange **res_r,
+ MovPosComb target, MovPosComb startpoint /*as for findcomb*/);
+ /* The resulting MovPosChange is a reservation which is guaranteed
+ * to be useable successfully later for any movpos_change for the
+ * same move, target and startpoing and a greater or equal
+ * maxdelay_ms. On successful exit *res_r is set to non-0.
+ *
+ * On transition out of Sta_Run, all unconfirmed reservations must
+ * be unreserved before points_all_abandon is called (see
+ * startup.c);
*/
void movpos_unreserve(MovPosChange *reservation /* 0 ok */);
* -1 means not known or cannot be represented as a MovPosComb
*/
+MovPosComb movpos_change_intent(MovPosChange *chg);
+ /* Returns the target value supplied to _reserve or _change */
+
+ /* Error returns from movpos calls
+ *
+ * EC_Invalid there is no movposcomb of tomove matching back/fwd
+ * EC_MovFeatTooLate maxdelay_ms could not be met
+ * EC_MovFeatReservationInapplicable
+ * reservation not compatible with change request
+ * and/or current situation
+ */
+
/*========== speedmgr.c ==========*/
void speedmanager_speedchange_request(Train *tra, long speed);