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) {
104 const SegmentInfo *segi;
105 int train_be_inverted, seg_be_inverted;
109 train_be_inverted= l->invert_count[1] > l->invert_count[0];
110 assert(l->invert_count[train_be_inverted] >= 0);
112 actual_inversions_start();
114 for (segn=0, seg=segments, segi=info_segments;
115 segn <= NUM_SEGMENTS;
116 segn++, seg++, segi++) {
117 if (!seg->tr_updated) continue;
118 assert(seg->owner == l->tra);
119 seg_be_inverted= train_be_inverted ^ seg->tr_backwards;
120 assert(!(seg_be_inverted && !segi->invertible));
121 seg->seg_inverted= seg_be_inverted;
122 actual_inversions_segment(seg);
124 actual_inversions_done();
127 static void lay_train_done(LayTrainState *l) {
131 for (segn=0, seg=segments;
132 segn <= NUM_SEGMENTS;
134 if (seg->owner == l->tra) {
135 if (!seg->tr_updated) seg_clear_stale(seg);
138 assert(!seg->tr_updated);
139 assert(seg->until_detect >= seg->until_here);
143 static ErrorCode lay_train(Train *tra, long added_slop) {
146 long head, headslop, tail, taildet;
149 seg= tra->foredetect;
152 tloc.into= tra->maxinto;
153 tloc.backwards= seg->tr_backwards ^ tra->backwards;
157 l.invert_count[0]= l.invert_count[1]= 0;
159 head= tra->backwards ? tra->tail : tra->head;
160 headslop= head + added_slop;
163 * update actual train location (train's head and future locations).
166 * update detection (future locations) only.
169 * train location (current location and tail)
171 * Clash checking is done in passes 1 and 3, including checking
172 * whether the train clashes with itself (ie, fail if we find
173 * segment with same train and tr_updated set).
175 lay_train_pass(&l, tloc, headslop, tra->speed, 0, 1);
176 lay_train_pass(&l, tloc, 0, tra->speed, 0, 0);
178 trackloc_reverse(&tloc);
179 seg->tr_updated= 0; /* we're about to do this one again */
180 tail= tra->backwards ? tra->head : tra->tail;
181 taildet= tra->detectable + tail;
183 lay_train_pass(&l, tloc, taildet, 0, 1, 1);
185 lay_train_inversions(&l);
191 void safety_notify_detection(Segment *seg) {
192 Train *tra= seg->owner;
197 safety_panic(0,seg, "unexpected detection");
199 if (!seg->until_detect)
204 tloc.backwards= seg->tr_backwards;
206 tra->foredetect= seg;
207 tra->maxinto= trackloc_remaininseg(&tloc);
209 if (seg->cm_autostop) {
211 if (!tra->estopping) {
212 speedmanager_autostop(tra);
213 if (!tra->speed && tra->maxinto > AUTOSTOP_UNCERTAINTY)
214 /* At some point we may need to allow more slop when the
215 * next segment is points than when this is the last segment
216 * (ie, just buffers next). */
217 tra->maxinto= AUTOSTOP_UNCERTAINTY;
220 tra->uncertainty= tra->maxinto;
222 if (!tra->estopping) {
223 ec= lay_train(tra, 0);
225 logmsg(ec,tra,seg->i, "emergency stop");
226 safety_emergencystop(tra);
231 void safety_emergencystop(Train *tra) {
234 if (tra->estopping) return;
236 ec= lay_train(tra, ESTOP_UNCERTAINTY);
237 if (ec) safety_panic(tra,0, "emergency stop forbidden!");
238 speedmanager_emergencystop(tra);
241 ErrorCode safety_requestspeed(Train *tra, long newspeed) {
245 oldspeed= tra->speed;
246 tra->speed= newspeed;
248 ec= lay_train(tra, 0);
253 if (oldspeed && oldspeed < newspeed) {
254 logmsg(ec,tra,0, "countermanded acceleration"
255 " from %ld to %ld", oldspeed, newspeed);
256 } else if (oldspeed) {
257 safety_panic(tra,0, "deceleration forbidden!"
258 " (from %ld to %ld", oldspeed, newspeed);
260 logmsg(ec,tra,0, "countermanded motion start");
262 oprintf(UPO, "countermanded %s %ld %ld\n",
263 tra->pname, oldspeed, newspeed);
264 revert_ec= lay_train(tra, 0);
266 safety_panic(tra,0, "countermanding"
267 " speed change insufficient!");