chiark / gitweb /
do not show plat edges as rails
[trains.git] / hostside / safety.c
1 /*
2  */
3
4 #include <stdio.h>
5 #include <assert.h>
6
7 #include "layoutinfo.h"
8 #include "safety.h"
9
10 static void seg_clear_stale(SegmentState *seg) {
11   if (!seg->tr_updated) {
12     seg->owned= 0;
13     seg->until_here= seg->until_detect= 0;
14   }
15 }
16
17 typedef struct {
18   /* constant inputs */
19   TrainNum tran;
20   /* modified in place by lay_train_pass: */
21   ErrorCode ec;
22   int invert_count[1]; /* count of (switchable) segments,
23                         * invert_count[0]: inverted from train's pov
24                         *   iff train is backwards (ie, train not inverted)
25                         * invert_count[1]: train is inverted
26                         * set to -1 if any unswitchable is the other way */
27   SegmentNum invert_forcer; /* the unswitchable which forces */
28 } LayTrainState;
29
30 static void lay_train_pass(LayTrainState *l,
31                            TrackLocation tloc, long advance,
32                            long speed, unsigned backwards,
33                            unsigned check_clash) {
34   State *s= &safety_state;
35   SegmentNum segn;
36   SegmentState *seg;
37   const SegmentInfo *segi;
38   long overall, remain, dist_until, time_until;
39   int *invert_likehere, train_inverted_here;
40   TrainState *tra= &s->trains[l->tran];
41
42   if (l->ec) return;
43
44   segn= tra->foredetect;
45   seg= &s->segments[segn];
46
47   remain= overall= advance + speed * SPEED_CLEAR_MULT;
48
49   for (;;) {
50     segn= tloc.segn;
51     seg= &s->segments[segn];
52     segi= &info_segments[segn];
53
54     if (check_clash) {
55       if (seg->tr_updated) {
56         l->ec= safety_problem(l->tran, tloc.segn, "self-collision");
57         return;
58       }
59       if (seg->owned) {
60         if (seg->owner != l->tran) {
61           l->ec= safety_problem(l->tran, tloc.segn, "collision with %s",
62                                 info_trains[seg->owner].pname);
63           return;
64         }
65         seg_clear_stale(seg);
66       }
67     }
68     
69     seg->owned= 1;
70     seg->tr_backwards= tloc.backwards ^ backwards;
71     seg->tr_updated= 1;
72     seg->owner= l->tran;
73
74     train_inverted_here= seg->seg_inverted ^ seg->tr_backwards;
75     invert_likehere= &l->invert_count[train_inverted_here];
76
77     if (segi->invertible) {
78       if (*invert_likehere >= 0)
79         (*invert_likehere)++;
80     } else {
81       if (*invert_likehere < 0) {
82         l->ec= safety_problem(l->tran, NOTA(Segment), "train requires"
83                               " noninvertible segments with opposite polarity:"
84                               " @%s, @%s", segi->pname,
85                               info_segments[l->invert_forcer].pname);
86         return;
87       }
88       assert(!seg->seg_inverted);
89       (*invert_likehere)++;
90       l->invert_count[!train_inverted_here]= -1;
91       l->invert_forcer= segn;
92     }
93
94     dist_until= (overall - remain) - advance;
95     time_until= !speed ? 0 : SPEED_CALC_TIME(speed, dist_until, DOWN);
96     *(check_clash ? &seg->until_here : &seg->until_detect)= time_until;
97
98     if (!remain) break;
99     trackloc_further(&tloc, &remain);
100   }
101 }
102
103 static void lay_train_inversions(LayTrainState *l) {
104   State *s= &safety_state;
105   SegmentNum segn;
106   SegmentState *seg;
107   const SegmentInfo *segi;
108   int train_be_inverted, seg_be_inverted;
109  
110   if (l->ec) return;
111
112   train_be_inverted= l->invert_count[1] > l->invert_count[0];
113   assert(l->invert_count[train_be_inverted] >= 0);
114
115   actual_inversions_start();
116   
117   for (segn=0, seg=s->segments, segi=info_segments;
118        segn <= NUM_SEGMENTS;
119        segn++, seg++) {
120     if (!seg->tr_updated) continue;
121     assert(seg->owner == l->tran);
122     seg_be_inverted= train_be_inverted ^ seg->tr_backwards;
123     assert(!(seg_be_inverted && !segi->invertible));
124     seg->seg_inverted= seg_be_inverted;
125     actual_inversions_segment(segn);
126   }
127   actual_inversions_done();
128 }
129
130 static void lay_train_done(LayTrainState *l) {
131   State *s= &safety_state;
132   SegmentNum segn;
133   SegmentState *seg;
134   
135   for (segn=0, seg=s->segments;
136        segn <= NUM_SEGMENTS;
137        segn++, seg++) {
138     if (seg->owner == l->tran) {
139       if (!seg->tr_updated) seg_clear_stale(seg);
140       seg->tr_updated= 0;
141     }
142     assert(!seg->tr_updated);
143     assert(seg->until_detect >= seg->until_here);
144   }
145 }
146
147 static ErrorCode lay_train(TrainNum tran, long added_slop) {
148   State *s= &safety_state;
149   TrainState *tra= &s->trains[tran];
150   const TrainInfo *trai= &info_trains[tran];
151   SegmentNum segn;
152   SegmentState *seg;
153   TrackLocation tloc;
154   long head, headslop, tail, taildet;
155   LayTrainState l;
156
157   segn= tra->foredetect;
158   seg= &s->segments[segn];
159
160   tloc.segn= segn;
161   tloc.into= tra->maxinto;
162   tloc.backwards= seg->tr_backwards ^ tra->backwards;
163
164   l.tran= tran;
165   l.ec= 0;
166   l.invert_count[0]= l.invert_count[1]= 0;
167   
168   head= tra->backwards ? trai->tail : trai->head;
169   headslop= head + added_slop;
170
171   /* 1st pass:
172    * update actual train location (train's head and future locations).
173    *
174    * 2nd pass:
175    * update detection (future locations) only.
176    *
177    * 3rd pass:
178    * train location (current location and tail)
179    *
180    * Clash checking is done in passes 1 and 3, including checking
181    * whether the train clashes with itself (ie, fail if we find
182    * segment with same train and tr_updated set).
183    */
184   lay_train_pass(&l, tloc,  headslop, tra->speed, 0, 1);
185   lay_train_pass(&l, tloc,  0,        tra->speed, 0, 0);
186
187   trackloc_reverse(&tloc);
188   seg->tr_updated= 0; /* we're about to do this one again */
189   tail= tra->backwards ? trai->head : trai->tail;
190   taildet= trai->detectable + tail;
191
192   lay_train_pass(&l, tloc, taildet,   0,          1, 1);
193
194   lay_train_inversions(&l);
195   lay_train_done(&l);
196
197   return l.ec;
198 }
199
200 static void setspeed(TrainNum tran, Speed newspeed) {
201   /* does not lay the train, caller must do that (or have done it,
202    * in which case they should already have set tra->speed). */
203   State *s= &safety_state;
204   TrainState *tra= &s->trains[tran];
205
206   tra->speed= newspeed;
207   actual_setspeed(tran);
208   speedmanager_speedchange_notify(tran);
209 }
210
211 void safety_notify_detection(SegmentNum segn) {
212   State *s= &safety_state;
213   SegmentState *seg= &s->segments[segn];
214   TrainNum tran= seg->owner;
215   TrainState *tra= &s->trains[tran];
216   ErrorCode ec;
217   TrackLocation tloc;
218
219   if (!seg->owned)
220     safety_panic(NOTA(Train), segn, "unexpected detection");
221
222   if (!seg->until_detect)
223     return;
224
225   tloc.segn= segn;
226   tloc.into= 0;
227   tloc.backwards= seg->tr_backwards;
228
229   tra->foredetect= segn;
230   tra->maxinto= trackloc_remaininseg(&tloc);
231   
232   if (seg->cm_autostop) {
233     if (tra->speed < AUTOSTOP_MAXSPEED &&
234         tra->maxinto > AUTOSTOP_UNCERTAINTY)
235       /* At some point we may need to allow more slop when the
236        * next segment is points than when this is the last segment
237        * (ie, just buffers next). */
238       tra->maxinto= AUTOSTOP_UNCERTAINTY;
239
240     seg->cm_autostop= 0;
241     setspeed(tran, 0);
242   }
243   tra->uncertainty= tra->maxinto;
244     
245   ec= lay_train(tran, 0);
246   if (ec) {
247     logmsg(ec, tran, segn, "emergency stop");
248     safety_emergencystop(tran);
249   }
250 }
251
252 void safety_emergencystop(TrainNum tran) {
253   State *s= &safety_state;
254   ErrorCode ec;
255   TrainState *tra= &s->trains[tran];
256
257   tra->speed= 0;
258   actual_emergencystop(tran);
259   ec= lay_train(tran, ESTOP_UNCERTAINTY);
260   if (ec) safety_panic(tran, NOTA(Segment), "emergency stop forbidden!");
261   speedmanager_speedchange_notify(tran);
262 }
263
264 void safety_requestspeed(TrainNum tran, long newspeed) {
265   State *s= &safety_state;
266   long oldspeed;
267   TrainState *tra= &s->trains[tran];
268   ErrorCode ec;
269
270   oldspeed= tra->speed;
271   tra->speed= newspeed;
272
273   ec= lay_train(tran, 0);
274
275   if (ec) {
276     if (oldspeed && oldspeed < newspeed) {
277       logmsg(ec, tran, NOTA(Segment), "countermanded acceleration"
278              " from %ld to %ld", oldspeed, newspeed);
279     } else if (oldspeed) {
280       safety_panic(tran, NOTA(Segment), "deceleration forbidden!"
281                    " (from %ld to %ld", oldspeed, newspeed);
282     } else {
283       logmsg(ec, tran, NOTA(Segment), "countermanded motion start");
284     }
285     setspeed(tran, oldspeed);
286     ec= lay_train(tran, 0);
287     if (ec)
288       safety_panic(tran, NOTA(Segment), "countermanding"
289                    " speed change insufficient!");
290     return;
291   }
292
293   setspeed(tran, newspeed);
294 }
295                              
296
297 int main(void) {
298   printf("%d\n",(int)sizeof(State));
299   return 0;
300 }