3 * safety core algorithm
15 static void seg_clear_stale(Segment *seg) {
16 if (!seg->tr_updated) {
18 seg->until_here= seg->until_detect= 0;
25 /* modified in place by lay_train_pass: */
27 int invert_count[2]; /* count of (switchable) segments,
28 * invert_count[0]: inverted from train's pov
29 * iff train is backwards (ie, train not inverted)
30 * invert_count[1]: train is inverted
31 * set to -1 if any unswitchable is the other way */
32 Segment *invert_forcer; /* the unswitchable which forces */
35 static void lay_train_pass(LayTrainState *l,
36 TrackLocation tloc, long advance,
37 long speed, unsigned backwards,
38 unsigned check_clash) {
40 long overall, remain, dist_until, time_until;
41 int *invert_likehere, train_inverted_here;
48 remain= overall= advance + speed * SPEED_CLEAR_MULT;
54 if (seg->tr_updated) {
55 l->ec= safety_problem(l->tra, tloc.seg, "self-collision");
59 if (seg->owner != l->tra) {
60 l->ec= safety_problem(l->tra, tloc.seg, "collision with %s",
69 seg->tr_backwards= tloc.backwards ^ backwards;
72 train_inverted_here= seg->seg_inverted ^ seg->tr_backwards;
73 invert_likehere= &l->invert_count[train_inverted_here];
75 if (seg->i->invertible) {
76 if (*invert_likehere >= 0)
79 if (*invert_likehere < 0) {
80 l->ec= safety_problem(l->tra,0, "train requires"
81 " noninvertible segments with opposite polarity:"
82 " @%s, @%s", seg->i->pname,
83 l->invert_forcer->i->pname);
86 assert(!seg->seg_inverted);
88 l->invert_count[!train_inverted_here]= -1;
89 l->invert_forcer= seg;
92 dist_until= (overall - remain) - advance;
93 time_until= !speed ? 0 : SPEED_CALC_TIME(speed, dist_until, DOWN);
94 *(check_clash ? &seg->until_here : &seg->until_detect)= time_until;
97 trackloc_further(&tloc, &remain);
101 static void lay_train_inversions(LayTrainState *l) {
103 int train_be_inverted, seg_be_inverted;
107 train_be_inverted= l->invert_count[1] > l->invert_count[0];
108 assert(l->invert_count[train_be_inverted] >= 0);
110 actual_inversions_start();
113 if (!seg->tr_updated) continue;
114 assert(seg->owner == l->tra);
115 seg_be_inverted= train_be_inverted ^ seg->tr_backwards;
116 assert(!(seg_be_inverted && !segi->invertible));
117 seg->seg_inverted= seg_be_inverted;
118 actual_inversions_segment(seg);
120 actual_inversions_done();
123 static void lay_train_done(LayTrainState *l) {
127 if (seg->owner == l->tra) {
128 if (!seg->tr_updated) seg_clear_stale(seg);
131 assert(!seg->tr_updated);
132 assert(seg->until_detect >= seg->until_here);
136 static ErrorCode lay_train(Train *tra, long added_slop) {
139 long head, headslop, tail, taildet;
142 seg= tra->foredetect;
145 tloc.into= tra->maxinto;
146 tloc.backwards= seg->tr_backwards ^ tra->backwards;
150 l.invert_count[0]= l.invert_count[1]= 0;
152 head= tra->backwards ? tra->tail : tra->head;
153 headslop= head + added_slop;
156 * update actual train location (train's head and future locations).
159 * update detection (future locations) only.
162 * train location (current location and tail)
164 * Clash checking is done in passes 1 and 3, including checking
165 * whether the train clashes with itself (ie, fail if we find
166 * segment with same train and tr_updated set).
168 lay_train_pass(&l, tloc, headslop, tra->speed, 0, 1);
169 lay_train_pass(&l, tloc, 0, tra->speed, 0, 0);
171 trackloc_reverse(&tloc);
172 seg->tr_updated= 0; /* we're about to do this one again */
173 tail= tra->backwards ? tra->head : tra->tail;
174 taildet= tra->detectable + tail;
176 lay_train_pass(&l, tloc, taildet, 0, 1, 1);
178 lay_train_inversions(&l);
184 void safety_notify_detection(Segment *seg) {
185 Train *tra= seg->owner;
190 safety_panic(0,seg, "unexpected detection");
192 if (!seg->until_detect)
197 tloc.backwards= seg->tr_backwards;
199 tra->foredetect= seg;
200 tra->maxinto= trackloc_remaininseg(&tloc);
202 if (seg->cm_autostop) {
204 if (!tra->estopping) {
205 speedmanager_autostop(tra);
206 if (!tra->speed && tra->maxinto > AUTOSTOP_UNCERTAINTY)
207 /* At some point we may need to allow more slop when the
208 * next segment is points than when this is the last segment
209 * (ie, just buffers next). */
210 tra->maxinto= AUTOSTOP_UNCERTAINTY;
213 tra->uncertainty= tra->maxinto;
215 if (!tra->estopping) {
216 ec= lay_train(tra, 0);
218 logmsg(ec,tra,seg->i, "emergency stop");
219 safety_emergencystop(tra);
224 void safety_emergencystop(Train *tra) {
227 if (tra->estopping) return;
229 ec= lay_train(tra, ESTOP_UNCERTAINTY);
230 if (ec) safety_panic(tra,0, "emergency stop forbidden!");
231 speedmanager_emergencystop(tra);
234 ErrorCode safety_requestspeed(Train *tra, long newspeed) {
238 oldspeed= tra->speed;
239 tra->speed= newspeed;
241 ec= lay_train(tra, 0);
246 if (oldspeed && oldspeed < newspeed) {
247 logmsg(ec,tra,0, "countermanded acceleration"
248 " from %ld to %ld", oldspeed, newspeed);
249 } else if (oldspeed) {
250 safety_panic(tra,0, "deceleration forbidden!"
251 " (from %ld to %ld", oldspeed, newspeed);
253 logmsg(ec,tra,0, "countermanded motion start");
255 oprintf(UPO, "countermanded %s %ld %ld\n",
256 tra->pname, oldspeed, newspeed);
257 revert_ec= lay_train(tra, 0);
259 safety_panic(tra,0, "countermanding"
260 " speed change insufficient!");