selectors.h
errorcodes.h
retransmit-table.h
-record-l.c
+record-l.[ch]
record-y.[ch]
BESSAR=bessar:things/trains-bessar/hostside/.
+SPECIAL_CFLAGS_record-l= $(FLEXCFLAGS)
+
on-bessar: $(TARGETS)
RSYNC_RSH=fsh rsync $(TARGETS) $(BESSAR)
RSYNC_RSH=fsh rsync $^ $(BESSAR)
realtime: realtime.o startup.o cdumgr.o safety.o trackloc.o \
- actual.o dummy-trains.o \
+ speed.o actual.o retransmit.o \
cmdinput.o obc.o eventhelp.o \
+ record.o record-l.o record-y.o \
utils.o serialio.o parseutils.o auproto-pic.o \
+ nmra.o encode.o \
../layout/ours.layout-data.o \
__oop-read-copy.o -loop
$(LINK)
static PicInsn polarityinsn= { { 0x90 }, 0 };
-#define SEG \
- const SegmentInfo *segi= &info_segments[segn];
-
#define OPONBIT_BYTE (polarityinsn.d[bytenum])
#define OPONBIT(body) do{ \
BoardObject bo; \
{ body } \
}while(0)
-int picinsn_polarity_testbit(const PicInsn *pi, SegmentNum segn) {
- SEG;
+int picinsn_polarity_testbit(const PicInsn *pi, const SegmentInfo *segi) {
OPONBIT({
return !!(OPONBIT_BYTE & bitv);
});
polarityinsn.l= (info_maxreverse + 4 + 6) / 7;
}
-void actual_inversions_segment(SegmentNum segn) {
- SEG;
- State *s= &safety_state;
- const SegmentState *seg= &s->segments[segn];
+void actual_inversions_segment(Segment *seg) {
+ const SegmentInfo *segi= seg->i;
OPONBIT({
Byte *insnbyte= &OPONBIT_BYTE;
__attribute__((format(printf,1,0)));
void *mmalloc(size_t sz);
+void *mrealloc(void *old, size_t sz);
char *mstrdupl(const char *s, int l);
char *mstrdup(const char *s);
void mgettimeofday(struct timeval *tv);
+void badusage(const char *why);
+
#define massert(x) ((x) ? (void)0 : diem())
/*---------- from serialio.c ----------*/
/*
*/
+FIXME this should go in a config file
+
#include "realtime.h"
#define NOTA_TRAIN { 0,0,0,0, "internal-error-not-a-train" }
void die_hook(void) { }
void die_vprintf_hook(const char *fmt, va_list al) { }
-static void badusage(const char *why) {
- fprintf(stderr,"bad usage: %s\n",why); exit(8);
-}
-
static void pahex(const char **argv) {
const char *data_string;
char hbuf[3], *ep;
CommandInput cmdi;
-static const char *device;
+static const char *device= "/dev/ttya0";
/*---------- general event handling ----------*/
/*---------- logging etc. ----------*/
-static char *transegn2suffixstring(TrainNum tran, SegmentNum segn) {
- /* Either arg may be NOTA(...), in which case it's not included.
+static char *transegn2suffixstring(Train *tra, Segment *seg) {
+ /* Either arg may be 0, in which case it's not included.
* Result string will be empty, or start with ": "
* Result string is from malloc.
* Never fails.
*/
- const char *tra, *seg;
+ const char *trapn, *segpn;
char *s;
int r;
- seg= SOMEP(segn) ? info_segments[segn].pname : 0;
- tra= SOMEP(tran) ? info_trains[tran].pname : 0;
+ segpn= seg ? seg->i->pname : 0;
+ trapn= tra ? tra->pname : 0;
r= asprintf(&s, "%s%s%s%s%s",
- seg||tra ? ":" : "",
- seg ? " @" : "",
- seg ? seg : "",
- tra ? " " : "",
- tra ? tra : "");
- if (r<0) diee("vasprintf failed in transegn2suffixstring "
- "tran=%lu segn=%lu", (unsigned long)tran, (unsigned long)segn);
+ segpn||trapn ? ":" : "",
+ segpn ? " @" : "",
+ segpn ? segpn : "",
+ trapn ? " " : "",
+ trapn ? trapn : "");
+ if (r<0) diee("vasprintf failed in transegn2suffixstring");
return s;
}
-void vlogmsg(ErrorCode ec, TrainNum tran, SegmentNum segn,
+void vlogmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
const char *fmt, va_list al) {
oprintf(UPO, "message %s ", ec ? errorcodelist[ec] : "info");
ovprintf(UPO,fmt,al);
- if (SOMEP(segn) || SOMEP(tran)) oprintf(UPO, ":");
- if (SOMEP(segn)) oprintf(UPO, " @%s", info_segments[segn].pname);
- if (SOMEP(tran)) oprintf(UPO, " %s", info_trains[tran].pname);
+ if (segi || tra) oprintf(UPO, ":");
+ if (segi) oprintf(UPO, " @%s", segi->pname);
+ if (tra) oprintf(UPO, " %s", tra->pname);
oprintf(UPO, "\n");
}
-void logmsg(ErrorCode ec, TrainNum tran, SegmentNum segn,const char *fmt,...) {
+void logmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
+ const char *fmt,...) {
va_list al;
va_start(al,fmt);
- vlogmsg(ec,tran,segn,fmt,al);
+ vlogmsg(ec,tra,segi,fmt,al);
va_end(al);
}
-void safety_vpanic(TrainNum tran, SegmentNum segn,const char *fmt,va_list al) {
+void safety_vpanic(Train *tra, Segment *seg,const char *fmt,va_list al) {
char *msg, *where;
PicInsn piob;
if (vasprintf(&msg,fmt,al) < 0)
diee("vasprintf failed in safety_vpanic fmt=\"%s\"", fmt);
- where= transegn2suffixstring(tran,segn);
+ where= transegn2suffixstring(tra,seg);
die("fatal safety problem: %s%s", msg, where);
}
-void safety_panic(TrainNum tran, SegmentNum segn, const char *fmt, ...) {
+void safety_panic(Train *tra, Segment *seg, const char *fmt, ...) {
va_list al;
va_start(al,fmt);
- safety_panic(tran, segn, fmt, al);
+ safety_panic(tra, seg, fmt, al);
}
-ErrorCode safety_problem(TrainNum tran, SegmentNum segn, const char *fmt,...) {
+ErrorCode safety_problem(Train *tra, Segment *seg, const char *fmt,...) {
va_list al;
va_start(al,fmt);
- vlogmsg(EC_Safety, tran, segn, fmt, al);
+ vlogmsg(EC_Safety, tra, seg?seg->i:0, fmt, al);
va_end(al);
return EC_Safety;
}
segn < info_nsegments;
segn++, segi++) {
if (!segi->invertible) continue;
- if (!picinsn_polarity_testbit(pi,segn)) continue;
+ if (!picinsn_polarity_testbit(pi,segi)) continue;
oprintf(UPO,"%s%s", delim, segi->pname);
delim= ",";
}
int main(int argc, const char **argv) {
oop_source_sys *sys_events;
+ const char *arg;
int r;
- device= argv[1];
- if (!device) device= "/dev/ttya0";
+ while ((arg=*++argv) && *arg=='-') {
+ arg++;
+ switch (*arg++) {
+ case 's': device= arg; break;
+ default: badusage("unknown option");
+ }
+ }
+
+ records_parse(argv);
sys_events= oop_sys_new(); if (!sys_events) diee("oop_sys_new");
events= oop_sys_source(sys_events); massert(events);
#define REALTIME_H
#include "daemons.h"
-#include "safety.h"
#include "auproto-pic.h"
#include "dliste.h"
#include <errno.h>
#include <assert.h>
#include <stdlib.h>
+#include <limits.h>
+#include <stddef.h>
#include <sys/types.h>
#include <sys/time.h>
+#include "../layout/layout-data.h"
+
+typedef struct Segment Segment;
+typedef struct Train Train;
+
/*---------- from retransmit.c ----------*/
typedef struct RetransmitRelaxedNode RetransmitRelaxedNode;
extern StartupState sta_state;
+/*---------- from/for record.c ----------*/
+
+void records_parse(const char **argv);
+
/*---------- from/for realtime.c ----------*/
void oupicio(const char *dirn, const PicInsnInfo *pii, int objnum);
/*---------- from actual.c ----------*/
-int picinsn_polarity_testbit(const PicInsn *pi, SegmentNum segn);
+int picinsn_polarity_testbit(const PicInsn *pi, const SegmentInfo *segi);
/* this belongs in {au,skel}proto-pic.[ch] really but it's
* more convenient here. */
void abandon_run(void);
+
+#include "safety.h"
+
+
#endif /*REALTIME_H*/
/* -*- fundamental -*- */
%{
-#include "y.tab.h"
+#include "record.h"
%}
%option warn
%option batch
%option noyywrap
-
-%option bison-locations
-%option bison-bridge
+%option yylineno
+%option prefix="record_yy"
+%option header-file="record-l.h"
%{
-#define STR yylval->str= xstrdup(yytext); return
+#define STR record_yylval.name= record_tempzone_strdup(yytext); return
%}
%%
-max-trains { yylval->str= 0; return MAXTRAINS; }
-
train { STR TRAIN; }
seg { STR SEG; }
is { STR IS; }
step { STR STEP; }
end { STR END; }
-[A-Za-z0-9_]+ { STR IDENT; }
+[A-Za-z][A-Za-z0-9_]+ { STR IDENT; }
-[0-9]{0,5} { yylval->num= strtoul(yytext,0,10); return NUM; }
-[0-9]{6} { badrecord("number too long"); }
+[0-9]{0,5} { record_yylval.num= strtoul(yytext,0,10); return NUM; }
+[0-9]{6} { record_yyerror("number too long"); }
-[-+:=~$/] { yylval->str= 0; return yytext[0]; }
+[-+:=~/] { record_yylval.name= 0; return yytext[0]; }
-\#.*\n|\ { yylval->str= 0; return NL; }
+\#.*\n|\n { record_yylval.name= 0; return NL; }
[ \t] { }
-. { badrecord("lexically invalid input"); }
+. { record_yyerror("lexically invalid input"); }
/* -*- fundamental -*- */
%{
-#include "realtime.h"
+#include "record.h"
+#include "record-l.h"
%}
%union {
- char *name;
+ const char *name;
Train *train;
Segment *seg;
int num;
}
%token <name> TRAIN SEG IS AT HAS STEP END IDENT
-%token <name> MAXTRAINS NL
+%token <name> NL
%token <num> NUM
%type <name> ident
%type <train> train
-%type <segment> seg
-%type <num> sign
+%type <seg> seg
+%type <num> backwards
%defines
+%error-verbose
+%name-prefix="record_yy"
%%
file: end
- | line NL file
+ | line NL { record_tempzone_clear(); } file
line: /* empty */
- | MAXTRAINS NUM
- { record_max_trains($2); }
- | TRAIN train AT sign SEG ':' NUM '+' '-' NUM
- { record_train_at($2,$4,$5,$7,$10); }
| TRAIN train IS NUM NUM '+' NUM '+' NUM
- { record_train_is($2,$4,$5,$7,$9); }
+ { if ($2) record_train_is($2,$4,$5,$7,$9);
+ }
+ | TRAIN train AT backwards seg ':' NUM '+' '-' NUM
+ { if ($2) record_train_at($2,$4,$5,$7,$10);
+ }
| TRAIN train STEP NUM '=' NUM NUM '/' NUM
- { record_train_step($2,$4,$6,$7,$9); }
- | SEG seg HAS sign train
- { record_seg_has($2,$4,$5); }
- | SEG seg HAS '$'
- { record_seg_has($2,0,0); }
- | SEG seg AT NUM
- { record_seg_at($2,$4); }
-
-sign: /* empty */ { return 1; }
- | '-' { return -1; }
+ { if (!trains) record_train_step_count();
+ else if ($2) record_train_step($2,$4,$6,$7,$9);
+ }
+ | SEG seg HAS backwards train
+ { if ($2 && $5) record_seg_has($2,$4,$5);
+ }
+ | SEG seg AT ident
+ { if ($2) record_seg_at($2,$4);
+ }
+
+backwards: /* empty */ { return 0; }
+ | '-' { return 1; }
ident: IDENT | TRAIN | SEG | IS | AT | STEP | END
-seg: ident { $$= pname2seg($1); }
-train: ident { $$= pname2train($1); }
+seg: ident { $$= record_pname2seg($1); }
+train: ident { $$= record_pname2train($1); }
end: END NL
* seg <segpn> at <movposcomb>
*/
+#include <sys/mman.h>
+
+#include "record.h"
+#include "record-l.h"
+
+/*---------- input and error handling ----------*/
+
+static const char *filename;
+
+void record_yyerror(const char *m) {
+ die("config: %s:%d: %s\n", filename, record_yylineno, m);
+}
+
+/*---------- pname lookup (also handles train counting) ----------*/
+
+static char **train_pnames;
+
+Train *record_pname2train(const char *pname) {
+ int tran;
+ char **trap;
+
+ for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
+ if (!strcmp(*trap, pname)) goto found;
+ }
+ if (trains) record_yyerror("number of trains changed between passes!");
+ tran= n_trains++;
+ train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
+ train_pnames[tran]= mstrdup(pname);
+
+found:
+ if (!trains) return 0;
+ return trains + tran;
+}
+
+Segment *record_pname2seg(const char *pname) {
+ int i;
+ const SegmentInfo *segi;
+
+ for (i=0, segi=info_segments;
+ i<NUM_SEGMENTS;
+ i++, segi++)
+ if (!strcmp(segi->pname, pname))
+ return segments + i;
+
+ return 0; /* silently discard data for segments no longer in the layout */
+}
+
+/*---------- zone allocator for strings ----------*/
+
+typedef struct TempZoneBlock TempZoneBlock;
+struct TempZoneBlock {
+ TempZoneBlock *next; /* for deallocation */
+ char buf[1];
+};
+
+static TempZoneBlock *tempzones_head;
+static char *tempzone_ptr;
+static int tempzone_remain, tempzone_head_len;
+
+char *record_tempzone_strdup(const char *s) {
+ int l;
+ char *r;
+
+ l= strlen(s) + 1;
+ if (l > tempzone_remain) {
+ TempZoneBlock *new;
+ assert(l < INT_MAX/20);
+ tempzone_head_len= tempzone_remain= l*10 + 65536;
+ new= mmalloc(sizeof(new) + tempzone_remain);
+ tempzone_ptr= (char*)new + sizeof(new);
+ new->next= tempzones_head;
+ tempzones_head= new;
+ }
+ r= tempzone_ptr;
+ tempzone_remain -= l;
+ tempzone_ptr += l;
+ memcpy(r,s,l);
+ return r;
+}
+
+void record_tempzone_clear(void) {
+ TempZoneBlock *clear, *clear_next;
+ if (!tempzones_head) return;
+
+ clear= tempzones_head->next;
+ while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
+ tempzones_head->next= 0;
+ tempzone_remain= tempzone_head_len;
+ tempzone_ptr= tempzones_head->buf;
+}
+
+/*---------- semantic actions ----------*/
+/*
+ * These are all called only during the second pass, when trains is non-0.
+ * Also, all Train* and Segment* arguments are known to be non-0.
+ */
+
+void record_train_is(Train *tra, int addr, int head, int det, int tail) {
+ tra->addr= addr;
+ tra->head= head;
+ tra->detectable= det;
+ tra->tail= tail;
+}
+
+void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
+ tra->foredetect= seg;
+ tra->maxinto= maxi;
+ tra->uncertainty= unc;
+ tra->backwards= backw;
+}
+
+void record_seg_has(Segment *seg, int backw, Train *tra) {
+ seg->owner= tra;
+ seg->tr_backwards= backw;
+}
+
+void record_seg_at(Segment *seg, const char *movposcombpname) {
+ const SegPosCombInfo *spci;
+ int i;
+
+ for (i=0, spci=seg->i->poscombs;
+ i<seg->i->n_poscombs;
+ i++, spci++)
+ if (!strcmp(spci->pname, movposcombpname))
+ goto found;
+ return;
+
+found:
+ seg->movposcomb= i;
+}
+
+/*---------- speed curves ----------*/
+
+static SpeedCurveEntry *curvebuf;
+static int curvebufsz, curvebufused;
+
+void record_train_step_count(void) {
+ curvebufsz++;
+}
+
+void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
+ Train *other;
+ SpeedCurveEntry *new;
+ int i;
+
+ if (curvebufused >= curvebufsz) record_yyerror("more speed points on 2nd pass!");
+
+ if (!tra->accel.curve) {
+ tra->accel.curve= curvebuf + curvebufused;
+ }
+
+ /* Make room for an extra speed step point for this train,
+ * by moving all of the other trains up. First move the data: */
+ memmove(tra->accel.curve + tra->accel.curvesz + 1,
+ tra->accel.curve + tra->accel.curvesz,
+ (char*)(curvebuf + curvebufused)
+ - (char*)(tra->accel.curve + tra->accel.curvesz));
+ /* Then adjust everyone's pointers: */
+ for (i=0, other=trains;
+ i<n_trains;
+ i++, other++)
+ if (other != tra && other->accel.curve &&
+ other->accel.curve >= tra->accel.curve)
+ other->accel.curve++;
+ /* ... that whole thing is O(n^2) if the speed points aren't sorted
+ * by train; we hope they are, in which case we are always adding
+ * to the last train in the list and there is then no data to copy,
+ * which makes it O(n*t) where n is the number of speed points and
+ * t is the number of trains. If we cared we could optimise away
+ * the loop in the common case where this train is right at the end
+ * but a special case seems like asking for a bug when we do have
+ * out of order speed points. */
+
+ new= &tra->accel.curve[tra->accel.curvesz++];
+ curvebufsz++;
+
+ new->step= step;
+ new->speed= speed;
+ new->upwait= upw;
+ new->downwait= downw;
+}
+
+static int speedcurveentry_compare(const void *av, const void *bv) {
+ const SpeedCurveEntry *a= av, *b= bv;
+ if (a->step == b->step)
+ record_yyerror("multiple speed curve points at same step");
+ return a->step - b->step;
+}
+
+static void sort_curves(void) {
+ int i;
+ Train *tra;
+
+ for (i=0, tra=trains;
+ i<n_trains;
+ i++, tra++) {
+ if (tra->accel.curve) {
+ if (tra->accel.curvesz < 2)
+ die("config: speed curve too short for %s", tra->pname);
+ qsort(tra->accel.curve, tra->accel.curvesz,
+ sizeof(*tra->accel.curve), speedcurveentry_compare);
+ if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
+ die("config: speed curve missing zero point for %s", tra->pname);
+ }
+ }
+}
+
+/*---------- entrypoint from main, and its subroutines ----------*/
+
+static void alloc(void) {
+ Train *tra; Segment *seg; const SegmentInfo *segi;
+ char **trap;
+ int i;
+
+ segments= mmap(0, sizeof(*segments)*NUM_SEGMENTS + sizeof(*trains)*n_trains,
+ PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1,0);
+ if (!segments) diee("mmap for shared region");
+
+ for (i=0, seg=segments, segi=info_segments;
+ i<NUM_SEGMENTS;
+ i++, seg++, segi++) {
+ seg->movposcomb= -1;
+ seg->i= segi;
+ }
+
+ trains= (void*)(segments + NUM_SEGMENTS);
+ for (i=0, tra=trains, trap=train_pnames;
+ i<n_trains;
+ i++, tra++, trap++) {
+ tra->pname= *trap;
+ tra->addr= -1;
+ tra->head= -1;
+ }
+
+ curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
+}
+
+static void parse_pass(const char **argv) {
+ FILE *f;
+
+ while ((filename= *argv++)) {
+ f= fopen(filename,"r");
+ if (!f) diee("config: cannot open input file: %s", filename);
+ record_yyrestart(f);
+ record_yyparse();
+ }
+}
+
+void records_parse(const char **argv) {
+ parse_pass(argv); /* trains==0: counts trains and curve points. */
+ alloc();
+ parse_pass(argv); /* trains!=0: populates data area */
+ record_tempzone_clear();
+ sort_curves();
+}
--- /dev/null
+/*
+ */
+
+#ifndef RECORD_H
+#define RECORD_H
+
+#include "realtime.h"
+#include "record-y.h"
+
+void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc);
+void record_train_is(Train *tra, int addr, int head, int det, int tail);
+void record_train_step(Train *tra, int step, int speed, int upw, int downw);
+void record_train_step_count(void);
+void record_seg_has(Segment *seg, int backw, Train *tra);
+void record_seg_at(Segment *seg, const char *movposcomb_pname);
+
+Train *record_pname2train(const char *pname);
+Segment *record_pname2seg(const char *pname);
+char *record_tempzone_strdup(const char *s);
+void record_yyerror(const char *m);
+void record_tempzone_clear(void);
+
+int record_yyparse(void);
+int record_yylex(void);
+extern int record_yylineno;
+
+#endif /*RECORD_H*/
+++ /dev/null
-/*
- * macros which help some files in the realtime daemon
- */
-
-#ifndef RTMACROS_H
-#define RTMACROS_H
-
-#define S State *s= &safety_state
-#define TRA TrainState *tra= &s->trains[tran]
-#define TRAI const TrainInfo *trai= &info_trains[tran];
-#define SEG SegmentState *seg= &s->segments[segn]
-
-#endif /*RTMACROS_H*/
#include "realtime.h"
-State safety_state;
+int n_trains;
+Train *trains;
+Segment *segments;
-static void seg_clear_stale(SegmentState *seg) {
+static void seg_clear_stale(Segment *seg) {
if (!seg->tr_updated) {
- seg->owned= 0;
+ seg->owner= 0;
seg->until_here= seg->until_detect= 0;
}
}
typedef struct {
/* constant inputs */
- TrainNum tran;
+ Train *tra;
/* modified in place by lay_train_pass: */
ErrorCode ec;
int invert_count[2]; /* count of (switchable) segments,
* iff train is backwards (ie, train not inverted)
* invert_count[1]: train is inverted
* set to -1 if any unswitchable is the other way */
- SegmentNum invert_forcer; /* the unswitchable which forces */
+ Segment *invert_forcer; /* the unswitchable which forces */
} LayTrainState;
static void lay_train_pass(LayTrainState *l,
TrackLocation tloc, long advance,
long speed, unsigned backwards,
unsigned check_clash) {
- S;
- SegmentNum segn;
- SegmentState *seg;
- const SegmentInfo *segi;
+ Segment *seg;
long overall, remain, dist_until, time_until;
int *invert_likehere, train_inverted_here;
- TrainState *tra= &s->trains[l->tran];
+ Train *tra= l->tra;
if (l->ec) return;
- segn= tra->foredetect;
- seg= &s->segments[segn];
+ seg= tra->foredetect;
remain= overall= advance + speed * SPEED_CLEAR_MULT;
for (;;) {
- segn= tloc.segn;
- seg= &s->segments[segn];
- segi= &info_segments[segn];
+ seg= tloc.seg;
if (check_clash) {
if (seg->tr_updated) {
- l->ec= safety_problem(l->tran, tloc.segn, "self-collision");
+ l->ec= safety_problem(l->tra, tloc.seg, "self-collision");
return;
}
- if (seg->owned) {
- if (seg->owner != l->tran) {
- l->ec= safety_problem(l->tran, tloc.segn, "collision with %s",
- info_trains[seg->owner].pname);
+ if (seg->owner) {
+ if (seg->owner != l->tra) {
+ l->ec= safety_problem(l->tra, tloc.seg, "collision with %s",
+ seg->owner->pname);
return;
}
seg_clear_stale(seg);
}
}
- seg->owned= 1;
+ seg->owner= l->tra;
seg->tr_backwards= tloc.backwards ^ backwards;
seg->tr_updated= 1;
- seg->owner= l->tran;
train_inverted_here= seg->seg_inverted ^ seg->tr_backwards;
invert_likehere= &l->invert_count[train_inverted_here];
- if (segi->invertible) {
+ if (seg->i->invertible) {
if (*invert_likehere >= 0)
(*invert_likehere)++;
} else {
if (*invert_likehere < 0) {
- l->ec= safety_problem(l->tran, NOTA(Segment), "train requires"
+ l->ec= safety_problem(l->tra,0, "train requires"
" noninvertible segments with opposite polarity:"
- " @%s, @%s", segi->pname,
- info_segments[l->invert_forcer].pname);
+ " @%s, @%s", seg->i->pname,
+ l->invert_forcer->i->pname);
return;
}
assert(!seg->seg_inverted);
(*invert_likehere)++;
l->invert_count[!train_inverted_here]= -1;
- l->invert_forcer= segn;
+ l->invert_forcer= seg;
}
dist_until= (overall - remain) - advance;
}
static void lay_train_inversions(LayTrainState *l) {
- S;
SegmentNum segn;
- SegmentState *seg;
+ Segment *seg;
const SegmentInfo *segi;
int train_be_inverted, seg_be_inverted;
actual_inversions_start();
- for (segn=0, seg=s->segments, segi=info_segments;
+ for (segn=0, seg=segments, segi=info_segments;
segn <= NUM_SEGMENTS;
- segn++, seg++) {
+ segn++, seg++, segi++) {
if (!seg->tr_updated) continue;
- assert(seg->owner == l->tran);
+ assert(seg->owner == l->tra);
seg_be_inverted= train_be_inverted ^ seg->tr_backwards;
assert(!(seg_be_inverted && !segi->invertible));
seg->seg_inverted= seg_be_inverted;
- actual_inversions_segment(segn);
+ actual_inversions_segment(seg);
}
actual_inversions_done();
}
static void lay_train_done(LayTrainState *l) {
- S;
SegmentNum segn;
- SegmentState *seg;
+ Segment *seg;
- for (segn=0, seg=s->segments;
+ for (segn=0, seg=segments;
segn <= NUM_SEGMENTS;
segn++, seg++) {
- if (seg->owner == l->tran) {
+ if (seg->owner == l->tra) {
if (!seg->tr_updated) seg_clear_stale(seg);
seg->tr_updated= 0;
}
}
}
-static ErrorCode lay_train(TrainNum tran, long added_slop) {
- S; TRA; TRAI;
- SegmentNum segn;
- SegmentState *seg;
+static ErrorCode lay_train(Train *tra, long added_slop) {
+ Segment *seg;
TrackLocation tloc;
long head, headslop, tail, taildet;
LayTrainState l;
- segn= tra->foredetect;
- seg= &s->segments[segn];
+ seg= tra->foredetect;
- tloc.segn= segn;
+ tloc.seg= seg;
tloc.into= tra->maxinto;
tloc.backwards= seg->tr_backwards ^ tra->backwards;
- l.tran= tran;
+ l.tra= tra;
l.ec= 0;
l.invert_count[0]= l.invert_count[1]= 0;
return l.ec;
}
-void safety_notify_detection(SegmentNum segn) {
- S; SEG;
- TrainNum tran= seg->owner;
- TRA;
+void safety_notify_detection(Segment *seg) {
+ Train *tra= seg->owner;
ErrorCode ec;
TrackLocation tloc;
- if (!seg->owned)
- safety_panic(NOTA(Train), segn, "unexpected detection");
+ if (!seg->owner)
+ safety_panic(0,seg, "unexpected detection");
if (!seg->until_detect)
return;
- tloc.segn= segn;
+ tloc.seg= seg;
tloc.into= 0;
tloc.backwards= seg->tr_backwards;
- tra->foredetect= segn;
+ tra->foredetect= seg;
tra->maxinto= trackloc_remaininseg(&tloc);
if (seg->cm_autostop) {
seg->cm_autostop= 0;
if (!tra->estopping) {
- speedmanager_autostop(tran);
+ speedmanager_autostop(tra);
if (!tra->speed && tra->maxinto > AUTOSTOP_UNCERTAINTY)
/* At some point we may need to allow more slop when the
* next segment is points than when this is the last segment
tra->uncertainty= tra->maxinto;
if (!tra->estopping) {
- ec= lay_train(tran, 0);
+ ec= lay_train(tra, 0);
if (ec) {
- logmsg(ec, tran, segn, "emergency stop");
- safety_emergencystop(tran);
+ logmsg(ec,tra,seg->i, "emergency stop");
+ safety_emergencystop(tra);
}
}
}
-void safety_emergencystop(TrainNum tran) {
- S; TRA;
+void safety_emergencystop(Train *tra) {
ErrorCode ec;
if (tra->estopping) return;
- ec= lay_train(tran, ESTOP_UNCERTAINTY);
- if (ec) safety_panic(tran, NOTA(Segment), "emergency stop forbidden!");
- speedmanager_emergencystop(tran);
+ ec= lay_train(tra, ESTOP_UNCERTAINTY);
+ if (ec) safety_panic(tra,0, "emergency stop forbidden!");
+ speedmanager_emergencystop(tra);
}
-ErrorCode safety_requestspeed(TrainNum tran, long newspeed) {
- S; TRA;
+ErrorCode safety_requestspeed(Train *tra, long newspeed) {
long oldspeed;
ErrorCode ec;
oldspeed= tra->speed;
tra->speed= newspeed;
- ec= lay_train(tran, 0);
+ ec= lay_train(tra, 0);
if (ec) {
+ ErrorCode revert_ec;
+
if (oldspeed && oldspeed < newspeed) {
- logmsg(ec, tran, NOTA(Segment), "countermanded acceleration"
+ logmsg(ec,tra,0, "countermanded acceleration"
" from %ld to %ld", oldspeed, newspeed);
} else if (oldspeed) {
- safety_panic(tran, NOTA(Segment), "deceleration forbidden!"
+ safety_panic(tra,0, "deceleration forbidden!"
" (from %ld to %ld", oldspeed, newspeed);
} else {
- logmsg(ec, tran, NOTA(Segment), "countermanded motion start");
+ logmsg(ec,tra,0, "countermanded motion start");
}
oprintf(UPO, "countermanded %s %ld %ld\n",
- info_trains[tran].pname, oldspeed, newspeed);
- ec= lay_train(tran, 0);
- if (ec)
- safety_panic(tran, NOTA(Segment), "countermanding"
+ tra->pname, oldspeed, newspeed);
+ revert_ec= lay_train(tra, 0);
+ if (revert_ec)
+ safety_panic(tra,0, "countermanding"
" speed change insufficient!");
- return;
}
+ return ec;
}
/*========== state of the layout ==========*/
typedef struct {
- fixme init location;
- fixme call init_fixed_train;
+ 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: */
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 */
Speed speed; /* when accelerating/decelerating, is maximum at this moment */
-
+
struct {
- const SpeedCurveEntry *curve;
- int curvesz;
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;
-} TrainState;
+};
-typedef struct {
+struct Segment {
+ Train *owner; /* or 0 */
unsigned
- owned:1, /* this track segment is reserved for a train */
tr_backwards:1, /* train's motion is (would be) backwards wrt track */
movfeat_moving:1, /* feature(s) have been told to change to movposcomb */
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 */
- TimeInterval until_here, /* ) nonnegative; */ /* ) always valid but */
- until_detect; /* ) 0 if already */ /* ) only meaningful */
- TrainNum owner; /* ) iff owned */
+ TimeInterval until_here, /* ) nonnegative; */ /* ) always valid but */
+ until_detect; /* ) 0 if already */ /* ) meaningful iff owner */
MovPosComb movposcomb;
- /*polarity?*/
-} SegmentState;
-
-typedef struct {
- TrainState trains[NUM_TRAINS];
- SegmentState segments[NUM_SEGMENTS];
-} State;
+ const SegmentInfo *i;
+};
-extern State safety_state;
+extern int n_trains;
+extern Train *trains;
+extern Segment *segments;
/*========== embed.c ==========*/
/* surrounds the algorithms and machinery in a program: logging, error
* handling, arg parsing, etc.
*/
-void vlogmsg(ErrorCode ec, TrainNum tran, SegmentNum segn,
+void vlogmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
const char *fmt, va_list al) __attribute__((format(printf,4,0)));
-void logmsg(ErrorCode ec, TrainNum tran, SegmentNum segn,
+void logmsg(ErrorCode ec, Train *tra, const SegmentInfo *segi,
const char *fmt, ...) __attribute__((format(printf,4,5)));
-void safety_vpanic(TrainNum tran, SegmentNum segn, const char *fmt, va_list al)
+void safety_vpanic(Train *tra, Segment *seg, const char *fmt, va_list al)
__attribute__((format(printf,3,0),noreturn));
-void safety_panic(TrainNum tran, SegmentNum segn, const char *fmt,...)
+void safety_panic(Train *tra, Segment *seg, const char *fmt,...)
__attribute__((format(printf,3,4),noreturn));
-ErrorCode safety_problem(TrainNum tran, SegmentNum segn, const char *fmt, ...)
+ErrorCode safety_problem(Train *tra, Segment *seg, const char *fmt, ...)
__attribute__((format(printf,3,4)));
/* simple wrapper around vlogmsg; implies and returns EC_Safety */
* etc.).
*/
-void safety_emergencystop(TrainNum);
+void safety_emergencystop(Train*);
/* Callable directly in response to application command. */
-ErrorCode safety_requestspeed(TrainNum tran, long newspeed);
+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(TrainNum tran, int sense_fixme_define_this_properly);
+void safety_setdirection(Train* tra, int sense_fixme_define_this_properly);
-void safety_notify_detection(SegmentNum segn);
+void safety_notify_detection(Segment *seg);
/* Called by startup.c when new train detection occurs in state Run. */
/*========== speedmgr.c ==========*/
-void speedmanager_speedchange_request(TrainNum tran, Speed speed);
- /* Callable directly in response to application command. */
+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(TrainNum tran);
-void speedmanager_autostop(TrainNum tran);
+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
* 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
* will then call an actual_... function to notify the change.
*/
-void actual_setspeed(TrainNum tran);
-void actual_emergencystop(TrainNum tran);
+void actual_setspeed(Train *tra);
+void actual_emergencystop(Train *tra);
void actual_inversions_start(void);
-void actual_inversions_segment(SegmentNum);
+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
typedef struct TrackLocation TrackLocation;
struct TrackLocation { /* transparent, and manipulable by trackloc_... fns */
- SegmentNum segn; /* current segment */
+ Segment *seg; /* current segment */
long into; /* distance from start of segment */
unsigned backwards:1; /* if 1, into is positive and measured from end */
};
#define CLEAR_FORESIGHT_TIME 500 /*ms*/
#define AUTOSTOP_MAXSPEED ((50 * SPEED_UNIT)/1000) /* 50 mm/s */
-#define AUTOSTOP_UNCERTAINTY 20
-#define ESTOP_UNCERTAINTY 300
+#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)
*/
#include "realtime.h"
+#include "nmra.h"
-typedef struct {
- SpeedStep step;
- Speed speed;
- TimeInterval upwait, downwait; /* between this insn and next one */
-} SpeedCurveEntry;
+static void changereq_internal(Train *tra, int newcommanded, int inautostop);
+static void accel_more(TimeoutEvent *toev);
-void reset_train(Train *tra) {
+void speedmanager_reset_train(Train *tra) {
Nmra n;
+ ErrorCode ec;
tra->estopping= 0;
tra->speed= 0;
toev_init(&tra->accel.more);
enco_nmra_speed126(&n, tra->addr, 0, tra->backwards);
retransmit_urgent_queue_relaxed(&tra->accel.rn, &n);
- ec= safety_requestspeed(tran, 0);
+ ec= safety_requestspeed(tra, 0);
if (ec)
safety_panic(tra, 0, "position reset impossible!");
}
-void init_fixed_train(Train *tra, const TrainInfo *trai) {
- /* does not init location (ie foredetect, maxinto, uncertainty, backwards);
- * at least backwards must already be valid */
- tra->addr= trai->addr;
- tra->head= trai->head;
- tra->detectable= trai->detectable;
- tra->tail= trai->tail;
- tra->accel.curve= trai->curve;
- tra->accel.curvesz= trai->curvesz;
- reset_train(tra);
-}
-
-void xmit_speed(Train *tra) {
+static void xmit_speed(Train *tra) {
Nmra n;
enco_nmra_speed126(&n, tra->addr,
- tra->curve[tra->accel.commanded].step,
+ tra->accel.curve[tra->accel.commanded].step,
tra->backwards);
retransmit_urgent_requeue(&tra->accel.rn, &n);
}
-void speedmanager_autostop(TrainNum tran) {
+void speedmanager_autostop(Train *tra) {
if (tra->speed < AUTOSTOP_MAXSPEED) {
- toev_stop(&tra->speedadjust);
+ toev_stop(&tra->accel.more);
tra->accel.commanded= tra->accel.target= tra->speed= 0;
xmit_speed(tra);
} else {
static void estop_done(TimeoutEvent *toev) {
Train *tra= (void*)((char*)toev - offsetof(Train, accel.more));
retransmit_urgent_cancel(&tra->accel.rn);
- reset_train(tra);
+ speedmanager_reset_train(tra);
}
void speedmanager_emergencystop(Train *tra) {
Nmra n;
tra->estopping= 1;
- toev_stop(&tra->speedadjust);
- enco_nmra_estop(&n, tra->addr);
+ toev_stop(&tra->accel.more);
+ enco_nmra_estop1(&n, tra->addr);
retransmit_urgent_requeue(&tra->accel.rn, &n);
toev_stop(&tra->accel.more);
toev_start(&tra->accel.more);
}
-static void accel_more(TimeoutEvent *toev) {
- Train *tra= (void*)((char*)toev - offsetof(Train, accel.more));
- adjust_next(tra, 0);
-}
-
static void adjust_next(Train *tra, int inautostop) {
- int newold, xmit;
long newspeed;
if (tra->accel.target > tra->accel.commanded) {
tra->accel.commanded++;
- tra->accel.more.duration= tra->curve[tra->accel.commanded].upwait;
- newspeed= tra->curve[tra->accel.commanded].speed;
+ tra->accel.more.duration= tra->accel.curve[tra->accel.commanded].upwait;
+ newspeed= tra->accel.curve[tra->accel.commanded].speed;
} else if (tra->accel.target < tra->accel.commanded) {
- newspeed= tra->curve[tra->accel.commanded].speed;
+ newspeed= tra->accel.curve[tra->accel.commanded].speed;
tra->accel.commanded--;
- tra->accel.more.duration= tra->curve[tra->accel.commanded].downwait;
+ tra->accel.more.duration= tra->accel.curve[tra->accel.commanded].downwait;
} else {
return;
}
if (!inautostop) {
- ec= safety_requestspeed(tra, newspeed);
+ ErrorCode ec= safety_requestspeed(tra, newspeed);
if (ec) {
assert(newspeed > tra->speed);
assert(tra->accel.target >= tra->accel.commanded);
xmit_speed(tra);
}
-int changereq_internal(Train *tra, int newcommanded, int inautostop) {
+static void accel_more(TimeoutEvent *toev) {
+ Train *tra= (void*)((char*)toev - offsetof(Train, accel.more));
+ adjust_next(tra, 0);
+}
+
+static void changereq_internal(Train *tra, int newcommanded, int inautostop) {
int reverse, newold;
- if (!speedadjust->running) {
+ if (!tra->accel.more.running) {
assert(tra->accel.commanded == tra->accel.target);
adjust_next(tra, 0);
} else {
if (reverse) {
/* switch from accel to decel or vice versa */
- toev_stop(&tra->speedadjust);
+ toev_stop(&tra->accel.more);
tra->accel.commanded= newold;
adjust_next(tra, 0);
}
}
}
-void speedmanager_speedchange_request(TrainNum tran, Speed speed) {
+void speedmanager_speedchange_request(Train *tra, long speed) {
changereq_internal(tra, speed, 0);
- int a, b, found;
- const SpeedCurveEntry *curve;
+ int a, b, try, found;
+ const SpeedCurveEntry *curve= tra->accel.curve;
if (tra->estopping) {
logmsg(EC_Invalid, tra, 0, "speed request ignored during emergency stop");
return;
}
- for (a=0, b=trai->nsteps;
+ for (a=0, b=tra->accel.curvesz;
a < b;
) {
try= (a + b) >> 2;
*/
found= a>1 ? a-1 : speed ? 1 : 0;
- if (curve[found].speed < speed && !NOTA(speed))
- logmsg(EC_Invalid, tran, NOTA(SegmentNum),
- "requested speed %l excessive; capping at %l",
- (long)speed, (long)curve[found].speed);
+ if (curve[found].speed < speed && speed != INT_MAX)
+ logmsg(EC_Invalid, tra,0,
+ "requested speed %ld excessive; capping at %ld",
+ speed, curve[found].speed);
changereq_internal(tra, found, 0);
}
info_segments[segn].pname);
return;
}
- safety_notify_detection(segn);
+ safety_notify_detection(&segments[segn]);
}
/*---------- fixme move these to where they want to go ----------*/
#include <assert.h>
-#include "safety.h"
+#include "realtime.h"
const SegPosCombInfo *trackloc_segposcomb(const TrackLocation *tloc) {
- State *s= &safety_state;
- SegmentState *seg= &s->segments[tloc->segn];
- const SegmentInfo *segi= &info_segments[tloc->segn];
+ Segment *seg= tloc->seg;
- assert(seg->movposcomb < segi->n_poscombs);
- return &segi->poscombs[seg->movposcomb];
+ assert(seg->movposcomb < seg->i->n_poscombs);
+ return &seg->i->poscombs[seg->movposcomb];
}
const SegmentLinkInfo *trackloc_segmentlink(const TrackLocation *tloc,
pci= trackloc_segposcomb(tloc);
lnki_far= trackloc_segmentlink(tloc, pci, 1);
*remain_io -= segment_remain;
- tloc->segn= lnki_far->next;
+ tloc->seg= &segments[lnki_far->next];
tloc->into= 0;
tloc->backwards ^= lnki_far->next_backwards;
}
exit(12);
}
+void badusage(const char *why) {
+ fprintf(stderr,"%s: bad usage: %s\n",progname,why); exit(8);
+}
+
void die(const char *fmt, ...)
{ va_list al; va_start(al,fmt); vdie(fmt,0,al); }
void diee(const char *fmt, ...)
push @segs, $seg;
}
o(sprintf
- "#define NUM_TRAINS 1000000\n".
"#define NUM_SEGMENTS %s\n\n".
"#include \"layout-data.h\"\n\n",
scalar @segs);
}
o("$delim\n");
o(sprintf " { %-8s %4d",
- '"'.$seg.(length $pi ? '/' : '').$pi.'",',
+ '"'.$pi.'",',
$segr->{Dist}[$comb]);
for ($end=0; $end<2; $end++) {
o(", { ");
$ptv= $segr->{Feats}{$pt};
next if exists $ptv->{Fixed};
o("$delim\n");
- o(" { \"$seg/$pt\", mfk_".lc($ptv->{Kind}).",".
+ o(" { \"$pt\", mfk_".lc($ptv->{Kind}).",".
" $ptv->{Posns}, $ptv->{Weight}, mfbo_${seg}_$pt }");
$delim=',';
}
/*========== basic types etc. ==========*/
-typedef unsigned short TrainNum;
-typedef unsigned short SegmentNum;
-typedef unsigned short MovPosComb;
-typedef unsigned short BoardObject;
-
-typedef unsigned char Small;
-
-typedef short Distance;
-typedef char Speed;
- /* for units, see safety.h */
+typedef int TrainNum;
+typedef int SegmentNum;
+typedef long MovPosComb;
+typedef long Speed;
+typedef int BoardObject;
+typedef int Small;
+typedef int Distance;
typedef enum {
mfk_none,
BoardObject sense, invert;
} SegmentInfo;
-typedef struct {
- Speed maxspeed;
- Distance tail, detectable, head;
- const char *pname;
-} TrainInfo;
-
-/* These data arrays have no sentinel members. Use the info_nfoobars
- * constants. Alternatively, it is legal to sed out everything from
+/* This data array has no sentinel member. Use the info_nsegments
+ * constant. Alternatively, it is legal to sed out everything from
* <name-of-layout>.layout-data.c from the first #include onwards, and
- * this will give definitions of NUM_SEGMENTS and NUM_TRAINS.
+ * this will give a definition of NUM_SEGMENTS.
*/
extern const SegmentNum info_nsegments;
extern const SegmentInfo info_segments[];
-extern const TrainNum info_nsegments;
-extern const TrainInfo info_trains[];
-
extern const BoardObject info_maxreverse; /* max. reverse + 1 */
-#define NOTA(x) (~(x##Num)0)
-#define SOMEP(x) (!!~(x))
+#define NOTA(x) (-1)
+#define SOMEP(x) ((x) >= 0)
#endif /*LAYOUT_DATA_H*/