3 * safety core algorithm
11 static void seg_clear_stale(SegmentState *seg) {
12 if (!seg->tr_updated) {
14 seg->until_here= seg->until_detect= 0;
21 /* modified in place by lay_train_pass: */
23 int invert_count[2]; /* count of (switchable) segments,
24 * invert_count[0]: inverted from train's pov
25 * iff train is backwards (ie, train not inverted)
26 * invert_count[1]: train is inverted
27 * set to -1 if any unswitchable is the other way */
28 SegmentNum invert_forcer; /* the unswitchable which forces */
31 static void lay_train_pass(LayTrainState *l,
32 TrackLocation tloc, long advance,
33 long speed, unsigned backwards,
34 unsigned check_clash) {
35 State *s= &safety_state;
38 const SegmentInfo *segi;
39 long overall, remain, dist_until, time_until;
40 int *invert_likehere, train_inverted_here;
41 TrainState *tra= &s->trains[l->tran];
45 segn= tra->foredetect;
46 seg= &s->segments[segn];
48 remain= overall= advance + speed * SPEED_CLEAR_MULT;
52 seg= &s->segments[segn];
53 segi= &info_segments[segn];
56 if (seg->tr_updated) {
57 l->ec= safety_problem(l->tran, tloc.segn, "self-collision");
61 if (seg->owner != l->tran) {
62 l->ec= safety_problem(l->tran, tloc.segn, "collision with %s",
63 info_trains[seg->owner].pname);
71 seg->tr_backwards= tloc.backwards ^ backwards;
75 train_inverted_here= seg->seg_inverted ^ seg->tr_backwards;
76 invert_likehere= &l->invert_count[train_inverted_here];
78 if (segi->invertible) {
79 if (*invert_likehere >= 0)
82 if (*invert_likehere < 0) {
83 l->ec= safety_problem(l->tran, NOTA(Segment), "train requires"
84 " noninvertible segments with opposite polarity:"
85 " @%s, @%s", segi->pname,
86 info_segments[l->invert_forcer].pname);
89 assert(!seg->seg_inverted);
91 l->invert_count[!train_inverted_here]= -1;
92 l->invert_forcer= segn;
95 dist_until= (overall - remain) - advance;
96 time_until= !speed ? 0 : SPEED_CALC_TIME(speed, dist_until, DOWN);
97 *(check_clash ? &seg->until_here : &seg->until_detect)= time_until;
100 trackloc_further(&tloc, &remain);
104 static void lay_train_inversions(LayTrainState *l) {
105 State *s= &safety_state;
108 const SegmentInfo *segi;
109 int train_be_inverted, seg_be_inverted;
113 train_be_inverted= l->invert_count[1] > l->invert_count[0];
114 assert(l->invert_count[train_be_inverted] >= 0);
116 actual_inversions_start();
118 for (segn=0, seg=s->segments, segi=info_segments;
119 segn <= NUM_SEGMENTS;
121 if (!seg->tr_updated) continue;
122 assert(seg->owner == l->tran);
123 seg_be_inverted= train_be_inverted ^ seg->tr_backwards;
124 assert(!(seg_be_inverted && !segi->invertible));
125 seg->seg_inverted= seg_be_inverted;
126 actual_inversions_segment(segn);
128 actual_inversions_done();
131 static void lay_train_done(LayTrainState *l) {
132 State *s= &safety_state;
136 for (segn=0, seg=s->segments;
137 segn <= NUM_SEGMENTS;
139 if (seg->owner == l->tran) {
140 if (!seg->tr_updated) seg_clear_stale(seg);
143 assert(!seg->tr_updated);
144 assert(seg->until_detect >= seg->until_here);
148 static ErrorCode lay_train(TrainNum tran, long added_slop) {
149 State *s= &safety_state;
150 TrainState *tra= &s->trains[tran];
151 const TrainInfo *trai= &info_trains[tran];
155 long head, headslop, tail, taildet;
158 segn= tra->foredetect;
159 seg= &s->segments[segn];
162 tloc.into= tra->maxinto;
163 tloc.backwards= seg->tr_backwards ^ tra->backwards;
167 l.invert_count[0]= l.invert_count[1]= 0;
169 head= tra->backwards ? trai->tail : trai->head;
170 headslop= head + added_slop;
173 * update actual train location (train's head and future locations).
176 * update detection (future locations) only.
179 * train location (current location and tail)
181 * Clash checking is done in passes 1 and 3, including checking
182 * whether the train clashes with itself (ie, fail if we find
183 * segment with same train and tr_updated set).
185 lay_train_pass(&l, tloc, headslop, tra->speed, 0, 1);
186 lay_train_pass(&l, tloc, 0, tra->speed, 0, 0);
188 trackloc_reverse(&tloc);
189 seg->tr_updated= 0; /* we're about to do this one again */
190 tail= tra->backwards ? trai->head : trai->tail;
191 taildet= trai->detectable + tail;
193 lay_train_pass(&l, tloc, taildet, 0, 1, 1);
195 lay_train_inversions(&l);
201 static void setspeed(TrainNum tran, Speed newspeed) {
202 /* does not lay the train, caller must do that (or have done it,
203 * in which case they should already have set tra->speed). */
204 State *s= &safety_state;
205 TrainState *tra= &s->trains[tran];
207 tra->speed= newspeed;
208 actual_setspeed(tran);
209 speedmanager_speedchange_notify(tran);
212 void safety_notify_detection(SegmentNum segn) {
213 State *s= &safety_state;
214 SegmentState *seg= &s->segments[segn];
215 TrainNum tran= seg->owner;
216 TrainState *tra= &s->trains[tran];
221 safety_panic(NOTA(Train), segn, "unexpected detection");
223 if (!seg->until_detect)
228 tloc.backwards= seg->tr_backwards;
230 tra->foredetect= segn;
231 tra->maxinto= trackloc_remaininseg(&tloc);
233 if (seg->cm_autostop) {
234 if (tra->speed < AUTOSTOP_MAXSPEED &&
235 tra->maxinto > AUTOSTOP_UNCERTAINTY)
236 /* At some point we may need to allow more slop when the
237 * next segment is points than when this is the last segment
238 * (ie, just buffers next). */
239 tra->maxinto= AUTOSTOP_UNCERTAINTY;
244 tra->uncertainty= tra->maxinto;
246 ec= lay_train(tran, 0);
248 logmsg(ec, tran, segn, "emergency stop");
249 safety_emergencystop(tran);
253 void safety_emergencystop(TrainNum tran) {
254 State *s= &safety_state;
256 TrainState *tra= &s->trains[tran];
259 actual_emergencystop(tran);
260 ec= lay_train(tran, ESTOP_UNCERTAINTY);
261 if (ec) safety_panic(tran, NOTA(Segment), "emergency stop forbidden!");
262 speedmanager_speedchange_notify(tran);
265 void safety_requestspeed(TrainNum tran, long newspeed) {
266 State *s= &safety_state;
268 TrainState *tra= &s->trains[tran];
271 oldspeed= tra->speed;
272 tra->speed= newspeed;
274 ec= lay_train(tran, 0);
277 if (oldspeed && oldspeed < newspeed) {
278 logmsg(ec, tran, NOTA(Segment), "countermanded acceleration"
279 " from %ld to %ld", oldspeed, newspeed);
280 } else if (oldspeed) {
281 safety_panic(tran, NOTA(Segment), "deceleration forbidden!"
282 " (from %ld to %ld", oldspeed, newspeed);
284 logmsg(ec, tran, NOTA(Segment), "countermanded motion start");
286 setspeed(tran, oldspeed);
287 oprintf(UPO, "countermanded %s %ld %ld\n",
288 info_trains[tran].pname, oldspeed, newspeed);
289 ec= lay_train(tran, 0);
291 safety_panic(tran, NOTA(Segment), "countermanding"
292 " speed change insufficient!");
296 setspeed(tran, newspeed);