#ifndef SAFETY_H
#define SAFETY_H
-typedef unsigned short TrainNum;
-typedef unsigned short SegmentNum;
-typedef unsigned short LocationNum;
-typedef short TimeInterval;
-typedef short Distance;
-typedef char Speed; /* non-negative, units of 4mm/s */
+#include <stdarg.h>
-typedef struct {
- SegmentNum foredetect;
- unsigned
- justarrived:1, /* is stopped just after triggering foredetect segment */
- backwards:1; /* train is moving backwards wrt its own front and back */
- Speed speed;
-} TrainState;
+#include "../layout/layout-data.h"
+#include "layoutinfo.h"
+#include "errorcodes.h"
+
+/*========== more basic types etc. ==========*/
+
+typedef short TimeInterval; /*ms*/
+
+/*---------- units and scales ----------*/
+
+/*
+ * Distances are in mm.
+ * Times are in ms.
+ * Speeds are in fixed point: unit is 1/SPEED_UNIT m/s
+ *
+ * To calculate with speeds and times without using floating point
+ * it turns out that we can cope with distances of up to
+ * 2^(31-SPEED_SHIFT) mm
+ */
+
+#define SPEED_SHIFT 16 /* units of 15um/s, max distance 2^15mm =~ 32m */
+#define SPEED_UNIT (1L<<SPEED_SHIFT) /* ms/mm */
+
+/*========== state of the layout ==========*/
+
+typedef struct MovPosChange MovPosChange;
typedef struct {
+ int step;
+ long speed;
+ TimeInterval upwait, downwait; /* between this insn and next one */
+} SpeedCurveEntry;
+
+struct Train {
+ /* Configuration (excluding speed curve): */
+ char *pname;
+ int addr;
+ Distance head, detectable, tail;
+
+ /* Location: */
+ struct Segment *foredetect; /* train's detectable part is at most maxinto */
+ Distance maxinto, uncertainty; /* into foredetect but train may be */
+ unsigned /* uncertainty less far advanced */
+ backwards:1, /* train is moving backwards wrt its own front and back */
+
+ /* Speed: */
+ estopping:1, /* set and cleared by speed.c */
+
+ /* Startup resolution (resolve.c): */
+ resolution:2;
+ Speed speed; /* when accelerating/decelerating, is maximum at this moment */
+
+ struct {
+ int target; /* index into curve */
+ int commanded; /* when ac-/decel, eq last value xmitted */
+ TimeoutEvent more;
+ RetransmitUrgentNode rn;
+
+ /* Configuration for acceleration: */
+ SpeedCurveEntry *curve;
+ int curvesz;
+ } accel;
+};
+
+struct Segment {
+ Train *owner; /* or 0 */
+ Train *home; /* or 0 */
unsigned
- owned:1, /* this track segment is reserved for a train */
- owner_backwards:1, /* train's motion is (would be) backwards wrt track */
- pt_sideways:1, /* points are set to `alternative'. (no points?: 0) */
- autostop:1, /* owning train is slow and wants to stop on detection */
- tr_updated:1; /* for use by safety_train_changed etc.; otherwise 0 */
- TimeInterval until_here, /* ) nonnegative; */ /* ) always valid but */
- until_detect; /* ) 0 if already */ /* ) only meaningful */
- TrainNum owner; /* ) iff owned */
- /*polarity?*/
-} SegmentState;
+ tr_backwards:1, /* train's motion is (would be) backwards wrt track */
+ ho_backwards:1, /* home train has its front and rear backwards wrt track */
+ cm_autostop:1, /* train should stop on detection */
+ seg_inverted:1, /* polarity is inverted */
+ tr_updated:1, /* for use by safety.c:lay_train etc.; otherwise 0 */
+ res_detect:1; /* detection noticed here during resolution */
+ TimeInterval until_here, /* ) nonnegative; */ /* ) always valid but */
+ until_detect; /* ) 0 if already */ /* ) meaningful iff owner */
+ MovPosComb movposcomb; /* -1 means not known */
+ MovPosChange *moving; /* non-0 iff feature(s) have been told to change */
+ const SegmentInfo *i;
+};
-typedef struct {
- unsigned next_rev:1;
- SegmentNum next;
- SegmentNum clashing;
- Distance dist;
-} SegmentLinkInfo;
+extern int n_trains;
+extern Train *trains;
+extern Segment *segments;
-typedef struct {
- SegmentLink backwards, forwards, sideways;
-} SegmentInfo;
+/*========== embed.c ==========*/
+/* surrounds the algorithms and machinery in a program: logging, error
+ * handling, arg parsing, etc.
+ */
-typedef struct {
- Speed maxspeed;
- Distance tail, detectable, head;
-} TrainInfo;
+void vlogmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
+ const char *fmt, va_list al) __attribute__((format(printf,4,0)));
+void logmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
+ const char *fmt, ...) __attribute__((format(printf,4,5)));
-extern const TrainInfo info_train[NUM_TRAINS];
-extern const SegmentInfo info_segment[NUM_SEGMENTS];
+void safety_vpanic(Train *tra, Segment *seg, const char *fmt, va_list al)
+ __attribute__((format(printf,3,0),noreturn));
+void safety_panic(Train *tra, Segment *seg, const char *fmt,...)
+ __attribute__((format(printf,3,4),noreturn));
-typedef struct {
- TrainState tras[NUM_TRAINS];
- SegmentState segs[NUM_SEGMENTS];
-} State;
+ErrorCode safety_problem(Train *tra, Segment *seg, const char *fmt, ...)
+ __attribute__((format(printf,3,4)));
+ /* simple wrapper around vlogmsg; implies and returns EC_Safety */
+
+/*========== safety.c ==========*/
+/*
+ * safety.c is responsible for ensuring that things don't go
+ * physically wrong (eg, collisions, derailments, short circuits,
+ * etc.).
+ */
+
+void safety_emergencystop(Train*);
+ /* Callable directly in response to application command. */
+
+ErrorCode safety_requestspeed(Train* tra, long newspeed);
+ /* To be called only by the speed manager, thus indirectly from
+ * user request. Any error will have been logged. On success,
+ * ->speed has been updated. Speed manager is responsible for
+ * calling actual_setspeed.
+ */
+
+void safety_setdirection(Train* tra, int sense_fixme_define_this_properly);
+
+void safety_notify_detection(Segment *seg);
+ /* Called by startup.c when new train detection occurs in state Run. */
+
+/*========== movpos.c ==========*/
+/*
+ * movpos.c manages the CDU and points and other moveable features.
+ * Only safety.c should request changes.
+ */
+
+ErrorCode
+movpos_change(Segment *back, Segment *tomove, Segment *fwd,
+ int maxdelay_ms, MovPosChange *reservation);
+ /* back and fwd may be 0 if we don't care (and must be if there is
+ * no track in that direction. It is immaterial which is back and
+ * which fwd.
+ *
+ * If segment has already been requested to change, an attempt is
+ * made to replace that change with the new request; if this is not
+ * successful then the existing change will still happen.
+ *
+ * reservation should be 0, or the results of movpos_reservechange
+ * on the same move. It is always consumed, even on error.
+ *
+ * On successful exit, tomove->moving is set non-0; from then on
+ * until ->moving becomes 0, movposcomb may not reflect the real
+ * physical state of the layout; instead it gives only information
+ * about the target configuration. (Choreographers are allowed to
+ * know implementation details and may know what movposcomb means
+ * for certain segments depending on the movement requested.)
+ *
+ * Returns EC_Invalid if there is no movposcomb of tomove matching
+ * back/fwd, or EC_MovFeatTooLate if the maxdelay_ms could not be
+ * met because of lack of ability to change points.
+ */
+
+ErrorCode
+movpos_reserve(Segment *move, int maxdelay_ms, MovPosChange **res_r);
+ /* Returns EC_MovFeatTooLate if the maxdelay_ms could not be met.
+ * The resulting MovPosChange is a reservation which is guaranteed
+ * to be useable successfully later for any movpos_change for
+ * the same move and a greater or equal maxdelay_ms. On successful
+ * exit *res_r is set to non-0. On transition out of Sta_Run,
+ * all unconfirmed reservations must be unreserved before
+ * points_all_abandon is called (see startup.c);
+ */
+
+void movpos_unreserve(MovPosChange *reservation);
+
+/*========== speedmgr.c ==========*/
+
+void speedmanager_speedchange_request(Train *tra, long speed);
+ /* Callable directly in response to application command.
+ * speed may be LONG_MAX to mean maximum permitted.
+ */
+
+void speedmanager_emergencystop(Train *tra);
+void speedmanager_autostop(Train *tra);
+ /* These are responsible for calling actual_setspeed.
+ *
+ * After speedmanager_autostop, ->speed will have been updated to a
+ * new desired speed. If it is 0 the train was going slowly and has
+ * been instructed to stop right now.
+ */
+
+void speedmanager_reset_train(Train *tra);
+
+/*========== actual.c ==========*/
+/* actual.c should only be called from safety.c.
+ * It is responsible for communicating with the PICs, including
+ * repeating the NMRA commands and redacting detection information.
+ *
+ * In general, when State information is shared between actual.c
+ * and safety.c, safety.c is responsible for modifying State, and
+ * will then call an actual_... function to notify the change.
+ */
+
+void actual_setspeed(Train *tra);
+void actual_emergencystop(Train *tra);
+
+void actual_inversions_start(void);
+void actual_inversions_segment(Segment *seg);
+void actual_inversions_done(void);
+ /* safety.c will call these in this order: first start, then segment
+ * for 0 or more segments (whose s.segments[segn].seg_inverted may
+ * or may not have changed), and finally done. At done, the PICs
+ * should be told to (de)invert the segments as specified. */
+
+/*
+ * Entrypoints are: Called from, and as a result of:
+ * actual_setspeed safety.c
+ * actual_emergencystop safety.c
+ */
+
+/*========== utils.c ==========*/
+
+typedef struct TrackLocation TrackLocation;
+struct TrackLocation { /* transparent, and manipulable by trackloc_... fns */
+ Segment *seg; /* current segment */
+ long into; /* distance from start of segment */
+ unsigned backwards:1; /* if 1, into is positive and measured from end */
+};
+
+long trackloc_remaininseg(const TrackLocation *tloc);
+ /* Returns dist that tloc can advance before it goes into next segment. */
+
+void trackloc_further(TrackLocation *tloc, long *remain_io);
+ /* Advances tloc, decrementing *remain_io, until either
+ * *remain_io becomes zero, or tloc->segn changes. */
+
+void trackloc_reverse(TrackLocation *tloc);
+ /* Reverses tloc without changing its actual location. */
+
+const SegPosCombInfo *trackloc_segposcomb(const TrackLocation *tloc);
+const SegmentLinkInfo *trackloc_segmentlink(const TrackLocation *tloc,
+ const SegPosCombInfo *pci,
+ unsigned far);
+
+/*========== useful macros and declarations ==========*/
+
+/*---------- looping over trains and segments ----------*/
+
+#define SEGMENT_ITERVARS(seg) \
+ SegmentNum seg##n; \
+ Segment *seg; \
+ const SegmentInfo *seg##i
+
+#define TRAIN_ITERVARS(tra) \
+ Train *tra; \
+ int tra##n
+
+#define FOR_SEGMENT(seg, initx, stepx) \
+ for (seg##n=0, seg=segments, seg##i=info_segments; \
+ seg##n < NUM_SEGMENTS; \
+ seg##n++, seg++, seg##i++, stepx)
+
+#define FOR_TRAIN(tra, initx, stepx) \
+ for (tra##n=0, tra=trains, initx; \
+ tra##n < n_trains; \
+ tra##n++, tra++, stepx)
+
+#define SEG_IV SEGMENT_ITERVARS(seg)
+#define FOR_SEG FOR_SEGMENT(seg,(void)0,(void)0)
+
+#define TRA_IV TRAIN_ITERVARS(tra)
+#define FOR_TRA FOR_TRAIN(tra,(void)0,(void)0)
+
+/*---------- calculations with fixed point speeds ----------*/
+
+#define DIVIDE_ROUNDING_UP(num,den) (((num) + (den) - 1) / (den))
+#define DIVIDE_ROUNDING_DOWN(num,den) ( (num) / (den))
+
+#define SPEED_CALC_TIME(speed,dist,round) \
+ DIVIDE_ROUNDING_##round((dist) * SPEED_UNIT, (speed))
+
+#define SPEED_CALC_DIST(speed,time,round) \
+ DIVIDE_ROUNDING_##round((time) * (speed), SPEED_UNIT)
+
+/*---------- safety margin parameters ----------*/
+
+#define CLEAR_FORESIGHT_TIME 500 /*ms*/
+#define AUTOSTOP_MAXSPEED ((50 * SPEED_UNIT)/1000) /* 50 mm/s */
+#define AUTOSTOP_UNCERTAINTY 20 /*mm*/
+#define ESTOP_UNCERTAINTY 300 /*mm*/
+#define ESTOP_DEADTIME 2000 /*ms*/
+
+#define SPEED_CLEAR_MULT SPEED_CALC_DIST(1,CLEAR_FORESIGHT_TIME,UP)
#endif /*SAFETY_H*/