2 * resolve detected trains into initial state
9 /*---------- main resolution processing ----------*/
13 * Notation and background:
16 * D \subset S set of segments where we have detection
19 * H(t) \subset S home range for train t \elem T
20 * E(t) \subset S set of segments where train t last expected
22 * H(t) \disjoint H(t') for t != t'
23 * E(t) \disjoint E(t') for t != t'
24 * but not necessarily E(t) \disjoint H(t')
26 * We want to find a mapping R(t)
27 * R(t) \elem { N, E, H } t \elem T, N(t') = \null
29 * A(t) = (R(t))(t) segments allocated to train t
32 * A(t) \disjoint A(t') for t != t' `disjoincy'
33 * D \subset U `coverage'
34 * and the solution is somehow optimal or at least not badly suboptimal.
36 * We compute R incrementally, maintaining disjoincy,,
37 * while increasing U. At each stage R is an optimal solution
38 * to the problem posed by D' = U, and we maintain this invariant.
40 * We define an optimality ordering on R by counting occurrences of
41 * H, E, and H in the range, in that order. Ie,
42 * Count(R,x) = |{ t: R(t) = x }|
43 * Rank(R) = Count(R,H) * (|T|+1) + Count(R,E)
44 * and compare Ranks numerically, lower being better.
46 * So we mean that for any incrementally computed R, there is no
47 * R' satisfying the same subproblem D' = U for which Rank(R') < Rank(R).
49 * For the benefit of client programs, we record the key elements of
50 * the proofs that we generate for non-N values of R.
52 * 1. Initially we set \all_{t} R(t) = N.
54 * Optimality: This is minimal for U = \null, as Rank(R) = 0.
56 * 2. Then we search for lack of coverage, ie violations of D \subset U.
57 * If none are found we are done.
59 * 3. Let d \elem D but d !\elem U.
60 * We will now calculate new R which we will call R2
61 * giving new U which we will call U2.
63 * 3a. Hope that \exists{t} st d \elem E(t) with
64 * E(t) \disjoint U and R(t) = N.
65 * If so set R2(t) = E giving a solution to U2.
67 * Optimality: if d \elem D then we need U2 to include d.
68 * That means d \elem E(t) or d \elem H(t'). No E(t')
69 * other than E(t) can help since they are disjoint, and
70 * we would certainly prefer adding E(t) to adding H(t').
71 * (Adding H(t') and removing some other H(t'') doesn't
72 * work either because the Hs are disjoint, so no
73 * H can stand in for any other.)
75 * So the rank increase by 1 is is minimal for U2.
77 * Proof elements: R(t) = E is demonstrated by
78 * every d meeting these conditions.
80 * 3b. Alternatively, hope that d \elem H(t')
82 * If so set R2(t') = H
83 * \all_{t+} R2(t+) = N where R(t+) = E.
85 * Optimality: in the case we are now dealing with, we
86 * didn't succeed with the test for 3a, so either:
88 * (i) There is no t where d \elem E(t), so R(t') = H is
89 * essential because no solution without R(t') = H has d \elem U2.
91 * (ii) There is t where d \elem E(t) but R(t) = H.
92 * We have therefore already proved that R(t) cannot be E.
94 * (iii) There is t where d \elem E(t) but E(t) !\disjoint U
95 * In this case, consider a clash d' between E(t) and U
96 * d' \elem U, d' \elem E(t)
98 * This clash at d' is preventing us covering d with E(t).
99 * E's can't clash since they are disjoint so it must be
100 * a clash with some H. But we have no unavoidable H's
101 * in our solution to U, so this solution to U2 has no
102 * unavoidable H's either.
104 * Or to put it algebraically,
105 * d' != d, because d !\elem U.
106 * d' \elem A(t'') for some t'' since d' \elem U.
107 * If R(t'') = E, d' \elem E(t'') but E's are disjoint.
108 * So R(t'') = H, which must have been unavoidable by our
109 * inductive premise. Thus for U2, R2(t'') = H is also
112 * Proof elements: R(t') = H is demonstrated by
113 * every d meeting these conditions
114 * together with for each such d
115 * the alternative train t if applicable
116 * (there can be only one)
118 * every clash d' between E(t) and U
119 * plus for each such d' the corresponding t''
120 * (we record all this indexed by t so we can reuse it
123 * R2 consists only of Hs and Ns. All of the Hs are necessary
124 * for U2 by the above, so R2 is minimal for U2.
126 * The rank is increased: we increase Count(R,H) and set
129 * 3c. If neither of these is true, we are stuck and cannot
130 * satisfy d. This is an error. We remove d from D
131 * and continue anyway to see if we can find any more.
133 * Proof elements: Lack of solution is demonstrated
134 * separately by every such d together with
135 * for each d if d \elem E(t) for some t
137 * plus the clashes d'' as for 3b(iii).
139 * Termination: at each iteration 3a or 3b, we strictly increase Rank.
140 * At each iteration 3c we reduce D.
141 * Rank cannot exceed |T|*(|T|+1) and D starts out finite. So
142 * eventually we must succeed or fail.
146 /* values for Train->resolution: */
150 #define RESOLUTION_CHARS "NEH"
152 /* During the main algorithm:
153 * We record R in tra->resolution,
154 * U in segi->mark0 and D in segi->res_detect
156 #define iselem_u mark0
158 static int resmain_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
159 MovPosComb *use_io) {
161 const char *pn= d->i->pname;
163 if (d->home && d->home->resolution == RR_H) {
164 d->tr_backwards= d->ho_backwards;
165 *use_io= 0; /* a bit kludgey */
166 DPRINTF(resolving,main, "getmovpos %s H\n", pn);
167 } else if (d->owner) {
168 d->tr_backwards ^= d->owner->backwards;
169 *use_io= d->movposcomb;
170 DPRINTF(resolving,main, "getmovpos %s E %d\n", pn, *use_io);
171 } else if (!d->res_movposset) {
173 DPRINTF(resolving,main, "getmovpos %s N -1\n", pn);
174 } else if (d->motion) {
175 *use_io= movpos_change_intent(d->motion);
176 DPRINTF(resolving,main, "getmovpos %s M %u\n", pn, *use_io);
181 static MovPosComb just_getmovpos(Segment *d, TrackAdvanceContext *tac) {
187 r= resmain_getmovpos(&tloc,tac,&target); assert(!r);
191 static int resolve_complete_main(void) {
192 int problems, phase, nextphase;
195 TRAIN_ITERVARS(tplus);
197 SEGMENT_ITERVARS(d1);
198 SEGMENT_ITERVARS(dplus);
201 FOR_TRAIN(t,NOOP,NOOP) t->resolution= RR_N;
203 for (phase=0; phase<3; phase=nextphase) {
205 DPRINTF(resolving,alg, "iteration %c\n", "EHX"[phase]);
207 DPRINTF(resolving,alg, " calculate-u\n");
209 FOR_SEGMENT(d,NOOP,NOOP) { /* calculate U */
212 #define ADDTO_U_EH(homeowner,HH_HE,string) \
213 if (d->homeowner && d->homeowner->resolution == HH_HE) { \
214 DPRINTF(resolving,alg, " covered %s " string " %s\n", \
215 di->pname, d->homeowner->pname); \
218 ADDTO_U_EH(home, RR_H, "at-home");
219 if (d->movposcomb >= 0)
220 ADDTO_U_EH(owner, RR_E, "as-expected");
223 d->iselem_u= updated;
226 DPRINTF(resolving,alg, " searching\n");
227 FOR_SEGMENT(d, NOOP, NOOP) {
228 if (!(d->res_detect && !d->iselem_u))
230 /* 3. we have a violation of D \subset U, namely d */
232 DPRINTF(resolving,alg, " violation %s\n", di->pname);
234 if (d->owner) { /* 3a perhaps */
236 DPRINTF(resolving,alg, " expected %s\n", t->pname);
239 DPRINTF(resolving,alg, " expected-is-not-addressable\n");
243 if (t->resolution == RR_H) {
244 DPRINTF(resolving,alg, " expected-is-at-home\n");
248 if (d->movposcomb < 0) {
249 DPRINTF(resolving,alg, " track-unknown-position\n");
253 if (t->resolution == RR_E)
254 /* we added it in this sweep and have found another d */
256 assert(t->resolution == RR_N);
258 /* check E(t) \disjoint U */
260 FOR_SEGMENT(d1, NOOP, NOOP) {
261 if (d1->owner == t && d1->iselem_u) {
262 FOR_TRAIN(t2, NOOP, NOOP) {
263 if (t2->resolution == RR_H && d1->owner == t2) {
264 DPRINTF(resolving,alg, " clash %s %s\n",
265 d1i->pname, t2->pname);
272 DPRINTF(resolving,alg, " expected-has-clashes\n");
280 DPRINTF(resolving,alg, " supposing %s as-expected\n", t->pname);
285 if (d->home) { /* 3b then */
289 Train *t1= d->home; /* t' st d \elem H(t') */
290 DPRINTF(resolving,alg, " home %s\n", t1->pname);
293 DPRINTF(resolving,alg, " home-is-not-addressable\n");
297 DPRINTF(resolving,alg, " reset-expecteds\n");
298 FOR_TRAIN(tplus, NOOP,NOOP) {
299 if (tplus->resolution == RR_E) {
300 DPRINTF(resolving,alg, " supposing %s absent\n", tplus->pname);
301 tplus->resolution= RR_N;
305 /* Now must trim U to correspond to our setting of R(t+)=N
306 * so that any other implied Hs are spotted. */
307 FOR_SEGMENT(dplus, NOOP,NOOP) {
308 Train *tplus= dplus->owner;
309 if (!(tplus && tplus->resolution == RR_E)) continue;
313 t1->resolution= RR_H;
316 DPRINTF(resolving,alg, " supposing %s at-home\n", t1->pname);
325 ouprintf("resolution inexplicable %s\n", di->pname);
331 TrackAdvanceContext tac;
333 tac.getmovpos= resmain_getmovpos;
335 FOR_SEGMENT(d,NOOP,NOOP) {
336 if (problems) return problems;
340 target= just_getmovpos(d,&tac);
342 if (d->i->invertible) {
343 Segment *interferer= segment_interferes_simple(&tac,d);
345 actual_inversions_start();
346 d->seg_inverted= interferer->seg_inverted
347 ^ d->i->interferes_polarity_opposed;
348 actual_inversions_segment(d);
349 actual_inversions_done();
353 if (d->i->n_poscombs>1) {
356 movpos_unreserve(d->motion);
359 if (!d->res_movposset)
362 ErrorCode ec= movpos_reserve(d,-1,&d->motion,target,-1);
364 ouprintf("resolution movpos-change-failed %s/%s %s\n",
365 d->i->pname, d->i->poscombs[target].pname,
373 assert(!d->i->n_movfeats);
382 /*---------- heads and tails of trains, final placement ----------*/
384 #define resfin_ours mark1
387 Distance hardallow, min, max;
389 Segment *lastdetect, *hardend;
393 TrackAdvanceContext tc; /* first! */
397 Distance atlastdetect;
399 FindEndConstraint constraints[2];
401 } FindEndUserContext;
403 static int findends_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
404 MovPosComb *use_io) {
405 const char *pn= t->seg->i->pname;
406 if (t->seg->motion) *use_io= movpos_change_intent(t->seg->motion);
408 DPRINTF(resolving,ends, " getmovpos %s fails\n", pn);
411 DPRINTF(resolving,ends, " getmovpos %s -> %d\n", pn, *use_io);
415 static int constraint_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
416 MovPosComb *mpc, const TrackLocation *before) {
418 FindEndUserContext *u= (void*)c;
420 DPRINTF(resolving,ends, " constraint_nextseg %c"
421 " %s%s remain=%d dist=INF-%d det=%d ours=%d owner=%s home=%s\n",
423 t->backwards?"-":"", t->seg->i->pname, t->remain,
424 TL_DIST_INF - c->distance,
427 t->seg->owner ? t->seg->owner->pname : "-",
428 t->seg->home ? t->seg->home->pname : "-");
430 if (!t->seg->resfin_ours) return 'o';
431 r= findends_getmovpos(t,0,mpc); if (r) return r;
433 const SegPosCombInfo *pci= &t->seg->i->poscombs[*mpc];
436 if (t->seg == u->startpoint) return 'l';
437 const SegmentLinkInfo *rlink= &pci->link[!t->backwards];
438 if (before->seg != &segments[rlink->next]) {
439 DPRINTF(resolving,ends, " j mismatch %s %s\n",
440 before->seg->i->pname, info_segments[rlink->next].pname);
444 if (t->seg->res_detect) {
445 u->atlastdetect= c->distance;
447 /* wind back to start of this segment which is necessary if this
448 * we are looking rearwards */
449 u->atlastdetect += (pci->dist - t->remain);
450 u->lastdetect= t->seg;
455 static int constraint_trackend(TrackLocation *t,
456 struct TrackAdvanceContext *c) {
460 static void end_startpoint(FindEndUserContext *u) {
463 u->tc.distance= TL_DIST_INF;
464 u->tc.nextseg= constraint_nextseg;
465 u->tc.getmovpos= findends_getmovpos;
466 u->tc.trackend= constraint_trackend;
469 r= trackloc_set_exactinto(&u->t, &u->tc,
470 u->startpoint, u->startpoint->tr_backwards,
475 static void end_constraints(FindEndUserContext *u) {
476 FindEndConstraint lim;
477 Train *tra= u->train;
481 u->tc.distance= TL_DIST_INF;
487 r= trackloc_reverse_exact(&u->t,&u->tc);
491 FindEndConstraint *cons= &u->constraints[u->rear];
493 cons->hardwhy= trackloc_advance(&u->t,&u->tc);
495 assert(cons->hardwhy);
496 assert(u->lastdetect);
497 assert(u->atlastdetect >= 0);
499 Distance nose= MARGIN_NOSE +
500 ((tra->backwards ^ u->rear) ? tra->head : tra->tail);
502 lim.min= TL_DIST_INF - u->atlastdetect;
503 lim.max= (TL_DIST_INF - u->tc.distance) - nose;
509 cons->max= -lim.min + ceil(tra->detectable * MARGIN_TRAINLENGTH);
510 cons->min= -lim.max + ceil(tra->detectable * MARGIN_TRAINLENGTH);
512 cons->lastdetect= u->lastdetect;
513 cons->hardend= u->t.seg;
515 if (cons->min > cons->max) {
516 ouprintf("resolution implausible %s %s overhang %d %d\n",
517 cons->lastdetect->i->pname, tra->pname,
518 cons->min - cons->max, nose);
522 DPRINTF(resolving,ends, " lims %c %s,%s,%c dist=INF-%d"
523 " lim=%d..%d out=%d..%d\n",
525 cons->lastdetect->i->pname, u->t.seg->i->pname, cons->hardwhy,
526 TL_DIST_INF - u->tc.distance,
527 lim.min, lim.max, cons->min, cons->max);
530 static int resolve_complete_ends_train(Train *tra) {
532 FindEndUserContext u;
533 const SegPosCombInfo *pci;
536 switch (tra->resolution) {
543 DPRINTF1(resolving,ends, "%s %c",
544 tra->pname, RESOLUTION_CHARS[tra->resolution]);
546 memset(&u,0,sizeof(u));
549 FOR_SEGMENT(seg,NOOP,NOOP) {
550 seg->resfin_ours= tra ==
551 (tra->resolution==RR_H ? seg->home : seg->owner);
552 if (!seg->resfin_ours) continue;
553 DPRINTF2(" %s%s", seg->tr_backwards?"-":"", seg->i->pname);
554 if (seg->res_detect) {
559 assert(u.startpoint);
562 /* There are four pieces of information we have:
563 * The rearmost detection, the rearmost end of ownership
564 * the foremost detection, the foremost end of ownership
566 * From each we compute a range of locations. These are
567 * in the form of a minimum and maximum distance of the
568 * foredetect ahead of 0 into the startpoint.
572 for (u.rear=0; u.rear<2; u.rear++)
577 if ((wrongness= u.constraints[0].min - u.constraints[1].max) > 0) {
578 for (u.rear=1; u.rear>0; u.rear--)
579 ouprintf("resolution implausible %s %s over-detect %d\n",
580 u.constraints[u.rear].lastdetect->i->pname,
581 tra->pname, wrongness);
584 if ((wrongness= u.constraints[1].min - u.constraints[0].max) > 0) {
585 for (u.rear=1; u.rear>0; u.rear--)
586 ouprintf("resolution implausible %s %s cramped %d\n",
587 u.constraints[u.rear].hardend->i->pname,
588 tra->pname, wrongness);
592 int min= MAX(u.constraints[0].min, u.constraints[1].min);
593 int max= MIN(u.constraints[0].max, u.constraints[1].max);
594 DPRINTF(resolving,ends, " lims a %d..%d problems=%d\n",
595 min, max, u.problems);
601 assert(max > 0); /* startpoint is detected so foredet is there or later */
603 /* Now we just need to turn this into the canonical format. */
608 r= trackloc_advance(&u.t, &u.tc); assert(!r);
610 tra->foredetect= u.t.seg;
611 tra->foredetect->tr_backwards= u.t.backwards;
613 r= trackloc_getlink(&u.t, &u.tc, &pci,0,0); assert(!r);
614 tra->maxinto= pci->dist - u.t.remain;
615 tra->uncertainty= max - min;
617 report_train_position(tra);
618 /* We don't lay the train out now since the moveable features
619 * are not yet in place. We can defer it, though, as it is guaranteed
625 static int resolve_complete_ends(void) {
630 FOR_TRAIN(tra,NOOP,NOOP)
631 problems += resolve_complete_ends_train(tra);
638 /*---------- main resolutionentrypoints ----------*/
640 void resolve_begin(void) {
642 actual_inversions_start();
645 if (segi->invertible)
646 actual_inversions_segment(seg);
648 seg->seg_inverted= 0;
649 if (segi->n_poscombs==1)
652 actual_inversions_done();
655 int resolve_complete(void) {
661 problems= resolve_complete_main();
663 problems += resolve_complete_ends();
666 ouprintf("resolution problems %d\n",problems);
671 if (seg->owner && seg->owner->resolution == RR_N)
674 MovPosChange *reservation= seg->motion;
675 if (!reservation) continue;
677 MovPosComb target= movpos_change_intent(reservation);
678 ec= movpos_change(seg, target, -1, reservation); assert(!ec);
682 speedmanager_reset_train(tra);
687 void resolve_motioncheck(void) {
693 assert(sta_state == Sta_Finalising);
696 if (seg->moving) return;
699 if (tra->resolution == RR_N) continue;
701 mgettimeofday(&tnow);
702 tra->plan_lookahead_nsegs= INT_MAX;
703 ec= predict(tra,tnow, PREDF_OLDPLAN, 0,0, 0,
704 0,(char*)"resolution confirmation");
709 if (seg->i->n_poscombs==1) continue;
710 if (seg->res_movposset) continue;
711 assert(!seg->motion);
715 sta_finalising_done();