chiark / gitweb /
hostside: more length for bavarian
[trains.git] / hostside / resolve.c
1 /*
2  * resolve detected trains into initial state
3  */
4
5 #include "realtime.h"
6
7 #define NOOP (void)0
8
9 /*---------- main resolution processing ----------*/
10  /*
11   * Algorithm:
12   *
13   *  Notation and background:
14   *
15   *     S                       set of segments
16   *     D \subset S             set of segments where we have detection
17   *
18   *     T                       set of trains
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
21   *
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')
25   *
26   *  We want to find a mapping R(t)
27   *     R(t) \elem { N, E, H }          t \elem T,  N(t') = \null
28   *  giving
29   *     A(t) = (R(t))(t)                segments allocated to train t
30   *      U = \union_{t} A(t)
31   *  satisfing
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.
35   *
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.
39   *
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.
45   *
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).
48   *
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.
51   *
52   *  1. Initially we set \all_{t} R(t) = N.
53   *
54   *     Optimality: This is minimal for U = \null, as Rank(R) = 0.
55   *
56   *  2. Then we search for lack of coverage, ie violations of D \subset U.
57   *     If none are found we are done.
58   *
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.
62   *
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.
66   *
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.)
74   *     
75   *     So the rank increase by 1 is is minimal for U2.
76   *
77   *     Proof elements: R(t) = E is demonstrated by
78   *     every d meeting these conditions.
79   *
80   *  3b.        Alternatively, hope that  d \elem H(t')
81   *
82   *     If so set  R2(t') = H
83   *                \all_{t+} R2(t+) = N where R(t+) = E.
84   *
85   *     Optimality: in the case we are now dealing with, we
86   *     didn't succeed with the test for 3a, so either:
87   *
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.
90   *
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.
93   *
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)
97   *
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.
103   *
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
110   *           unavoidable.
111   *
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)
117   *     plus in case (iii)
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
121   *           if needed)
122   *
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.
125   *
126   *     The rank is increased: we increase Count(R,H) and set
127   *     Count(R,E) to 0.
128   *
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.
132   *
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
136   *         that t
137   *         plus the clashes d'' as for 3b(iii).
138   *
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.
143   *
144   */
145
146 /* values for Train->resolution: */
147 #define RR_N 0u
148 #define RR_E 1u
149 #define RR_H 2u
150 #define RESOLUTION_CHARS "NEH"
151
152 /* During the main algorithm:
153  *  We record R in tra->resolution,
154  *  U in segi->mark0 and D in segi->res_detect
155  */
156 #define iselem_u mark0
157
158 static int resmain_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
159                              MovPosComb *use_io) {
160   Segment *d= t->seg;
161   const char *pn= d->i->pname;
162
163   if (d->home && d->home->resolution == RR_H) {
164     *use_io= 0; /* a bit kludgey */
165     DPRINTF(resolving,main, "getmovpos %s H\n", pn);
166   } else if (d->owner) {
167     *use_io= d->movposcomb;
168     DPRINTF(resolving,main, "getmovpos %s E %d\n", pn, *use_io);
169   } else if (!d->res_movposset) {
170     *use_io= -1;
171     DPRINTF(resolving,main, "getmovpos %s N -1\n", pn);
172   } else if (d->motion) {
173     *use_io= movpos_change_intent(d->motion);
174     DPRINTF(resolving,main, "getmovpos %s M %u\n", pn, *use_io);
175   }
176   return 0;
177 }
178
179 static MovPosComb just_getmovpos(Segment *d, TrackAdvanceContext *tac) {
180   MovPosComb target;
181   TrackLocation tloc;
182   int r;
183
184   tloc.seg= d;
185   r= resmain_getmovpos(&tloc,tac,&target);  assert(!r);
186   return target;
187 }
188
189 static int resolve_complete_main(void) {
190   int problems, phase, nextphase;
191   TRAIN_ITERVARS(t);
192   TRAIN_ITERVARS(t2);
193   TRAIN_ITERVARS(tplus);
194   SEGMENT_ITERVARS(d);
195   SEGMENT_ITERVARS(d1);
196   SEGMENT_ITERVARS(dplus);
197
198   problems= 0;
199   FOR_TRAIN(t,NOOP,NOOP) t->resolution= RR_N;
200
201   for (phase=0; phase<3; phase=nextphase) {
202     nextphase= phase+1;
203     DPRINTF(resolving,alg, "iteration %c\n", "EHX"[phase]);
204
205     DPRINTF(resolving,alg, " calculate-u\n");
206
207     FOR_SEGMENT(d,NOOP,NOOP) { /* calculate U */
208       unsigned updated= 0;
209
210 #define ADDTO_U_EH(homeowner,HH_HE,string)                              \
211       if (d->homeowner && d->homeowner->resolution == HH_HE) {          \
212         DPRINTF(resolving,alg, "  covered %s " string " %s\n",  \
213                 di->pname, d->homeowner->pname);                        \
214         updated++;                                                      \
215       }
216       ADDTO_U_EH(home,  RR_H, "at-home");
217       if (d->movposcomb >= 0)
218         ADDTO_U_EH(owner, RR_E, "as-expected");
219
220       assert(updated<=1);
221       d->iselem_u= updated;
222     }
223
224     DPRINTF(resolving,alg, " searching\n");
225     FOR_SEGMENT(d, NOOP, NOOP) {
226       if (!(d->res_detect && !d->iselem_u))
227         continue;
228       /* 3. we have a violation of D \subset U, namely d */
229
230       DPRINTF(resolving,alg, "  violation %s\n", di->pname);
231
232       if (d->owner) { /* 3a perhaps */
233         t= d->owner;
234         DPRINTF(resolving,alg, "   expected %s\n", t->pname);
235
236         if (t->addr < 0) {
237           DPRINTF(resolving,alg, "    expected-is-not-addressable\n");
238           goto not_3a;
239         }
240
241         if (t->resolution == RR_H) {
242           DPRINTF(resolving,alg, "    expected-is-at-home\n");
243           goto not_3a;
244         }
245
246         if (d->movposcomb < 0) {
247           DPRINTF(resolving,alg, "    track-unknown-position\n");
248           goto not_3a;
249         }
250
251         if (t->resolution == RR_E)
252           /* we added it in this sweep and have found another d */
253           goto already_3a;
254         assert(t->resolution == RR_N);
255
256         /* check E(t) \disjoint U */
257         int clashes= 0;
258         FOR_SEGMENT(d1, NOOP, NOOP) {
259           if (d1->owner == t && d1->iselem_u) {
260             FOR_TRAIN(t2, NOOP, NOOP) {
261               if (t2->resolution == RR_H && d1->owner == t2) {
262                 DPRINTF(resolving,alg, "    clash %s %s\n",
263                         d1i->pname, t2->pname);
264                 clashes++;
265               }
266             }
267           }
268         }
269         if (clashes) {
270           DPRINTF(resolving,alg, "   expected-has-clashes\n");
271           goto not_3a;
272         }
273
274         /* Yay! 3a. */
275         t->resolution= RR_E;
276         nextphase= 0;
277       already_3a:
278         DPRINTF(resolving,alg, "   supposing %s as-expected\n", t->pname);
279         continue;
280       }
281     not_3a:
282
283       if (d->home) { /* 3b then */
284         if (phase<1)
285           continue;
286
287         Train *t1= d->home; /* t' st d \elem H(t') */
288         DPRINTF(resolving,alg, "   home %s\n", t1->pname);
289         
290         if (t1->addr < 0) {
291           DPRINTF(resolving,alg, "    home-is-not-addressable\n");
292           goto not_3b;
293         }
294
295         DPRINTF(resolving,alg, "   reset-expecteds\n");
296         FOR_TRAIN(tplus, NOOP,NOOP) {
297           if (tplus->resolution == RR_E) {
298             DPRINTF(resolving,alg, "   supposing %s absent\n", tplus->pname);
299             tplus->resolution= RR_N;
300             nextphase= 0;
301           }
302         }
303         /* Now must trim U to correspond to our setting of R(t+)=N
304          * so that any other implied Hs are spotted. */
305         FOR_SEGMENT(dplus, NOOP,NOOP) {
306           Train *tplus= dplus->owner;
307           if (!(tplus && tplus->resolution == RR_E)) continue;
308           dplus->iselem_u= 0;
309         }
310
311         t1->resolution= RR_H;
312         nextphase= 0;
313
314         DPRINTF(resolving,alg, "   supposing %s at-home\n", t1->pname);
315         continue;
316       }
317     not_3b:
318
319       /* Oh dear, 3c. */
320       if (phase<2)
321         continue;
322
323       ouprintf("resolution inexplicable %s\n", di->pname);
324       d->res_detect= 0;
325       problems++;
326     }
327   }
328
329   FOR_TRAIN(t,NOOP,NOOP) {
330     if (t->resolution == RR_H)
331       t->backwards= 0;
332   }
333   FOR_SEGMENT(d,NOOP,NOOP) {
334     if (d->home && d->home->resolution == RR_H)
335       d->tr_backwards= d->ho_backwards;
336   }
337     
338   TrackAdvanceContext tac;
339   MovPosComb target;
340   tac.getmovpos= resmain_getmovpos;
341
342   FOR_SEGMENT(d,NOOP,NOOP) {
343     if (problems) return problems;
344
345     d->iselem_u= 0;
346
347     target= just_getmovpos(d,&tac);
348
349     if (d->i->invertible) {
350       Segment *interferer= segment_interferes_simple(&tac,d);
351       if (interferer) {
352         actual_inversions_start();
353         d->seg_inverted= interferer->seg_inverted
354             ^ d->i->interferes_polarity_opposed;
355         actual_inversions_segment(d);
356         actual_inversions_done();
357       }
358     }
359
360     if (d->i->n_poscombs>1) {
361       if (target >= 0) {
362         if (!d->moving) {
363           movpos_unreserve(d->motion);
364           d->motion= 0;
365         }
366         if (!d->res_movposset)
367           d->movposcomb= -1;
368
369         ErrorCode ec= movpos_reserve(d,-1,&d->motion,target,-1);
370         if (ec) {
371           ouprintf("resolution movpos-change-failed %s/%s %s\n",
372                    d->i->pname, d->i->poscombs[target].pname,
373                    errorcodelist[ec]);
374           problems++;
375         } else {
376           d->res_movposset= 1;
377         }
378       }
379     } else {
380       assert(!d->i->n_movfeats);
381       d->movposcomb= 0;
382     }
383   }
384   return problems;
385 }
386
387 #undef iselem_u
388
389 /*---------- heads and tails of trains, final placement ----------*/
390
391 #define resfin_ours mark1
392
393 typedef struct {
394   Distance hardallow, min, max;
395   int hardwhy;
396   Segment *lastdetect, *hardend;
397 } FindEndConstraint;
398
399 typedef struct {
400   TrackAdvanceContext tc; /* first! */
401   TrackLocation t;
402   Train *train;
403   Segment *startpoint;
404   Distance atlastdetect;
405   Segment *lastdetect;
406   FindEndConstraint constraints[2];
407   int problems, rear;
408 } FindEndUserContext;
409
410 static int findends_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
411                               MovPosComb *use_io) {
412   const char *pn= t->seg->i->pname;
413   if (t->seg->motion) *use_io= movpos_change_intent(t->seg->motion);
414   if (*use_io<0) {
415     DPRINTF(resolving,ends, "   getmovpos %s fails\n", pn);
416     return 'm';
417   }
418   DPRINTF(resolving,ends, "   getmovpos %s -> %d\n", pn, *use_io);
419   return 0;
420 }
421
422 static int constraint_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
423                               MovPosComb *mpc, const TrackLocation *before) {
424   int r;
425   FindEndUserContext *u= (void*)c;
426   
427   DPRINTF(resolving,ends, "  constraint_nextseg %c"
428           " %s%s remain=%d dist=INF-%d det=%d ours=%d owner=%s home=%s\n",
429           "frx"[u->rear],
430           t->backwards?"-":"", t->seg->i->pname, t->remain,
431           TL_DIST_INF - c->distance,
432           t->seg->res_detect,
433           t->seg->resfin_ours,
434           t->seg->owner ? t->seg->owner->pname : "-",
435           t->seg->home ?  t->seg->home->pname  : "-");
436
437   if (!t->seg->resfin_ours) return 'o';
438   r= findends_getmovpos(t,0,mpc);  if (r) return r;
439
440   const SegPosCombInfo *pci= &t->seg->i->poscombs[*mpc];
441
442   if (before) {
443     if (t->seg == u->startpoint) return 'l';
444     const SegmentLinkInfo *rlink= &pci->link[!t->backwards];
445     if (before->seg != &segments[rlink->next]) {
446       DPRINTF(resolving,ends, "   j mismatch %s %s\n",
447               before->seg->i->pname, info_segments[rlink->next].pname);
448       return 'j';
449     }
450   }
451   if (t->seg->res_detect) {
452     u->atlastdetect= c->distance;
453     if (!before)
454       /* wind back to start of this segment which is necessary if this
455        * we are looking rearwards */
456       u->atlastdetect += (pci->dist - t->remain);
457     u->lastdetect= t->seg;
458   }
459   return 0;
460 }
461
462 static int constraint_trackend(TrackLocation *t,
463                                struct TrackAdvanceContext *c) {
464   return 'e';
465 }
466
467 static void end_startpoint(FindEndUserContext *u) {
468   int r;
469
470   u->tc.distance= TL_DIST_INF;
471   u->tc.nextseg= constraint_nextseg;
472   u->tc.getmovpos= findends_getmovpos;
473   u->tc.trackend= constraint_trackend;
474   u->tc.u= 0;
475
476   r= trackloc_set_exactinto(&u->t, &u->tc,
477                             u->startpoint, u->startpoint->tr_backwards,
478                             0);
479   assert(!r);
480 }
481
482 static void end_constraints(FindEndUserContext *u) {
483   FindEndConstraint lim;
484   Train *tra= u->train;
485   int r;
486
487   end_startpoint(u);
488   u->tc.distance= TL_DIST_INF;
489
490   u->lastdetect= 0;
491   u->atlastdetect= -1;
492
493   if (u->rear) {
494     r= trackloc_reverse_exact(&u->t,&u->tc);
495     assert(!r);
496   }
497
498   FindEndConstraint *cons= &u->constraints[u->rear];
499
500   cons->hardwhy= trackloc_advance(&u->t,&u->tc);
501   
502   assert(cons->hardwhy);
503   assert(u->lastdetect);
504   assert(u->atlastdetect >= 0);
505
506   Distance nose= MARGIN_NOSE +
507     ((tra->backwards ^ u->rear) ? tra->head : tra->tail);
508
509   lim.min= TL_DIST_INF - u->atlastdetect;
510   lim.max= (TL_DIST_INF - u->tc.distance) - nose;
511
512   if (!u->rear) {
513     cons->min= lim.min;
514     cons->max= lim.max;
515   } else {
516     cons->max= -lim.min + ceil(tra->detectable * MARGIN_TRAINLENGTH);
517     cons->min= -lim.max + ceil(tra->detectable * MARGIN_TRAINLENGTH);
518   }
519   cons->lastdetect= u->lastdetect;
520   cons->hardend= u->t.seg;
521
522   if (cons->min > cons->max) {
523     ouprintf("resolution implausible %s %s overhang %d %d\n",
524              cons->lastdetect->i->pname, tra->pname,
525              cons->min - cons->max, nose);
526     u->problems++;
527   }
528
529   DPRINTF(resolving,ends, " lims %c %s,%s,%c dist=INF-%d"
530           " lim=%d..%d out=%d..%d\n",
531           "fr"[u->rear],
532           cons->lastdetect->i->pname, u->t.seg->i->pname, cons->hardwhy,
533           TL_DIST_INF - u->tc.distance,
534           lim.min, lim.max, cons->min, cons->max);
535 }
536
537 static int resolve_complete_ends_train(Train *tra) {
538   SEG_IV;
539   FindEndUserContext u;
540   const SegPosCombInfo *pci;
541   int r, DP;
542
543   switch (tra->resolution) {
544   case RR_H: break;
545   case RR_E: break;
546   case RR_N: return 0;
547   default: abort();
548   }
549
550   DPRINTF1(resolving,ends, "%s %c",
551            tra->pname, RESOLUTION_CHARS[tra->resolution]);
552
553   memset(&u,0,sizeof(u));
554   u.startpoint= 0;
555   
556   FOR_SEGMENT(seg,NOOP,NOOP) {
557     seg->resfin_ours= tra ==
558       (tra->resolution==RR_H ? seg->home : seg->owner);
559     if (!seg->resfin_ours) continue;
560     DPRINTF2(" %s%s", seg->tr_backwards?"-":"", seg->i->pname);
561     if (seg->res_detect) {
562       DPRINTF2("*");
563       u.startpoint= seg;
564     }
565   }
566   assert(u.startpoint);
567   DPRINTF2("\n");
568
569   /* There are four pieces of information we have:
570    * The rearmost detection, the rearmost end of ownership
571    * the foremost detection, the foremost end of ownership
572    *
573    * From each we compute a range of locations.  These are
574    * in the form of a minimum and maximum distance of the
575    * foredetect ahead of 0 into the startpoint.
576    */
577
578   u.train= tra;
579   for (u.rear=0; u.rear<2; u.rear++)
580     end_constraints(&u);
581
582   Distance wrongness;
583
584   if ((wrongness= u.constraints[0].min - u.constraints[1].max) > 0) {
585     for (u.rear=1; u.rear>0; u.rear--)
586       ouprintf("resolution implausible %s %s over-detect %d\n",
587                u.constraints[u.rear].lastdetect->i->pname,
588                tra->pname, wrongness);
589     u.problems++;
590   }
591   if ((wrongness= u.constraints[1].min - u.constraints[0].max) > 0) {
592     for (u.rear=1; u.rear>0; u.rear--)
593       ouprintf("resolution implausible %s %s cramped %d\n",
594                u.constraints[u.rear].hardend->i->pname,
595                tra->pname, wrongness);
596     u.problems++;
597   }
598
599   int min= MAX(u.constraints[0].min, u.constraints[1].min);
600   int max= MIN(u.constraints[0].max, u.constraints[1].max);
601   DPRINTF(resolving,ends, " lims a %d..%d problems=%d\n",
602           min, max, u.problems);
603
604   if (u.problems)
605     return u.problems;
606   
607   assert(min <= max);
608   assert(max > 0); /* startpoint is detected so foredet is there or later */
609
610   /* Now we just need to turn this into the canonical format. */
611
612   end_startpoint(&u);
613
614   u.tc.distance= max;
615   r= trackloc_advance(&u.t, &u.tc);  assert(!r);
616
617   tra->foredetect= u.t.seg;
618   tra->foredetect->tr_backwards= u.t.backwards;
619
620   r= trackloc_getlink(&u.t, &u.tc, &pci,0,0);  assert(!r);
621   tra->maxinto= pci->dist - u.t.remain;
622   tra->uncertainty= max - min;
623
624   report_train_position(tra);
625   /* We don't lay the train out now since the moveable features
626    * are not yet in place.  We can defer it, though, as it is guaranteed
627    * to work, now. */
628
629   return 0;
630 }
631
632 static int resolve_complete_ends(void) {
633   TRA_IV;
634   int problems;
635
636   problems= 0;
637   FOR_TRAIN(tra,NOOP,NOOP)
638     problems += resolve_complete_ends_train(tra);
639   return problems;
640 }
641
642 #undef resfin_ours
643 #undef resfin_done
644
645 /*---------- main resolutionentrypoints ----------*/
646
647 void resolve_begin(void) {
648   SEG_IV;
649   actual_inversions_start();
650   FOR_SEG {
651     seg->res_detect= 0;
652     if (segi->invertible)
653       actual_inversions_segment(seg);
654     else
655       seg->seg_inverted= 0;
656     if (segi->n_poscombs==1)
657       seg->movposcomb= 0;
658   }
659   actual_inversions_done();
660 }
661
662 int resolve_complete(void) {
663   int problems;
664   ErrorCode ec;
665   TRA_IV;
666   SEG_IV;
667
668   problems= resolve_complete_main();
669   if (!problems)
670     problems += resolve_complete_ends();
671
672   if (problems) {
673     ouprintf("resolution problems %d\n",problems);
674     return -1;
675   }
676
677   FOR_SEG {
678     if (seg->owner && seg->owner->resolution == RR_N)
679       seg->owner= 0;
680
681     MovPosChange *reservation= seg->motion;
682     if (!reservation) continue;
683     seg->motion= 0;
684     MovPosComb target= movpos_change_intent(reservation);
685     ec= movpos_change(seg, target, -1, reservation);  assert(!ec);
686   }
687
688   FOR_TRA
689     speedmanager_reset_train(tra);
690
691   return 0;
692 }
693
694 void resolve_motioncheck(void) {
695   ErrorCode ec;
696   SEG_IV;
697   TRA_IV;
698   struct timeval tnow;
699
700   assert(sta_state == Sta_Finalising);
701   
702   FOR_SEG
703     if (seg->moving) return;
704
705   FOR_TRA {
706     if (tra->resolution == RR_N) continue;
707     
708     mgettimeofday(&tnow);
709     tra->plan_lookahead_nsegs= INT_MAX;
710     ec= predict(tra,tnow, PREDF_OLDPLAN, 0,0, 0,
711                 0,(char*)"resolution confirmation");
712     assert(!ec);
713   }
714
715   FOR_SEG {
716     if (seg->i->n_poscombs==1) continue;
717     if (seg->res_movposset) continue;
718     assert(!seg->motion);
719     seg->movposcomb= -1;
720   }
721
722   sta_finalising_done();
723 }