%.on-bessar: %
RSYNC_RSH=fsh rsync $^ $(BESSAR)
-realtime: realtime.o startup.o cdumgr.o safety.o trackloc.o \
+REALTIME_CORE= realtime.o startup.o cdumgr.o safety.o trackloc.o \
speed.o actual.o retransmit.o persist.o resolve.o \
- cmdinput.o commands.o obc.o eventhelp.o \
+ cmdinput.o commands.o obc.o eventhelp.o simulate.o \
record.o record-l.o record-y.o \
utils.o serialio.o parseutils.o auproto-pic.o \
nmra.o encode.o movpos.o \
- ../layout/ours.layout-data.o \
+ ../layout/ours.layout-data.o
+
+realtime: $(REALTIME_CORE) \
__oop-read-copy.o -loop -lm
$(LINK)
In principle, all output to PICs, in raw form, but
subject to suppression
- U< picioh <timestamp> in junk <byte> [<byte>...]
- U< picioh <timestamp> in aargh <byte> [<byte>...]
- U< picioh <timestamp> in hello <byte> [<byte>...]
- U< picioh <timestamp> in off <byte> [<byte>...]
- U< picioh <timestamp> in toolong <byte> [<byte>...]
- U< picioh <timestamp> in msg <byte> [<byte>...]
+ U< picioh in junk <byte> [<byte>...]
+ U< picioh in aargh <byte> [<byte>...]
+ U< picioh in hello <byte> [<byte>...]
+ U< picioh in off <byte> [<byte>...]
+ U< picioh in toolong <byte> [<byte>...]
+ U< picioh in msg <byte> [<byte>...]
U< picio out polarity <[<segment>[,...]]> literal < and > bracket segs
U< picio out unknown data printed in assoc'd picioh
U< debug <context> : <debug message>
U< info : <informational message>
+ U> timestamp <seconds>.<microseconds>
+ U> timer-event <class>.<instance>
+
======================================================================
COMMANDS AND RESPONSES
P> <command> [<arguments>...]
results in:
- R< executing <command>
+ R< executing <command> [<arguments>...]
consequential messages including picio, signalling problems etc.
then one of these
R< ack <command> ok
#include "daemons.h"
+oop_source *events;
+
static void *cmdi_exception(oop_source *evts, int fd,
oop_event evt, void *cmdi_v) {
CommandInput *cmdi= cmdi_v;
if (evt != OOP_RD_OK)
return cmdi_iferr(evts,cl_read,evt,errmsg,errnoval,data,recsz,cmdi_v);
- ps.cl= 0;
ps.remain= data;
cmdi->doline(&ps, cmdi);
return OOP_CONTINUE;
/*---------- from parseutils.c ----------*/
struct ParseState {
- Client *cl; /* used only by multiplexer */
const char *remain;
const char *thisword;
int lthisword;
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 real_mgettimeofday(struct timeval *tv);
void mrename(const char *old, const char *new);
};
void obc_init(OutBufferChain *ch);
+void obc_init_core(OutBufferChain *ch); /* doesn't mess with fd */
int obc_tryflush(OutBufferChain *ch);
/* returns 0 for all flushed or errno, including particularly EWOULDBLOCK */
/*---------- from cmdinput.c ----------*/
+extern oop_source *events;
+
typedef void CommandInputCallback(ParseState *ps, CommandInput *cmdi);
struct CommandInput {
/* fill in cmdi->out's `set by user' fields before calling cmdin_new,
* as cmdin_new will call obc_init and will use cmdi->out->fd. */
-/*---------- from eventhelp.c ----------*/
-
-extern oop_source *events;
-
-typedef struct TimeoutEvent TimeoutEvent;
-typedef void TimeoutEventFn(TimeoutEvent*);
-struct TimeoutEvent { /* Undefined Idle Running set by */
- int running; /* any 0 1 toev_ */
- int duration; /*ms*/ /* any any[1] any[1] caller */
- TimeoutEventFn *callback; /* any any valid[2] caller */
- struct timeval abs; /* any any valid toev_ */
-}; /* [1] duration must be >=0 or -1 when toev_start is called;
- * [2] callback may be modified while timeout is running;
- * value used is that prevailing when timeout happens
- * when the timeout happens, TimeoutEvent's state goes from R to I
- * and then callback member is read and the function called
- */
-
-void toev_init(TimeoutEvent*); /* U -> I */
-void toev_start(TimeoutEvent*); /* IR -> R; reads duration */
- /* if duration is -1 then is same as toev_stop */
-void toev_stop(TimeoutEvent*); /* IR -> I */
-
#endif /*DAEMONS_H*/
/*
- * daemons
+ * realtime
* helpers for event handling
*/
#include <assert.h>
#include <limits.h>
-#include "daemons.h"
+#include "realtime.h"
-oop_source *events;
+void real_mgettimeofday(struct timeval *tv) {
+ int r;
+ r= gettimeofday(tv,0);
+ if (r) diee("gettimeofday failed");
+ oprintf(UPO,"timestamp %ld.%06ld\n", tv->tv_sec, tv->tv_usec);
+}
-static void *toev_callback(oop_source *source, struct timeval tv, void *t_v) {
+void *toev_callback(oop_source *source, struct timeval tv, void *t_v) {
TimeoutEvent *toev= t_v;
+ oprintf(UPO,"timer-event %s.%s\n", toev->pclass, toev->pinst);
toev->running= 0;
toev->callback(toev);
return OOP_CONTINUE;
toev->abs.tv_usec += toev->duration * 1000;
toev->abs.tv_sec += toev->abs.tv_usec / 1000000;
toev->abs.tv_usec %= 1000000;
- events->on_time(events, toev->abs, toev_callback, toev);
+ if (simulate) sim_toev_start(toev);
+ else events->on_time(events, toev->abs, toev_callback, toev);
}
void toev_stop(TimeoutEvent *toev) {
if (!toev->running) return;
toev->running= 0;
- events->cancel_time(events, toev->abs, toev_callback, toev);
+ if (simulate) sim_toev_stop(toev);
+ else events->cancel_time(events, toev->abs, toev_callback, toev);
+}
+
+void mgettimeofday(struct timeval *tv) {
+ if (simulate) {
+ sim_mgettimeofday(tv);
+ } else {
+ real_mgettimeofday(tv);
+ }
}
badcmdreport_data= data;
badcmdreport_recsz= recsz;
- ps.cl= 0;
ps.remain= data;
ps_needword(&ps);
}
}
-void obc_init(OutBufferChain *ch) {
- int r;
+void obc_init_core(OutBufferChain *ch) {
ch->done_of_head= 0;
ch->total= 0;
if (!ch->limit) ch->limit= 128*1024;
+ LIST_INIT(ch->obs);
+}
+
+void obc_init(OutBufferChain *ch) {
+ int r;
+ obc_init_core(ch);
r= oop_fd_nonblock(ch->fd, 1);
if (r) diee("nonblock(OutBufferChain->fd,1)");
- LIST_INIT(ch->obs);
}
void ovprintf(OutBufferChain *ch, const char *fmt, va_list al) {
static void command_doline(ParseState *ps, CommandInput *cmdi_arg) {
int r;
+ oprintf(UPO, "executing %s\n",ps->remain);
ci= 0;
ci= some_needword_lookup(ps, toplevel_cmds, "command");
if (!ci) return;
- oprintf(UPO, "executing %s\n", ci->name);
r= ci->fn(ps,ci);
switch (r) {
case 0: oprintf(UPO, "ack %s ok\n", ci->name); break;
/*---------- serial input (via oop) ----------*/
-static PicInsn serial_buf;
+PicInsn serial_buf;
static void *serial_readable(oop_source *evts, int fd,
oop_event evt, void *u0) {
assert(r>0);
buf_used= serial_buf.l + r;
+ serial_indata_process(buf_used);
+ return OOP_CONTINUE;
+}
+void serial_indata_process(int buf_used) {
for (;;) {
serial_buf.l= buf_used;
serial_moredata(&serial_buf);
if (!buf_used) break;
}
serial_buf.l= buf_used;
- return OOP_CONTINUE;
}
/*---------- serial port output (not via liboop) ----------*/
/* do this before we call malloc so that MAP_FIXED is sure to work */
persist_entrails_run_converter();
- sys_events= oop_sys_new(); if (!sys_events) diee("oop_sys_new");
- events= oop_sys_source(sys_events); massert(events);
-
- cmdi.out.desc= (char*)"command";
- cmdi.out.fd= 1;
- cmdi.out.error= obc_error;
- cmdi.doline= command_doline;
-
- cmdin_new(&cmdi, 0);
-
while ((arg=*++argv) && *arg=='-') {
arg++;
switch (*arg++) {
case 's': device= arg; break;
case 'p': persist_fn= arg; break;
case 'v': picio_send_noise= atoi(arg); break;
+ case 'S': simulate= arg; break;
default: badusage("unknown option");
}
}
+ cmdi.out.desc= (char*)"command";
+ cmdi.out.fd= 1;
+ cmdi.out.error= obc_error;
+ cmdi.doline= command_doline;
+
persist_entrails_interpret();
records_parse(argv);
- serial_open(device);
- r= oop_fd_nonblock(serial_fd, 1); if (r) diee("nonblock(serial_fd,1)");
+ if (!simulate) {
+ sys_events= oop_sys_new(); if (!sys_events) diee("oop_sys_new");
+ events= oop_sys_source(sys_events); massert(events);
+
+ cmdin_new(&cmdi, 0);
+
+ serial_open(device);
+ r= oop_fd_nonblock(serial_fd, 1); if (r) diee("nonblock(serial_fd,1)");
- events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0);
- events->on_fd(events, serial_fd, OOP_EXCEPTION, read_exception, 0);
+ events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0);
+ events->on_fd(events, serial_fd, OOP_EXCEPTION, read_exception, 0);
+
+ } else {
+ sim_initialise();
+ sys_events= 0;
+ }
sta_startup();
- oop_sys_run(sys_events);
+
+ if (!simulate) oop_sys_run(sys_events);
+ else sim_run();
+
abort();
}
typedef struct Segment Segment;
typedef struct Train Train;
+typedef struct TimeoutEvent TimeoutEvent;
/*---------- from retransmit.c ----------*/
void serial_transmit(const PicInsn *pi);
+/*---------- for/from simulate.c ----------*/
+
+const char *simulate;
+void serial_indata_process(int buf_used);
+
+void sim_initialise(void);
+void sim_run(void);
+
+void mgettimeofday(struct timeval *tv); /* contains magic for simulation */
+void *toev_callback(oop_source *source, struct timeval tv, void *t_v);
+
+void sim_toev_start(TimeoutEvent *toev);
+void sim_toev_stop(TimeoutEvent *toev);
+void sim_mgettimeofday(struct timeval *tv);
+
+extern PicInsn serial_buf;
+
/*---------- from actual.c ----------*/
int picinsn_polarity_testbit(const PicInsn *pi, const SegmentInfo *segi);
void points_turning_on(void);
void points_all_abandon(void);
+/*---------- from eventhelp.c ----------*/
+
+typedef void TimeoutEventFn(TimeoutEvent*);
+struct TimeoutEvent { /* Undefined Idle Running set by */
+ int running; /* any 0 1 toev_ */
+ int duration; /*ms*/ /* any any[1] any[1] caller */
+ TimeoutEventFn *callback; /* any any valid[2] caller */
+ struct timeval abs; /* any any valid toev_ */
+ const char *pclass, *pinst; /* any any valid caller */
+}; /* [1] duration must be >=0 or -1 when toev_start is called;
+ * [2] callback may be modified while timeout is running;
+ * value used is that prevailing when timeout happens
+ * when the timeout happens, TimeoutEvent's state goes from R to I
+ * and then callback member is read and the function called
+ */
+
+void toev_init(TimeoutEvent*); /* U -> I */
+void toev_start(TimeoutEvent*); /* IR -> R; reads duration */
+ /* if duration is -1 then is same as toev_stop */
+void toev_stop(TimeoutEvent*); /* IR -> I */
+
/*---------- tbi ----------*/
void choreographers_all_abandon(void);
--- /dev/null
+/*
+ * realtime
+ * simulation harness
+ */
+
+#include "realtime.h"
+
+typedef struct SimTimeout {
+ struct { struct SimTimeout *back, *next; } l;
+ TimeoutEvent *toev;
+} SimTimeout;
+
+typedef void SimEventFn(void);
+
+static SimEventFn se_timestamp, se_timerevent;
+static SimEventFn se_serial, se_command, se_eof;
+
+static SimTimeout *simtimeouts;
+
+static FILE *siminput;
+static char *sevent_buf;
+static size_t sevent_buflen;
+
+static SimEventFn *sevent_type;
+static char *sevent_data, *sevent_extrap;
+static struct timeval sevent_abst;
+
+/*---------- simulation input stream parser ----------*/
+
+static void simbad(const char *how) __attribute__((noreturn));
+static void simbad(const char *how) {
+ die("bad simulation (in `%s'): %s", sevent_buf, how);
+}
+
+static void sevent(void) {
+ ssize_t gr;
+ char *p;
+
+ if (sevent_type)
+ return;
+
+ for (;;) {
+ gr= getline(&sevent_buf,&sevent_buflen,siminput);
+ if (feof(siminput)) { sevent_type= se_eof; return; }
+ if (ferror(siminput)) diee("read simulation input failed");
+ assert(gr>0);
+ assert(sevent_buf[gr-1]=='\n');
+ sevent_buf[gr-1]= 0;
+
+#define IF_ET(pfx, et) \
+ if (!strncmp(pfx " ", sevent_buf, sizeof(pfx)) \
+ && (sevent_type=(et), p=sevent_buf+(sizeof(pfx))))
+
+ IF_ET("executing", se_command) {
+ sevent_data= p;
+ return;
+ }
+ IF_ET("picioh in", se_serial) {
+ p= strchr(p,' ');
+ if (!p) simbad("missing space after `in'");
+ sevent_data= p+1;
+ return;
+ }
+ IF_ET("timer-event", se_timerevent) {
+ sevent_extrap= strchr(p,'.');
+ if (!sevent_extrap) simbad("missing `.' in timer event name");
+ *sevent_extrap++= 0;
+ sevent_data= p;
+ return;
+ }
+ IF_ET("timestamp", se_timestamp) {
+ int n, r;
+ n=-1;
+ r= sscanf(p,"%ld.%ld%n", &sevent_abst.tv_sec, &sevent_abst.tv_usec, &n);
+ if (r<2 || !(n==-1 || !p[n])) simbad("unparseable timestamp");
+ return;
+ }
+ }
+}
+
+/*---------- hooks for eventhelp ----------*/
+
+void sim_toev_start(TimeoutEvent *toev) {
+ SimTimeout *s= mmalloc(sizeof(*s));
+ s->toev= toev;
+ DLIST1_PREPEND(simtimeouts,s,l);
+}
+
+static void sim_toev_remove(SimTimeout *s) {
+ DLIST1_REMOVE(simtimeouts,s,l);
+ free(s);
+}
+
+void sim_toev_stop(TimeoutEvent *toev) {
+ SimTimeout *s;
+ for (s=simtimeouts; !(s->toev==toev); s=s->l.next);
+ sim_toev_remove(s);
+}
+
+void sim_mgettimeofday(struct timeval *tv) {
+ sevent();
+ *tv= sevent_abst;
+ if (sevent_type==se_timestamp)
+ sevent_type= 0;
+}
+
+/*---------- simulation events ----------*/
+
+static void se_timestamp(void) { }
+
+static void se_timerevent(void) {
+ TimeoutEvent *toev;
+ SimTimeout *s;
+
+ for (s=simtimeouts; s; s=simtimeouts->l.next) {
+ toev= s->toev;
+ if (!strcmp(toev->pclass, sevent_data) &&
+ !strcmp(toev->pinst, sevent_extrap))
+ goto found;
+ }
+ simbad("timeout event not found");
+
+ found:
+ sim_toev_remove(s);
+ toev_callback(0, sevent_abst, toev);
+}
+
+static void se_serial(void) {
+ int l, buf_used, i;
+ char c[3], *ep;
+ const char *p;
+ Byte *q;
+
+ l= strlen(sevent_data);
+ if (l%2) simbad("odd number of hex digits");
+ l /= 2;
+
+ buf_used= serial_buf.l + l;
+ if (buf_used > sizeof(serial_buf.d)) simbad("serial_buf overrun");
+
+ c[2]= 0;
+ for (i=0, p=sevent_data, q=serial_buf.d+serial_buf.l; i<l; i++, q++) {
+ c[0]= *p++;
+ c[1]= *p++;
+ *q= strtoul(c,&ep,16);
+ if (*ep) simbad("bad hex");
+ }
+ serial_indata_process(buf_used);
+}
+
+static void se_command(void) {
+ ParseState ps;
+ ps.remain= sevent_data;
+ ps.thisword= 0;
+ ps.lthisword= 0;
+ cmdi.doline(&ps,&cmdi);
+}
+
+static void se_eof(void) {
+ exit(0);
+}
+
+/*---------- core ----------*/
+
+void sim_initialise(void) {
+ obc_init_core(&cmdi.out);
+ serial_fd= open("/dev/null",O_WRONLY);
+ if (serial_fd<0) diee("open /dev/null for dummy serial");
+ siminput= fopen(simulate,"r");
+ if (!siminput) diee("open simulation input %s",simulate);
+}
+
+void sim_run(void) {
+ for (;;) {
+ sevent();
+ sevent_type();
+ obc_tryflush(&cmdi.out);
+ }
+}
tra->speed.step= 0;
toev_init(&tra->speed.decel);
tra->speed.decel.callback= decel_done;
+ tra->speed.decel.pclass= "decel";
+ tra->speed.decel.pinst= tra->pname;
tra->speed.speed= 0;
tra->speed.try_speed= -1;
if (tra->addr < 0)
const char *const stastatelist[]= DEFINE_STASTATE_DATA;
StartupState sta_state;
-static TimeoutEvent sta_toev;
-static TimeoutEvent ping_toev;
+static TimeoutEvent sta_toev= { .pclass="startup", .pinst="sta" };
+static TimeoutEvent ping_toev= { .pclass="startup", .pinst="ping" };
static int ping_seq;
static PicInsn piob;
char *mstrdup(const char *s) { return mstrdupl(s,strlen(s)); }
-void mgettimeofday(struct timeval *tv) {
- int r;
- r= gettimeofday(tv,0);
- if (r) diee("gettimeofday failed");
-}
-
void mrename(const char *old, const char *new) {
if (rename(old,new))
diee("failed to rename `%s' to `%s'", old,new);