chiark / gitweb /
83b570c0fd6d05e8363881f0c81f8ab7ef84708a
[trains.git] / hostside / safety.h
1 /**/
2
3 #ifndef SAFETY_H
4 #define SAFETY_H
5
6 #include <stdarg.h>
7
8 #include "../layout/layout-data.h"
9 #include "layoutinfo.h"
10 #include "errorcodes.h"
11
12 /*========== more basic types etc. ==========*/
13
14 typedef long TimeInterval; /*ms*/
15
16 /*---------- units and scales ----------*/
17
18 /*
19  * Distances are in mm.
20  * Times are generally in s.
21  */
22
23 /*========== state of the layout ==========*/
24
25 typedef struct MovPosChange MovPosChange;
26
27 #define SPEEDSTEPS 126
28
29 typedef struct {
30   float speed; /* applies to all speeds up to and including this one */
31   Distance xs; /* max stopping distance */
32   TimeInterval ts; /* max stopping time */
33 } SpeedRange;
34
35 struct Train {
36   /* Configuration (excluding speed curve): */
37   char *pname;
38   int addr;
39   Distance head, detectable, tail;
40
41   /* Location: */
42   struct Segment *foredetect;
43   Distance maxinto, uncertainty;
44     /* Front of train's detectable portion (the `reference') is at
45      * most maxinto into foredetect, but may be up to uncertainty
46      * further back (which may put it in a previous segment).  If the
47      * train is stationary it owns the track forwards from the
48      * foremost reference possibility, plus ->head, plus MARGIN_NOSE
49      * (and if it is not stationary it may own more).  It owns the
50      * track backwards from the rearmost reference possibility for
51      * ceil(->detectable * MARGIN_TRAINLENGTH) + ->tail + MARGIN_NOSE.
52      */
53   unsigned
54     backwards:1, /* train is moving backwards wrt its own front and back */
55     autopoint:1, /* set points automatically if point has autopoint too */
56
57   /* Startup resolution (resolve.c): */
58     resolution:2; /* for use by speedmanager */
59
60   int plan_lookahead_nsegs; /* counts from foredetect exclusive */
61
62   struct {
63     int step; /* commanded */
64     TimeoutEvent decel; /* running iff decelerating */
65     double speed; /* iff decerating, at last change; otherwise accurate */
66     RetransmitUrgentNode rn;
67   } speed;
68
69   int n_speedregimes;
70   SpeedRange *speedregimes;
71   float speedcurve[SPEEDSTEPS+1];
72 };
73
74 struct Segment {
75   Train *owner;                                  /* or 0 */
76   Train *home;                                   /* or 0 */
77   unsigned
78     tr_backwards:1, /* train's motion is (would be) backwards wrt track */
79     ho_backwards:1, /* home train has its front and rear backwards wrt track */
80     autopoint:1, /* set points automatically if train has autopoint too */
81     seg_inverted:1, /* polarity is inverted */
82     det_ignore:1, det_expected:1, /* safety.c */
83     moving:1, /* feature(s) have been told to change */
84     mark0:1,mark1:1,mark2:1,mark3:1, /* for temporary private uses */
85     res_movposset:1, /* we have set the position so it is known good */
86     res_detect:1, /* detection noticed here during resolution */
87     detect_actual:1, /* last detection state received */
88     detect_reported:1; /* detection state reported to parent */
89   int detect_flaps;
90   MovPosComb movposcomb; /* -1 means not known or moving */
91   MovPosChange *motion; /* if ->moving, owned by movpos, otherwise by safety */
92   TimeInterval until;            /* } for use by safety.c */
93   MovPosChange *motion_newplan;  /* }  during predict     */
94   const SegmentInfo *i;
95 };
96
97 /*========== embed.c ==========*/
98 /* surrounds the algorithms and machinery in a program: logging, error
99  * handling, arg parsing, etc.
100  */
101
102 void safety_vpanic(Train *tra, Segment *seg, const char *fmt, va_list al)
103      __attribute__((format(printf,3,0),noreturn));
104 void safety_panic(Train *tra, Segment *seg, const char *fmt,...)
105      __attribute__((format(printf,3,4),noreturn));
106
107 /*========== safety.c ==========*/
108 /*
109  * safety.c is responsible for ensuring that things don't go
110  * physically wrong (eg, collisions, derailments, short circuits,
111  * etc.).
112  */
113
114 typedef void PredictionProblemCallback(Train *tra, Segment *seg /* may be 0 */,
115                                        void *pu, const char *message);
116
117 ErrorCode safety_checktrain(Train *tra,
118                             PredictionProblemCallback *ppc, void *ppcu);
119
120 void safety_notify_detection(Segment *seg);
121   /* Called by startup.c when new train detection occurs in state Run. */
122
123 ErrorCode safety_movposchange(Segment *seg, MovPosComb comb,
124      int allow_queueing, PredictionProblemCallback *ppc, void *ppcu);
125   /* If this succeeds, caller need not call movpos_change.
126    * allow_queueing==0  means we only allow unowned segments to move
127    *               ==1  allows changing a segment the train is approaching
128    *               ==2  allows queueing a change for when the train has left
129    */
130
131 ErrorCode safety_setdirection(Train *tra, int backwards,
132                               PredictionProblemCallback *ppc, void *ppcu);
133
134 #define PREDF_JUSTDET 001u
135    /* we have just detected the train entering its foredetect so we
136     * can subtract the uncertainty from the stopping distance */
137 #define PREDF_OLDPLAN 002u
138    /* the old plan is necessarily sufficient as we are not intending
139     * to accelerate, change a movpos, etc. */
140 #define PREDF_INITQUEST 004u
141    /* the initial location is questionable (ie, the initial present
142     * marking is allowed to fail) */
143
144 typedef struct {
145   unsigned stopping:1;
146   double max_speed_estimate;
147   Distance stopping_distance;
148 } SpeedInfo;
149
150 ErrorCode predict(Train *tra, struct timeval tnow, unsigned flags,
151                   Segment *desire_move, int desire_movposcomb,
152                   const SpeedInfo *speed_info /* 0: use speedmgr */,
153                   PredictionProblemCallback *ppc, void *ppcu);
154   /* Lower-level interface for speedmanager etc.
155    * Caller must call this with different situations until it succeeds!
156    * Caller may pass ppc=0 and ppcu=(char*)"some context" to
157    * cause safety_panic if it fails.
158    */
159
160 void report_train_position(Train *tra);
161 void report_train_ownerships(Train *tra, Segment *furthest,
162                              int always_use_motions);
163   /* ... for use by things which update these only, which
164    * is basically safety.c and resolve.c */
165
166 void safety_abandon_run(void);
167
168 /*========== movpos.c ==========*/
169 /*
170  * movpos.c manages the CDU and points and other moveable features.
171  * Only safety.c should request changes.
172  */
173
174 ErrorCode
175 movpos_change(Segment *tomove, MovPosComb target,
176               int maxdelay_ms, MovPosChange *reservation);
177   /* If segment has already been requested to change, an attempt is
178    * made to replace that change with the new request; if this is not
179    * successful then the existing change will still happen.
180    *
181    * reservation should be 0, or the results of movpos_reserve
182    * on the same move.  It is always consumed, even on error.
183    *
184    * One of the following must be true of tomove on entry:
185    *   moving==1   motion already owned by movpos, see above
186    *   motion==0   motion empty, will become owned by movpos
187    *
188    * On successful exit, tomove->moving=1 and tomove->motion!=0; from
189    * then on until ->moving becomes 0, movposcomb may not reflect the
190    * real physical state of the layout; instead it gives only
191    * information about the target configuration.  (Choreographers are
192    * allowed to know implementation details and may know what
193    * movposcomb means for certain segments depending on the movement
194    * requested.)
195    *
196    * If a reservation is supplied, maxdelay_ms may be -1 to use the
197    * same value as was given to movpos_reserve, or if no reservation
198    * given, to say we don't care.
199    */
200
201 ErrorCode movpos_findcomb_bysegs(Segment *back, Segment *move, Segment *fwd,
202                                  MovPosComb startpoint, MovPosComb *chosen_r);
203   /* Looks for a movposcomb of move where the adjacent segment in one
204    * direction is back and the other fwd.  back and fwd may be 0 if we
205    * don't care (and must be if there is no track in that direction.
206    * It is immaterial which is back and which fwd.
207    *
208    * The returned movposcomb is the `best' one which is the one
209    * closest to the starting point (and if that is not unique, the
210    * lowest-numbered).
211    *
212    * startpoint=-1 means starting with current state.  chosen_r may be 0.
213    */
214
215 ErrorCode
216 movpos_reserve(Segment *move, int maxdelay_ms, MovPosChange **res_r,
217                MovPosComb target, MovPosComb startpoint /*as for findcomb*/);
218   /* The resulting MovPosChange is a reservation which is guaranteed
219    * to be useable successfully later for any movpos_change for the
220    * same move, target and startpoint and a greater or equal
221    * maxdelay_ms.  On successful exit *res_r is set to non-0.
222    *
223    * On transition out of Sta_Run, all unconfirmed reservations must
224    * be unreserved before points_all_abandon is called (see
225    * startup.c);
226    */
227
228 void movpos_unreserve(MovPosChange *reservation /* 0 ok */);
229
230 MovPosComb movpos_change_intent(MovPosChange *chg);
231   /* Returns the target value supplied to _reserve or _change */
232
233  /* Error returns from movpos calls
234   *
235   *  EC_Invalid         there is no movposcomb of tomove matching back/fwd
236   *  EC_MovFeatTooLate  maxdelay_ms could not be met
237   *  EC_MovFeatReservationInapplicable
238   *                     reservation not compatible with change request
239   *                       and/or current situation
240   */
241
242 const char *movpos_pname(const Segment *move, MovPosComb poscomb);
243
244 MovPosComb movposcomb_feature_update(const MovFeatInfo *mfi,
245                                      MovPosComb startpoint,
246                                      int featpos);
247   /* Returns movposcomb representing a changed combination of positions;
248    * pure function. */
249
250 /*========== speed.c ==========*/
251
252 ErrorCode speedmanager_speedchange_request(Train *tra, int step,
253                            PredictionProblemCallback *ppc, void *ppcu);
254   /* ppc and ppcu as for predict_confirm */
255
256 void speedmanager_safety_stop(Train *tra, struct timeval tnow,
257                               unsigned predictflags);
258   /* Ignores value of PREDF_OLDPLAN */
259 void speedmanager_reset_train(Train *tra);
260
261 void speedmanager_getinfo(Train *tra, struct timeval tnow, SpeedInfo*);
262 int speedmanager_stopped(Train *tra);
263
264 void actual_speed(Train *tra, int step);
265
266 void speedmanager_init(void);
267
268 /*========== actual.c ==========*/
269 /* actual.c should only be called from safety.c.
270  * It is responsible for communicating with the PICs, including
271  * repeating the NMRA commands and redacting detection information.
272  *
273  * In general, when State information is shared between actual.c
274  * and safety.c, safety.c is responsible for modifying State, and
275  * will then call an actual_... function to notify the change.
276  */
277
278 void actual_setspeed(Train *tra);
279 void actual_emergencystop(Train *tra);
280
281 void actual_inversions_start(void);
282 void actual_inversions_segment(Segment *seg);
283 void actual_inversions_done(void);
284   /* safety.c will call these in this order: first start, then segment
285    * for 0 or more segments (whose s.segments[segn].seg_inverted may
286    * or may not have changed), and finally done.  At done, the PICs
287    * should be told to (de)invert the segments as specified.  */
288   
289 /*
290  * Entrypoints are:                  Called from, and as a result of:
291  *   actual_setspeed                    safety.c
292  *   actual_emergencystop               safety.c
293  */
294
295 /*========== utils.c ==========*/
296
297 typedef struct TrackLocation TrackLocation;
298 struct TrackLocation { /* transparent, and manipulable by trackloc_... fns */
299   Segment *seg; /* current segment */
300   Distance remain; /* distance from end of segment as we look at it */
301   unsigned backwards:1; /* if 1, into is positive and measured from end */
302 };
303
304 void trackloc_set_maxinto(TrackLocation *tloc, Segment *seg, int backwards);
305
306 typedef struct TrackAdvanceContext {
307   /* all set by caller, only distance modified by trackloc: */
308   int distance;
309   /* All event callbacks are optional; if not supplied we pretend
310    * that they were a no-op which returned 0. */
311   int (*nextseg)(TrackLocation *t, struct TrackAdvanceContext *c,
312                  MovPosComb *mpc_io, const TrackLocation *before);
313     /* On entry *mpc_io is from next->movposcomb.  nextseg may modify
314      * it if it feels like it in which case the modified value will
315      * be used instead.  If on 0-return it is still -1, getmovpos is used.
316      * t->remain is not valid on entry and should not be touched.
317      * Called once right at the start with before==0. */
318   int (*getmovpos)(TrackLocation *t, struct TrackAdvanceContext *c,
319                    MovPosComb *use_io);
320     /* Will be called                            *use_io on entry is
321      *  - at the start of trackloc_advance         value from segment
322      *  - after nextseg                            value left by nextseg
323      *  - by trackloc_getlink                      getlink's mpc argument
324      *  - by interfering_segment                   base->movposcomb
325      * May modify *use_io if it wishes.  It is OK to leave it as
326      * -1 in which case advance will return whatever getmovpos did
327      * (even if 0).  When called by interfering_segment, non-0 returns
328      * will be treated as an indication that the movposcomb is unknown.
329      * On entry t->remain is invalid; it should not be modified. */
330   int (*trackend)(TrackLocation *t, struct TrackAdvanceContext *c);
331     /* trackloc_advance stops even if this returns 0. */
332   void *u;
333 } TrackAdvanceContext;
334
335 int trackloc_getlink(TrackLocation *t, TrackAdvanceContext *c,
336                      const SegPosCombInfo **pci_r /* 0 ok */,
337                      const SegmentLinkInfo **link_r /* 0 ok */,
338                      MovPosComb mpc /* -1: get from segment */);
339   /* Of c, only c->getmovpos is used.  t->remain is not used.
340    * c may be 0 which works just like c->getmovpos==0. */
341
342 Segment *segment_interferer(Segment *base);
343   /* Returns the segment which may interfere with base, if any. */
344
345 ErrorCode segment_interferer_does(TrackAdvanceContext *c, Segment *base,
346                                   Segment *interferer, int *does_r);
347   /* Checks whether it interferes with base.
348    * Return is 0 or the error value from c->getmovpos.
349    * *does_r is set (only) on successful return, 
350    * c is used as for trackloc_getlink.  If c->getmovpos is 0
351    * the segments are taken to interfere if they would in any movposcomb.
352    * interferer may be 0 in which case succeeds, setting *does_r=0.
353    */
354
355 Segment *segment_interferes_simple(TrackAdvanceContext *c, Segment *base);
356   /* Combines segment_interferer and segment_interferer_does.
357    * c->getmovpos MUST NOT return errors; we abort if it does. */
358
359 int trackloc_advance(TrackLocation *t, TrackAdvanceContext *c);
360   /* Advances t by dist until distance becomes 0, some callback
361    * returns non-0, or sometimes 0 if we cannot continue and the
362    * relevant callback is missing or returned 0.  If we exhausted
363    * c->distance then *t is the end of the counted distance, on the
364    * earlier of the two segments if there is a choice.  Otherwise *t
365    * is the last location on the last sensible segment.  (Ie, nextseg
366    * and getmovpos get a putative new segment and if they don't work
367    * trackloc_advance rewinds.)  In any case c->distance is updated by
368    * the distance successfully traversed. */
369
370 int trackloc_reverse_exact(TrackLocation *t, TrackAdvanceContext *c);
371 int trackloc_set_exactinto(TrackLocation *t, TrackAdvanceContext *c,
372                            Segment *seg, int backwards, int into);
373   /* c is used just as for trackloc_getlink.
374    * If we can't determine the movposcombs we call abort. */
375
376 #define TL_DIST_INF (INT_MAX/16)
377
378 /*========== useful macros and declarations ==========*/
379
380 /*---------- looping over trains and segments ----------*/
381
382 #define SEGMENT_ITERVARS(seg)                   \
383   SegmentNum seg##n;                            \
384   Segment *seg;                                 \
385   const SegmentInfo *seg##i
386
387 #define TRAIN_ITERVARS(tra)                     \
388   Train *tra;                                   \
389   int tra##n
390
391 #define FOR_SEGMENT(seg, initx, stepx)                  \
392   for (seg##n=0, seg=segments, seg##i=info_segments;    \
393        seg##n < NUM_SEGMENTS;                           \
394        seg##n++, seg++, seg##i++, stepx)
395
396 #define FOR_TRAIN(tra, initx, stepx)            \
397   for (tra##n=0, tra=trains, initx;             \
398        tra##n < n_trains;                       \
399        tra##n++, tra++, stepx)
400
401 #define SEG_IV SEGMENT_ITERVARS(seg)
402 #define FOR_SEG FOR_SEGMENT(seg,(void)0,(void)0)
403   
404 #define TRA_IV TRAIN_ITERVARS(tra)
405 #define FOR_TRA FOR_TRAIN(tra,(void)0,(void)0)
406
407 /*---------- calculations with fixed point speeds ----------*/
408
409 #define SPEED_CALC_TIME(speed,dist,round) ((dist) / ((speed)+1e-6))
410 #define SPEED_CALC_DIST(speed,time,round) ((time) * (speed))
411
412 /*---------- safety margin parameters ----------*/
413
414 #define MARGIN_NOSE 6 /*mm*/ /* physical spare at each end of train */
415 #define MARGIN_TAIL 6 /*mm*/ /* spare when in motion only */
416 #define MARGIN_SPEED 1.2 /*ratio*/
417 #define MARGIN_AUTOPOINTTIME 500 /*ms*/
418 #define UNMARGIN_ROUNDING 1e-4 /* mm/ms; for 1s, leads to max err of 100um */
419 #define MARGIN_TRAINLENGTH 1.05
420
421 #define MARGIN_STOPTIME 240 /*ms*/
422 #define MARGIN_POLARISETIME 120 /*ms*/
423
424 #define UNMARGIN_WATCHDOG_16 7 /* 16ms units */ 
425 #define UNMARGIN_WATCHDOG (UNMARGIN_WATCHDOG_16*16) /*ms*/
426                                   /* should be < MARGIN_POLARISETIME */
427 #define MARGIN_WATCHDOG 40
428
429 #endif /*SAFETY_H*/