From ab8ad1e1a6364dd0b11a2f8f636182f748f60227 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 25 Jan 2008 22:19:00 +0000 Subject: [PATCH] merge changes from trunk into our branch; cvs up -j branchpoint-hostside-wip-2006-05-06 -j branchend-hostside-wip-2006-05-06; discarded changes: NOPOINT (trunk has 500ms delay before turning CDU off instead) and startwait (trunk has stf_waiting instead) --- TODO | 4 +- cebpic/README.protocol | 7 +- cebpic/morse-generator | 4 +- cprogs.make | 12 +- detpic/Makefile | 20 +- detpic/common.inc | 2 +- detpic/morse.messages | 2 - detpic/nmra-stream.asm | 2 +- detpic/panic.asm | 2 +- detpic/program.asm | 22 ++ detpic/program.clocks | 9 +- detpic/reverse.asm | 3 +- hostside/.cvsignore | 8 +- hostside/Makefile | 46 ++-- hostside/README | 17 +- hostside/Structure | 46 ++++ hostside/TODO | 15 +- hostside/actual.c | 47 ++++ hostside/cdumgr.c | 20 ++ hostside/client.c | 91 +------- hostside/cmdinput.c | 60 +++++ hostside/commands.c | 58 ++--- hostside/common.h | 80 ++++++- hostside/daemons.h | 84 +++++++ hostside/dliste.h | 73 +++++++ hostside/dummy-trains.c | 19 ++ hostside/errorcodes.h.gen | 32 +++ hostside/eventhelp.c | 38 ++++ hostside/gui-plan.c | 6 +- hostside/hostside.c | 43 +--- hostside/hostside.h | 124 +---------- hostside/main.c | 6 +- hostside/measure-speeds | 261 ++++++++++++++++++++++ hostside/multiplex.c | 4 + hostside/multiplex.h | 44 ++++ hostside/obc.c | 39 +++- hostside/output.c | 70 ------ hostside/parse-proto-spec | 6 + hostside/parseutils.c | 28 ++- hostside/realtime.c | 374 ++++++++++++++++++++++++++++++++ hostside/realtime.h | 111 ++++++++++ hostside/record-l.l | 39 ++++ hostside/record-y.y | 61 ++++++ hostside/record.c | 268 +++++++++++++++++++++++ hostside/record.h | 27 +++ hostside/retransmit-table.h.gen | 29 +++ hostside/retransmit.c | 134 +++++++++++- hostside/safety.c | 198 +++++++---------- hostside/safety.h | 124 +++++++---- hostside/santafe-1.speeds | 126 +++++++++++ hostside/santafe-2.speeds | 126 +++++++++++ hostside/selectors.h.gen | 5 +- hostside/selectout.c | 33 +++ hostside/serialio.c | 1 + hostside/skelproto-pic.c | 53 +++-- hostside/skelproto-pic.h | 22 +- hostside/speed.c | 163 ++++++++++++++ hostside/startup.c | 218 +++++++++++++++++++ hostside/stastate.h.gen | 21 ++ hostside/trackloc.c | 12 +- hostside/utils.c | 47 +++- layout/Makefile | 6 +- layout/data2safety | 91 +++++--- layout/layout | 2 +- layout/layout-data.h | 39 ++-- pic.make | 10 +- 66 files changed, 3124 insertions(+), 670 deletions(-) create mode 100644 hostside/Structure create mode 100644 hostside/actual.c create mode 100644 hostside/cdumgr.c create mode 100644 hostside/cmdinput.c create mode 100644 hostside/daemons.h create mode 100644 hostside/dliste.h create mode 100644 hostside/dummy-trains.c create mode 100755 hostside/errorcodes.h.gen create mode 100644 hostside/eventhelp.c create mode 100755 hostside/measure-speeds create mode 100644 hostside/multiplex.c create mode 100644 hostside/multiplex.h create mode 100644 hostside/realtime.c create mode 100644 hostside/realtime.h create mode 100644 hostside/record-l.l create mode 100644 hostside/record-y.y create mode 100644 hostside/record.c create mode 100644 hostside/record.h create mode 100755 hostside/retransmit-table.h.gen create mode 100644 hostside/santafe-1.speeds create mode 100644 hostside/santafe-2.speeds create mode 100644 hostside/selectout.c create mode 100644 hostside/speed.c create mode 100644 hostside/startup.c create mode 100755 hostside/stastate.h.gen diff --git a/TODO b/TODO index 902e32b..f3d6d46 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,6 @@ -race between cdu off (due to watchdog or short) and points +avoid retransmitting too often (S9.2 C l114-) + +as a result of running, then touching track near panel to induce detection layout polarity diagram colourful segment encoding: diff --git a/cebpic/README.protocol b/cebpic/README.protocol index df31395..14938cf 100644 --- a/cebpic/README.protocol +++ b/cebpic/README.protocol @@ -24,7 +24,7 @@ From host to PIC: > 0 0010 000 (10) OFF Power off ;> 00000000 CRASHED Acknowledge panic, go to readout mode -;> 00001002 (0a) TELLMODE Confirm mode - say HELLO or CRASHED +;> 00001010 (0a) TELLMODE Confirm mode - say HELLO or CRASHED ; if crashed, undoes the effect of ack ; In crash readout mode: @@ -62,7 +62,7 @@ From PIC to host: < 1 0001 XXX 0 XXXXXXX (88+) PONG Pong `X' (reply to Ping `X') < 0 000 1001 (HT) HELLO I am booted < 0 000 1011 (VT) AAARGH Followed by debug chars (only) - < 0 000 1101 (CR) WATCHDOG Timeout happened + < 0 000 1101 (CR) WTIMEOUT Watchdog timeout happened < 0 000 0111 (BEL) FAULT Fault exists < 0 000 0110 (ACK) FIXED Fault now fixed < 0 0100 PPP (20+) POINTED Point change done using capacitor P @@ -193,7 +193,8 @@ Bits which do not correspond to defined reversal segments will be ignored by the PICs. The host must send exactly as many bytes as are necessary to include all of the reversal segments for each reversers board (for every potential reversal segment, regardless of whether -that segment is a defined segment corresponding to some actual track). +that segment is a defined segment corresponding to some actual track; +however a board with _no_ reversers segments used does not count). For example, if there are 14 reversible segments (numbered 1 to 14) then the following message diff --git a/cebpic/morse-generator b/cebpic/morse-generator index 622ba7a..590f5e4 100755 --- a/cebpic/morse-generator +++ b/cebpic/morse-generator @@ -152,8 +152,8 @@ while (<>) { print("morse_$morse_name db @data\n") or die $! if $which eq 'asm'; - printf("morse_$morse_name equ morse_messages_start+0x%x\n", - $bytes) or die $! + printf("morse_$morse_name equ morse_messages_start+0x%x; panic_address=%x\n", + $bytes, $bytes/4) or die $! if $which eq 'inc'; $bytes += scalar @data; diff --git a/cprogs.make b/cprogs.make index e6fd70c..00bf7e5 100644 --- a/cprogs.make +++ b/cprogs.make @@ -8,6 +8,14 @@ CFLAGS= $(CPPFLAGS) -D_GNU_SOURCE \ CPPFLAGS= LINK= $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) +LEX=flex +BISON=bison + +FLEXCFLAGS= -Wno-unused -Wno-missing-declarations -Wno-missing-prototypes + %.o: %.c $(AUTOINCS) - $(CC) $(CFLAGS) -MM $< >$*.d - $(CC) $(CFLAGS) -o $@ -c $< + $(CC) $(CFLAGS) $(SPECIAL_CFLAGS_$*) -MM $< >$*.d + $(CC) $(CFLAGS) $(SPECIAL_CFLAGS_$*) -o $@ -c $< + +%.c: %.y + $(BISON) -o $@ $< diff --git a/detpic/Makefile b/detpic/Makefile index 3b04fa2..b947e9e 100644 --- a/detpic/Makefile +++ b/detpic/Makefile @@ -8,11 +8,11 @@ FILES= variables vectors \ syncwrite mascan energy \ reverse nmra-stream tick watchdog -PROGRAMS= program program+nd +PROGRAMS= program program+dbg -OBJS_program+nd= $(addsuffix +nd.o, $(FILES)) -XCODEN_program+nd= morse -XCODE1_program+nd= ours+pindata +OBJS_program+dbg= $(addsuffix +dbg.o, $(FILES)) +XCODEN_program+dbg= morse +XCODE1_program+dbg= ours+pindata OBJS_program= $(addsuffix .o, $(FILES)) XCODEN_program= morse @@ -41,9 +41,9 @@ bessar: program+program.map $(SYNCWRITES): %: ../iwjpictest/% cp $< $@ -%+nd.o: %.asm $(INCLUDES) - $(ASSEMBLE) -DNDEBUG=1 -o $@ -c $< - @mv $*+nd.lst $*+nd+asm.lst +%+dbg.o: %.asm $(INCLUDES) + $(ASSEMBLE) -DDEBUG=1 -o $@ -c $< + @mv $*+dbg.lst $*+dbg+asm.lst %+externs.fin: make-externs %.asm $(addsuffix .asm, $(FILES)) ./$+ >$@.new @@ -61,6 +61,12 @@ i2clib+panic.mangles: make-panicworthy i2clib.inc i2clib.asm Makefile t.%.crash: crashread program+program.map ./$< /dev/ttya0 $(filter-out $<,$^) $* $o +verify%: program+entire%.hex + odyssey-train $* verify $^ + +verify: $(addprefix verify,$(PICNOS)) + echo 'ready for operation' + clean: manypic-clean rm -f *+clocks.inc ours+pindata.* i2clib+panic.* rm -f $(VARSFILES)+vars.* $(SYNCWRITES) diff --git a/detpic/common.inc b/detpic/common.inc index 8f276a9..c3537b2 100644 --- a/detpic/common.inc +++ b/detpic/common.inc @@ -129,7 +129,7 @@ tickdiv_us equ tick_us * tickdiv ; STATUS any trashed ; all others any preserved - ifndef NDEBUG + ifdef DEBUG ;---------- Dv macro ; sorry, but assembler's dw directive isn't case-sensitive ; diff --git a/detpic/morse.messages b/detpic/morse.messages index e317f0c..8194ad3 100644 --- a/detpic/morse.messages +++ b/detpic/morse.messages @@ -48,7 +48,6 @@ HP ; host sent >2-byte PING command # Regarding communications by master with slaves MX ; slave (or intern) sent bad extra byte MR mascan:b,mascan:cslot ; reversers set byte with zz bit set -MD ; i2cmu_done ! # Problematic interrupts IH INTCON,INTCON3,PIR1,PIR2 ; Interrupt source not found (high pri.) @@ -97,6 +96,5 @@ PC ; Firing point when CDU empty PS points:pointslave,points:pointmsg ; Firing point on nonexistent board PF ::t ; Flash mentions point not on board PX ; Host sent >2-byte POINT command -PM ; Firing point when master/CDU busy PQ ; POINTED when already charging PA ; POINTED when already firing diff --git a/detpic/nmra-stream.asm b/detpic/nmra-stream.asm index 1331f23..7693f2b 100644 --- a/detpic/nmra-stream.asm +++ b/detpic/nmra-stream.asm @@ -87,10 +87,10 @@ debug macro debugvalue mov_lw debugvalue call debug_serial_transmit endm - endif debug_serial_transmit mov_wfa TXREG ; move contents of W (i.e. debugvalue) + endif ; to TXREG for transmission waitfortsr bt_fa_if0 TXSTA,1 diff --git a/detpic/panic.asm b/detpic/panic.asm index 663d7fe..d083b20 100644 --- a/detpic/panic.asm +++ b/detpic/panic.asm @@ -72,7 +72,7 @@ panic_routine @ mov_ff LATC, psave_latc ; now we have time to save registers etc -; (turning off interrupts is urgent (we might get interrupted while +; (turning off interrupts is urgent: we might get interrupted while ; panicing which would be bad because we might forget to panic). mov_wf panic_address diff --git a/detpic/program.asm b/detpic/program.asm index 2a87efe..7baba73 100644 --- a/detpic/program.asm +++ b/detpic/program.asm @@ -60,6 +60,7 @@ master_init call memory_erase clr_f flags call serial_init + call startwait call i2cm_init call serialtxbuf_init call polarity_master_init @@ -102,6 +103,27 @@ master_interrupt_low @ Dl 0x8f panic morse_IL +;---------------------------------------- +startwait +; We pause before starting up to give slaves a chance to wake up. +; +; Timer 0 any trashed, left running +; GIEH, GIEL remain continuously disabled +; + clr_f T0CON + bc_f INTCON,TMR0IF + mov_lw startwait_master_t0inith + mov_wf TMR0H + mov_lw startwait_master_t0initl + mov_wf TMR0L + mov_lw (1< M|S|MS how interval +# M|S|MS[T] how interval # where how is one of -# T1ov T3ov +# T0ov8 T0ov16 T1ov T3ov # set T[13]CON to | _{master,slave}_t[13]scale # load TMR[13][HL] with _{master,slave}_t[13]init[hl] # or to put it another way, TMR[13] with # 65535 - _{master,slave}_t[13]cycles # then time to overflow will be specified time +# T2period +# set T2CON to | _{master,slave}_t2scale +# set PR2 to _{master,slave}_t2cycles +# M and S mean generate _{master,slave}_ respectively +# T means generate _{us,ms} points MS T3ov 10ms tick MST T2period 1ms diff --git a/detpic/reverse.asm b/detpic/reverse.asm index 725edfc..ea1e3ab 100644 --- a/detpic/reverse.asm +++ b/detpic/reverse.asm @@ -40,6 +40,7 @@ polarity_local_do @ ; LATA,LATE any modified appropriately ; all others any preserved ; +; ( bit-in-byte letters in command_polarity: Nf Ne Nd Nc Nb Na ) ; on entry: ; W = PP PP v3 v0 v2 v1 v5 v4 ; ; where PP bits are those specifying that this is a polarity message @@ -113,7 +114,7 @@ command_polarity @ ; zz bit zero ; other things are where ; is a for LSb in message to PIC, b for next bit, -; and so on until f for bit 5. (See polarity_do_here, below.) +; and so on until f for bit 5. (See polarity_local_do, above.) ; ; we accumulate (`gather') the `g' bits in t. mov_lfsr polarity_cmds, 1 diff --git a/hostside/.cvsignore b/hostside/.cvsignore index e285fdd..1962df4 100644 --- a/hostside/.cvsignore +++ b/hostside/.cvsignore @@ -1,5 +1,6 @@ -hostside +realtime hostside-old +hostside safety t layoutinfo.h @@ -8,3 +9,8 @@ proto-expanded auproto-* gui-plan-bot selectors.h +errorcodes.h +stastate.h +retransmit-table.h +record-l.[ch] +record-y.[ch] diff --git a/hostside/Makefile b/hostside/Makefile index a56c460..589bffa 100644 --- a/hostside/Makefile +++ b/hostside/Makefile @@ -1,12 +1,12 @@ # -AUTOINCS= selectors.h +AUTOINCS= auproto-pic.h layoutinfo.h selectors.h retransmit-table.h \ + errorcodes.h stastate.h +TARGETS= hostside-old gui-plan-bot realtime include ../common.make include ../cprogs.make -TARGETS= hostside hostside-old gui-plan-bot - default: all recurse: all for-pic: @@ -15,10 +15,24 @@ all: $(TARGETS) hostside-old: serialio.o nmra.o main.o encode.o utils.o $(LINK) -hostside: hostside.o serialio.o client.o obc.o commands.o utils.o \ - nmra.o encode.o retransmit.o output.o auproto-pic.o \ - parseutils.o \ - -loop +BESSAR=bessar:things/trains-bessar/hostside/. + +SPECIAL_CFLAGS_record-l= $(FLEXCFLAGS) + +on-bessar: $(TARGETS) + RSYNC_RSH=fsh rsync $(TARGETS) $(BESSAR) + +%.on-bessar: % + RSYNC_RSH=fsh rsync $^ $(BESSAR) + +realtime: realtime.o startup.o cdumgr.o safety.o trackloc.o \ + speed.o actual.o retransmit.o \ + cmdinput.o commands.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) proto-expanded: ../cebpic/README.protocol @@ -29,22 +43,26 @@ gui-plan-bot: gui-plan-%: gui-plan.o utils.o parseutils.o \ __oop-read-copy.o -loop $(LINK) -L/usr/X11R6/lib -lXpm -lX11 -commands.o auproto-pic.o: auproto-pic.h - -auproto-%: parse-proto-spec proto-expanded skelproto-% +auproto-pic.c auproto-pic.h: auproto-%: \ + parse-proto-spec proto-expanded skelproto-% ./$+ $o layoutinfo.h: ../layout/ours.layout-data.c Makefile sed -e '/^#include/,$$d' $< $o -selectors.h: selectors.h.gen - ./$< $o - -safety.o trackloc.o: layoutinfo.h +selectors.h retransmit-table.h errorcodes.h stastate.h: %: %.gen + (echo "/*autogenerated*/" && ./$<) $o safety: safety.o utils.o trackloc.o ../layout/ours.layout-data.o $(LINK) +hostside: hostside.o serialio.o client.o obc.o commands.o utils.o \ + nmra.o encode.o retransmit.o output.o auproto-pic.o \ + parseutils.o \ + record-l.o record-y.o record.o \ + -loop + $(LINK) + clean: rm -f *.o *.d $(TARGETS) selectors.h rm -f auproto-pic.h *~ core proto-expanded diff --git a/hostside/README b/hostside/README index bf67f48..39d1695 100644 --- a/hostside/README +++ b/hostside/README @@ -32,14 +32,15 @@ To test: Protocol over new hostside stdin: Example (always the same msg): - P> nmra [*] [...]] nmra speed28 3 13 1 - P> nmra [*] [=] nmra =0348 - P> nmra [*] [:] nmra :03484b - P> nmra [*] [_] nmra _7f7f00644197 - in each case * indicates that the message should be put in - the retransmission cycle, and cancels any previous message with the - same *. may be empty. - P> nmra * + P> nmra [] [...]] nmra speed28 3 13 1 + P> nmra [] [=] nmra =0348 + P> nmra [] [:] nmra :03484b + P> nmra [] [_] nmra _7f7f00644197 + in each case (if present) is * or % and + indicates that the message should be put in the retransmission + cycle, and cancels any previous message with the same . + may be empty. * indicates urgent + P> nmra cancels the relevant retransmission. Example (always the same msg): diff --git a/hostside/Structure b/hostside/Structure new file mode 100644 index 0000000..538e1ad --- /dev/null +++ b/hostside/Structure @@ -0,0 +1,46 @@ +pic + talks pic protocol to safety + +safety + rtprio + passes on all pic traffic + safety incl polarity, no collisions, curvature, etc. + converts instructions to/from text + takes instructions only from multiplex +all real-time stuff so clients can be confident of no misthings ? + on-event for point changing, stopping, etc. ? + specify intended path and speed profile for each train ? + - list of points, in order, with setting + this is good enough to be in time for shinkansen on inner loop + inner loop is 3589mm + minus longest section for slop -657mm (X10) + minus length of the train -1118mm + ------- + spare 1813mm + or at 1m/s 1813ms + minus safety delay -500ms + ------- + 1313ms + + optimistic path change + - optimistic change request recorded on some point + - when we find it we check + is point free + is another set request recorded + if so check whether we'll be out of the way + plot new path for this train + + deadline scheduler for point change and cdu + + for points autochanging + lay train an extra 600ms or so + + +commsn between realtime and multiplexer + each command produces `ack' or `error' + multiplexer counts responses for flow control + +multiplex + talks to all clients + access control + message mux/demux incl filtering diff --git a/hostside/TODO b/hostside/TODO index c08e6f4..1c8443e 100644 --- a/hostside/TODO +++ b/hostside/TODO @@ -1,3 +1,16 @@ +before can test + commands for speed manager + create data files containing some train data + write trivial initial settlement to get data array right + write out layout data on crash + check that everything in Train and Segment is init'd + +dunno but maybe before can test + train speed curve measurements + wiring to gui display + things not yet considered at all in safety code - polarity + coming up against points the wrong way min. curve specifications + +initialise safety_state with appropriate stuff diff --git a/hostside/actual.c b/hostside/actual.c new file mode 100644 index 0000000..21eef04 --- /dev/null +++ b/hostside/actual.c @@ -0,0 +1,47 @@ +/* + * realtime + * implementation of safety algorithm's decisions + */ + +#include "realtime.h" + +static PicInsn polarityinsn= { { 0x90 }, 0 }; + +#define OPONBIT_BYTE (polarityinsn.d[bytenum]) +#define OPONBIT(body) do{ \ + BoardObject bo; \ + int bytenum, bitnum; \ + Byte bitv; \ + \ + bo= segi->invert; \ + assert(bo); \ + bytenum= (bo + 3) / 7; \ + bitnum= bytenum ? 6 - (bo + 3) % 7 : 3 - bo; \ + bitv= 1u << bitnum; \ + \ + { body } \ + }while(0) + +int picinsn_polarity_testbit(const PicInsn *pi, const SegmentInfo *segi) { + OPONBIT({ + return !!(OPONBIT_BYTE & bitv); + }); +} + +void actual_inversions_start(void) { + polarityinsn.l= (info_maxreverse + 4 + 6) / 7; +} + +void actual_inversions_segment(Segment *seg) { + const SegmentInfo *segi= seg->i; + + OPONBIT({ + Byte *insnbyte= &OPONBIT_BYTE; + if (seg->seg_inverted) *insnbyte |= bitv; + else *insnbyte &= ~bitv; + }); +} + +void actual_inversions_done(void) { + serial_transmit(&polarityinsn); +} diff --git a/hostside/cdumgr.c b/hostside/cdumgr.c new file mode 100644 index 0000000..db6f8ba --- /dev/null +++ b/hostside/cdumgr.c @@ -0,0 +1,20 @@ +/* + * realtime + * cdu and point charge control and scheduler + */ + +#include "realtime.h" + +void on_pic_charged(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { + if (sta_state <= Sta_Settling) return; + /* fixme do something here */ +} + +void on_pic_pointed(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { + if (sta_state <= Sta_Settling) return; + /* fixme do something here */ +} + +void on_pic_nopoint(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { + /* fixme perhaps log this if it was unexpected */ +} diff --git a/hostside/client.c b/hostside/client.c index 989f782..8b3a24a 100644 --- a/hostside/client.c +++ b/hostside/client.c @@ -5,102 +5,23 @@ #include #include -#include "hostside.h" +#include "multiplex.h" #include "../layout/dlist.h" struct ClientList clients; -void vbadcmd(ParseState *ps, const char *fmt, va_list al) { - oprintf(&ps->cl->ch,"error "); - ovprintf(&ps->cl->ch,fmt,al); - owrite(&ps->cl->ch,"\n",1); -} - -const void *any_lookup(ParseState *ps, const void *inf, size_t sz) { - const char *tname; - - for (; - (tname= *(const char *const*)inf); - inf= (const char*)inf + sz) - if (!thiswordstrcmp(ps,tname)) - return inf; - return 0; -} - -const void *any_needword_lookup(ParseState *ps, const void *infs, - size_t sz, const char *what) { - const void *r; - if (!ps_needword(ps)) return 0; - r= any_lookup(ps,infs,sz); - if (!r) { badcmd(ps,"unknown %s",what); return 0; } - return r; -} - -void ps_callword(ParseState *ps, const CmdInfo *infs, const char *what) { - const CmdInfo *ci; - ci= some_needword_lookup(ps,infs,what); - if (ci) ci->fn(ps,ci); -} - -static void *client_iferr(oop_source *evts, oop_read *cl_read, - oop_rd_event evt, const char *errmsg, int errnoval, - const char *data, size_t recsz, void *cl_v) { - Client *cl= cl_v; - - cl->ch.error(&cl->ch, "read", - oop_rd_errmsg(cl_read, evt, - errnoval, OOP_RD_STYLE_GETLINE)); - return OOP_CONTINUE; -} - -static void *client_ifok(oop_source *evts, oop_read *cl_read, - oop_rd_event evt, const char *errmsg, int errnoval, - const char *data, size_t recsz, void *cl_v) { - Client *cl= cl_v; - ParseState ps; - - if (evt == OOP_RD_EOF) { - cl->ch.error(&cl->ch,0,0); - return OOP_CONTINUE; - } - - if (evt != OOP_RD_OK) - return client_iferr(evts,cl_read,evt,errmsg,errnoval,data,recsz,cl_v); - - ps.cl= cl; - ps.remain= data; - ps_callword(&ps, toplevel_cmds, "command"); - return OOP_CONTINUE; -} - -static void *client_exception(oop_source *evts, int fd, - oop_event evt, void *cl_v) { - Client *cl= cl_v; - cl->ch.error(&cl->ch,"comms error","exception"); - return OOP_CONTINUE; -} - static void new_client(int fd, OutBufferError error, char *desc, Selector default_selectors) { Client *cl; int r; cl= mmalloc(sizeof(*cl)); - cl->sel= default_selectors; - cl->ch.desc= desc; - cl->ch.fd= fd; - cl->ch.error= error; - obc_init(&cl->ch); - - events->on_fd(events, fd, OOP_EXCEPTION, client_exception, cl); - - cl->rd= oop_rd_new_fd(events, fd, 0,0); - if (!cl->rd) diee("oop_rd_new_fd"); - r= oop_rd_read(cl->rd, OOP_RD_STYLE_GETLINE, 1024, - client_ifok, cl, - client_iferr, cl); - if (r) diee("oop_rd_read"); + + cl->ci.out.desc= desc; + cl->ci.out.fd= fd; + cl->ci.out.error= error; + cmdin_new(&cl->ci); LIST_LINK_TAIL(clients, cl); } diff --git a/hostside/cmdinput.c b/hostside/cmdinput.c new file mode 100644 index 0000000..51cec79 --- /dev/null +++ b/hostside/cmdinput.c @@ -0,0 +1,60 @@ +/* + * realtime & multiplexer + * reading and parsing commands from some fd + */ + +#include "daemons.h" + +static void *cmdi_exception(oop_source *evts, int fd, + oop_event evt, void *cmdi_v) { + CommandInput *cmdi= cmdi_v; + cmdi->out.error(&cmdi->out, "error by command source", + "exceptional condition"); + return OOP_CONTINUE; +} + +static void *cmdi_iferr(oop_source *evts, oop_read *cl_read, + oop_rd_event evt, const char *errmsg, int errnoval, + const char *data, size_t recsz, void *cmdi_v) { + CommandInput *cmdi= cmdi_v; + + cmdi->out.error(&cmdi->out, "read", + oop_rd_errmsg(cl_read, evt, + errnoval, OOP_RD_STYLE_GETLINE)); + return OOP_CONTINUE; +} + +static void *cmdi_ifok(oop_source *evts, oop_read *cl_read, + oop_rd_event evt, const char *errmsg, int errnoval, + const char *data, size_t recsz, void *cmdi_v) { + CommandInput *cmdi= cmdi_v; + ParseState ps; + + if (evt == OOP_RD_EOF) { + cmdi->out.error(&cmdi->out,0,0); + return OOP_CONTINUE; + } + + 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; +} + +void cmdin_new(CommandInput *cmdi, int readfd) { + int r; + + obc_init(&cmdi->out); + + events->on_fd(events, readfd, OOP_EXCEPTION, cmdi_exception, cmdi); + + cmdi->rd= oop_rd_new_fd(events, readfd, 0,0); + if (!cmdi->rd) diee("oop_rd_new_fd"); + r= oop_rd_read(cmdi->rd, OOP_RD_STYLE_GETLINE, 1024, + cmdi_ifok, cmdi, + cmdi_iferr, cmdi); + if (r) diee("oop_rd_read"); +} diff --git a/hostside/commands.c b/hostside/commands.c index b14ed03..9dd5434 100644 --- a/hostside/commands.c +++ b/hostside/commands.c @@ -6,7 +6,7 @@ #include #include -#include "hostside.h" +#include "realtime.h" #include "auproto-pic.h" #define NMRA_MAX_NARGS 10 @@ -16,7 +16,7 @@ struct ManualRetransmitNode { ManualRetransmitNode *back, *next; char *name; int lname; - RetransmitNode rn; + RetransmitUrgentNode rn; }; #define bogus_volatile /*empty*/ @@ -33,6 +33,10 @@ struct NmraParseEncodeCaller { jmp_buf jb; }; +static void ouack(const CmdInfo *ci) { + oprintf(UPO, "ack %s\n", ci->name); +} + unsigned long nmra_argnumber(NmraParseEncodeCaller *pec, int argi) { return pec->arg[argi]; } @@ -42,7 +46,7 @@ void nmra_problem(NmraParseEncodeCaller *pec, const char *problem) { longjmp(pec->jb, 1); } -static int cmd_nmra_command(ParseState *ps, RetransmitNode *rn) { +static int cmd_nmra_command(ParseState *ps, PicInsn *pi) { int hex; const char *cmdarg; int lcmdarg; @@ -53,7 +57,7 @@ static int cmd_nmra_command(ParseState *ps, RetransmitNode *rn) { assert(ps->remain); switch (ps->remain[0]) { - case '_': ps->remain++; return ps_needhextoend(ps, rn->pi.d, &rn->pi.l); + case '_': ps->remain++; return ps_needhextoend(ps, pi->d, &pi->l); case '=': hex=1; checksum=1; break; case ':': hex=1; checksum=0; break; default: hex=0; checksum=1; break; @@ -89,17 +93,22 @@ static int cmd_nmra_command(ParseState *ps, RetransmitNode *rn) { if (checksum) nmra_addchecksum(&nmra); - nmra_encodeforpic(&nmra, &rn->pi); + nmra_encodeforpic(&nmra, pi); return 1; } static void cmd_nmra(ParseState *ps, const CmdInfo *ci) { static struct { ManualRetransmitNode *head, *tail; } mrns; - ManualRetransmitNode *mrn; - RetransmitNode *rn, rn_buf; + PicInsn *pi, pi_buf; + ManualRetransmitNode *mrn=0; + void (*retrans)(RetransmitUrgentNode *urg, Nmra *n)= 0; - if (ps->remain && ps->remain[0]=='*') { + if (ps->remain) { + if (ps->remain[0]=='*') retrans= retransmit_urgent_queue; + else if (ps->remain[0]=='%') retrans= retransmit_urgent_queue_relaxed; + } + if (retrans) { const char *mrname; int lmrname; @@ -112,52 +121,44 @@ static void cmd_nmra(ParseState *ps, const CmdInfo *ci) { !memcmp(mrn->name, mrname, lmrname)); mrn= mrn->next); if (mrn) { - retransmit_cancel(&mrn->rn); + retransmit_urgent_cancel(&mrn->rn); } else { mrn= mmalloc(sizeof(*mrn)); mrn->name= mmalloc(lmrname); memcpy(mrn->name, mrname, lmrname); mrn->lname= lmrname; } - } else { - mrn= 0; } if (!ps->remain) { - if (!mrn) { + if (!retrans) { badcmd(ps,"nmra must have slot to cancel or data to send"); return; } free(mrn->name); free(mrn); + ouack(ci); return; } - rn= mrn ? &mrn->rn : &rn_buf; - rn->pi.l= sizeof(rn->pi.d); + pi= retrans ? &mrn->rn.pi : &pi_buf; - if (!cmd_nmra_command(ps, rn)) { - if (mrn) { free(mrn->name); free(mrn); } + if (!cmd_nmra_command(ps, pi)) { + if (retrans) { free(mrn->name); free(mrn); } return; } - if (mrn) - retransmit_queue(&mrn->rn); + if (retrans) + retrans(&mrn->rn, 0); else - serial_transmit(&rn->pi); + serial_transmit(pi); + ouack(ci); } static void cmd_noop(ParseState *ps, const CmdInfo *ci) { - oprintf(&ps->cl->ch,"noop successful\n"); + ouack(ci); } -typedef struct PicCmdInfo PicCmdInfo; -struct PicCmdInfo { - const char *name; - Byte opcode; - int argbits; -}; - static void cmd_pic(ParseState *ps, const CmdInfo *ci) { const PicInsnInfo *pii; PicInsn pi; @@ -180,9 +181,10 @@ static void cmd_pic(ParseState *ps, const CmdInfo *ci) { } if (!ps_neednoargs(ps)) return; - enco_pic_anyinsn(&pi, pii, arg); + enco_pic_pii(&pi, pii, arg); } serial_transmit(&pi); + ouack(ci); } const CmdInfo toplevel_cmds[]= { diff --git a/hostside/common.h b/hostside/common.h index a1b163d..b44acbd 100644 --- a/hostside/common.h +++ b/hostside/common.h @@ -1,11 +1,22 @@ /* - * declarations common to simple test program and real controller daemon + * declarations common to + * - simple test program + * - realtime daemon + * - multiplexer daemon */ #ifndef COMMON_H #define COMMON_H #include +#include +#include + +typedef struct ParseState ParseState; +typedef struct CmdInfo CmdInfo; +typedef struct Client Client; + +extern const char *progname; /*---------- types ----------*/ @@ -24,7 +35,56 @@ typedef struct PicInsn { int l; } PicInsn; -/*---------- from serialio.c ----------*/ +/*---------- from parseutils.c ----------*/ + +struct ParseState { + Client *cl; /* used only by multiplexer */ + const char *remain; + const char *thisword; + int lthisword; +}; + +void vbadcmd(ParseState *ps, const char *fmt, va_list al) + __attribute__((format(printf,2,0))); +void badcmd(ParseState *ps, const char *fmt, ...) + __attribute__((format(printf,2,3))); + +int lstrstrcmp(const char *a, int la, const char *b); +int thiswordstrcmp(ParseState *ps, const char *b); + +int ps_word(ParseState *ps); +int ps_needword(ParseState *ps); +int ps_needhextoend(ParseState *ps, Byte *dbuf, int *len_io); +int ps_neednumber(ParseState *ps, long *r, long min, long max, const char *wh); +int ps_neednoargs(ParseState *ps); + +/*---------- macro for table lookups, with help from parseutils.c ----------*/ + +#define some_lookup(ps, infos) \ + ((const typeof(infos[0])*) \ + any_lookup((ps),(infos),sizeof((infos)[0]))) + +#define some_needword_lookup(ps, infos, what) \ + ((const typeof(infos[0])*) \ + any_needword_lookup((ps),(infos),sizeof((infos)[0]),(what))) + +const void *any_lookup(ParseState *ps, const void *infos, size_t infosz); +const void *any_needword_lookup(ParseState *ps, const void *infos, + size_t sz, const char *what); + +/*---------- from client.c ----------*/ + +struct CmdInfo { + const char *name; + void (*fn)(ParseState *ps, const CmdInfo *ci); + int xarg; +}; + +void stdin_client(void); + +extern const CmdInfo toplevel_cmds[]; /* defined in commands.c*/ + +/*---------- from utils.c ----------*/ void vdie(const char *fmt, int ev, va_list al) __attribute__((noreturn,format(printf,1,0))); @@ -32,12 +92,24 @@ void die(const char *fmt, ...) __attribute__((noreturn,format(printf,1,2))); void diee(const char *fmt, ...) __attribute__((noreturn,format(printf,1,2))); void diem(void) __attribute__((noreturn)); -void serial_open(const char *device); -void serial_transmit_now(const Byte *command, int length); +void die_hook(void); +void die_vprintf_hook(const char *fmt, va_list al) + __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 ----------*/ + +void serial_open(const char *device); +void serial_transmit_now(const Byte *command, int length); extern int serial_fd, serial_fudge_delay; diff --git a/hostside/daemons.h b/hostside/daemons.h new file mode 100644 index 0000000..0db7963 --- /dev/null +++ b/hostside/daemons.h @@ -0,0 +1,84 @@ +/* + * declarations common to + * - realtime daemon + * - multiplexer daemon + */ + +#ifndef DAEMONS_H +#define DAEMONS_H + +#include +#include + +#include "common.h" + +typedef struct CommandInput CommandInput; + +/*---------- from obc.c ----------*/ + +typedef struct OutBuffer OutBuffer; +typedef struct OutBufferChain OutBufferChain; +typedef void OutBufferError(OutBufferChain*, const char *e1, const char *e2 + /* on error: both e1 and e2 non-0. say `$e1: $e2' + * on eof: both e1 and e2 =0. */); + +struct OutBufferChain { + /* set by user: */ + char *desc; + int fd; + OutBufferError *error; + /* set/used by obc_..., oprintf, etc., only */ + int done_of_head; + struct { OutBuffer *head, *tail; } obs; +}; + +void obc_init(OutBufferChain *ch); +int obc_tryflush(OutBufferChain *ch); + /* returns 0 for all flushed or errno, including particularly EWOULDBLOCK */ + +void ovprintf(OutBufferChain *ch, const char *fmt, va_list al) + __attribute__((format(printf,2,0))); +void oprintf(OutBufferChain *ch, const char *msg, ...) + __attribute__((format(printf,2,3))); +void owrite(OutBufferChain *ch, const char *data, int l); +void voerror(OutBufferChain *ch, const char *fmt, va_list al) + __attribute__((format(printf,2,0))); + +/*---------- from cmdinput.c ----------*/ + +typedef void CommandInputCallback(ParseState *ps, CommandInput *cmdi); + +struct CommandInput { + OutBufferChain out; + CommandInputCallback *doline; + oop_read *rd; +}; + +void cmdin_new(CommandInput *cmdi, int readfd); + /* 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*/ diff --git a/hostside/dliste.h b/hostside/dliste.h new file mode 100644 index 0000000..bade3d4 --- /dev/null +++ b/hostside/dliste.h @@ -0,0 +1,73 @@ +/* + * dlist.h + * Doubly linked lists + * + * This file is part of epithet, a DNS nameserver. + * epithet is Copyright 2001 Ian Jackson. + * + * epithet is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program and documentation is distributed in the hope that it + * will be useful, but without any warranty; without even the implied + * warranty of merchantability or fitness for a particular + * purpose. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with epithet; if not, write to the Free Software Foundation, + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or email + * epithet-maint@chiark.greenend.org.uk. + */ + +#ifndef DLIST_H +#define DLIST_H + + +#define DLIST_NNODE(nodetype,linktag) struct linktag { nodetype *next, *back; } +#define DLIST_NODE(nodetype) DLIST_NNODE(nodetype, ) + +#define DLIST1_PREPEND(head,node,linkmemb) \ + ((void)((node)->linkmemb.next= (head), \ + (node)->linkmemb.back= 0, \ + ((head) ? (head)->linkmemb.back= (node) : 0), \ + (head)= (node))) + +#define DLIST1_REMOVE(head,node,link) \ + ((void) \ + ((node)->link.next ? ((node)->link.next->link.back= (node)->link.back) :0, \ + (node)->link.back ? ((node)->link.back->link.next= (node)->link.next) \ + : ((head)= (node)->link.next))) + + +#define DLIST2_NHEAD(nodetype,listtag) struct listtag { nodetype *head,*tail; } +#define DLIST2_HEAD(nodetype) DLIST2_NHEAD(nodetype, ) + +#define DLIST2_INIT(list) ((list).head= (list).tail= 0) + +#define DLIST2_REMOVE(list,node,link) \ + ((void) \ + (((node)->link.back ? ((node)->link.back->link.next= (node)->link.next) \ + : ( (list).head= (node)->link.next)), \ + ((node)->link.next ? ((node)->link.next->link.back= (node)->link.back) \ + : ( (list).tail= (node)->link.back)))) + +#define DLIST2_APPEND(list,node,link) \ + ((void) \ + ((node)->link.next= 0, \ + (node)->link.back= (list).tail, \ + ((list).tail ? ((list).tail->link.next= (node)) \ + : ((list).head= (node))), \ + (list).tail= (node))) + +#define DLIST2_PREPEND(list,node,link) \ + ((void) \ + ((node)->link.next= (list).head, \ + (node)->link.back= 0, \ + ((list).head ? ((list).head->link.back= (node)) \ + : ((list).tail= (node))), \ + (list).head= (node))) + + +#endif /*DLIST_H*/ diff --git a/hostside/dummy-trains.c b/hostside/dummy-trains.c new file mode 100644 index 0000000..f03295a --- /dev/null +++ b/hostside/dummy-trains.c @@ -0,0 +1,19 @@ +/* + */ + +FIXME this should go in a config file + +#include "realtime.h" + +#define NOTA_TRAIN { 0,0,0,0, "internal-error-not-a-train" } + +#define SPEED_MMS2INTERN(mms /* mm/s */) \ + (((mms) * SPEED_UNIT + 999) / 1000) + +const TrainInfo info_trains[]= { + /* data here is all made up ! */ + NOTA_TRAIN, + { SPEED_MMS2INTERN(1000), 20, 900, 20 }, + { SPEED_MMS2INTERN(400), 10, 50, 10 }, + { 0,0,0,0,0 } +}; diff --git a/hostside/errorcodes.h.gen b/hostside/errorcodes.h.gen new file mode 100755 index 0000000..7608367 --- /dev/null +++ b/hostside/errorcodes.h.gen @@ -0,0 +1,32 @@ +#!/usr/bin/perl + +@f= qw( + OK + Invalid + Safety + ); + + + +$decl= "const char *const errorcodelist[]"; + +print < +#include + +#include "daemons.h" + +oop_source *events; + +static void *toev_callback(oop_source *source, struct timeval tv, void *t_v) { + TimeoutEvent *toev= t_v; + toev->running= 0; + toev->callback(toev); + return OOP_CONTINUE; +} + +void toev_init(TimeoutEvent *toev) { toev->running= 0; } + +void toev_start(TimeoutEvent *toev) { + toev_stop(toev); + if (toev->duration==-1) return; + toev->running= 1; + mgettimeofday(&toev->abs); + assert(toev->duration < INT_MAX/1000); + 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); +} + +void toev_stop(TimeoutEvent *toev) { + if (!toev->running) return; + toev->running= 0; + events->cancel_time(events, toev->abs, toev_callback, toev); +} diff --git a/hostside/gui-plan.c b/hostside/gui-plan.c index 1df8766..9e1f592 100644 --- a/hostside/gui-plan.c +++ b/hostside/gui-plan.c @@ -15,7 +15,7 @@ #include #include -#include "hostside.h" +#include "common.h" #include "../layout/plan-data-format.h" #include @@ -44,6 +44,7 @@ struct SegmovfeatState { }; oop_source *events; +const char *progname= "gui-plan"; static SegmovfeatState **state, *states_head; static Display *d; @@ -56,6 +57,9 @@ static unsigned long train_pixel, owned_pixel; static const char *badcmdreport_data; static size_t badcmdreport_recsz; +void die_hook(void) { } +void die_vprintf_hook(const char *fmt, va_list al) { } + static void diex(const char *fn, const char *w) __attribute__((noreturn)); static void diex(const char *fn, const char *w) { die("Xlib call failed: %s%s%s%s", fn, diff --git a/hostside/hostside.c b/hostside/hostside.c index 6b92d86..813cab3 100644 --- a/hostside/hostside.c +++ b/hostside/hostside.c @@ -8,47 +8,6 @@ #include "../layout/dlist.h" #include "hostside.h" +#include "auproto-pic.h" -oop_source *events; FILE *dump_stream= 0; - -static void serial_error(OutBufferChain *ch, const char *e1, const char *e2) { - abort(); -} - -static OutBufferChain serial_ochain= { (char*)"serial", -1, serial_error }; - -static void *serial_exception(oop_source *evts, int fd, - oop_event evt, void *cl_v) { - die("serial port - exception"); -} - -static void *serial_readable(oop_source *evts, int fd, - oop_event evt, void *u0) { - events->cancel_fd(events, serial_fd, OOP_READ); - return OOP_CONTINUE; -} - -const char *device; - -int main(int argc, const char **argv) { - oop_source_sys *sys_events; - int r; - - device= argv[1]; - if (!device) device= "/dev/ttyS0"; - - sys_events= oop_sys_new(); if (!sys_events) diee("oop_sys_new"); - events= oop_sys_source(sys_events); assert(events); - - serial_open(device); - r= oop_fd_nonblock(serial_fd, 1); if (r) diee("nonblock(serial_fd,1)"); - serial_ochain.fd= serial_fd; - - stdin_client(); - - events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0); - events->on_fd(events, serial_fd, OOP_EXCEPTION, serial_exception, 0); - oop_sys_run(sys_events); - abort(); -} diff --git a/hostside/hostside.h b/hostside/hostside.h index db981f5..d350c07 100644 --- a/hostside/hostside.h +++ b/hostside/hostside.h @@ -1,4 +1,5 @@ /* + * OBSOLETE FILE * declarations for hostside controller daemon */ @@ -7,54 +8,6 @@ #include "common.h" -#include -#include - -typedef struct Client Client; -typedef struct ParseState ParseState; -typedef struct CmdInfo CmdInfo; -typedef struct RetransmitNode RetransmitNode; - -/*---------- from obc.c ----------*/ - -typedef struct OutBuffer OutBuffer; -typedef struct OutBufferChain OutBufferChain; -typedef void OutBufferError(OutBufferChain*, const char *e1, const char *e2); - -struct OutBufferChain { - /* set by user: */ - char *desc; - int fd; - OutBufferError *error; - /* set/used by obc_..., oprintf, etc., only */ - int done_of_head; - struct { OutBuffer *head, *tail; } obs; -}; - -void obc_init(OutBufferChain *ch); - -void ovprintf(OutBufferChain *ch, const char *fmt, va_list al) - __attribute__((format(printf,2,0))); -void oprintf(OutBufferChain *ch, const char *msg, ...) - __attribute__((format(printf,2,3))); -void owrite(OutBufferChain *ch, const char *data, int l); - -/*---------- from output.c ----------*/ - -typedef unsigned long Selector; -#include "selectors.h" - -void oovprintf(Selector sel, const char *fmt, va_list al) - __attribute__((format(printf,2,0))); -void ooprintf(Selector sel, const char *fmt, ...) - __attribute__((format(printf,2,3))); -void oowrite(Selector sel, const char *data, int l); - -void serial_transmit(const PicInsn *pi); - -void output_hex(Selector sel, const char *work, - const Byte *command, int length); - /*---------- from protocol.c ----------*/ typedef struct OrdinaryPicMessage OrdinaryPicMessage; @@ -64,77 +17,4 @@ struct OrdinaryPicMessage { int operand_bits; /* 0: no operand. >=7, insn is a 2-byte message */ }; -/*---------- from hostside.c ----------*/ - -extern oop_source *events; - -/*---------- from client.c ----------*/ - -struct ClientList { Client *head, *tail; }; -extern struct ClientList clients; - -struct Client { - OutBufferChain ch; - Client *back, *next; - Selector sel; - oop_read *rd; -}; - -struct ParseState { - Client *cl; - const char *remain; - const char *thisword; - int lthisword; -}; - -struct CmdInfo { - const char *name; - void (*fn)(ParseState *ps, const CmdInfo *ci); - int xarg; -}; - -int lstrstrcmp(const char *a, int la, const char *b); -int thiswordstrcmp(ParseState *ps, const char *b); - -int ps_word(ParseState *ps); -int ps_needword(ParseState *ps); -int ps_needhextoend(ParseState *ps, Byte *dbuf, int *len_io); -void ps_callword(ParseState *ps, const CmdInfo *infs, const char *what); -int ps_neednumber(ParseState *ps, long *r, long min, long max, const char *wh); -int ps_neednoargs(ParseState *ps); - -void vbadcmd(ParseState *ps, const char *fmt, va_list al) - __attribute__((format(printf,2,0))); -void badcmd(ParseState *ps, const char *fmt, ...) - __attribute__((format(printf,2,3))); - -void stdin_client(void); - -extern const CmdInfo toplevel_cmds[]; /* defined in commands.c*/ - -/*---------- from retransmit.c ----------*/ - -struct RetransmitNode { - /* set by caller: */ - PicInsn pi; - /* internal: */ -}; - -void retransmit_queue(RetransmitNode *rn); -void retransmit_cancel(RetransmitNode *rn); - -/*---------- macro for table lookups, with help from client.c ----------*/ - -#define some_lookup(ps, infos) \ - ((const typeof(infos[0])*) \ - any_lookup((ps),(infos),sizeof((infos)[0]))) - -#define some_needword_lookup(ps, infos, what) \ - ((const typeof(infos[0])*) \ - any_needword_lookup((ps),(infos),sizeof((infos)[0]),(what))) - -const void *any_lookup(ParseState *ps, const void *infos, size_t infosz); -const void *any_needword_lookup(ParseState *ps, const void *infos, - size_t sz, const char *what); - -#endif /*HOSTSIDE_H*/ +#endif diff --git a/hostside/main.c b/hostside/main.c index 7f2cc6d..1a8ffbd 100644 --- a/hostside/main.c +++ b/hostside/main.c @@ -19,6 +19,7 @@ #include "common.h" +const char *progname= "hostside-old"; static FILE *dump_stream= 0; static int repeat_delay= -1, iterations= -1; @@ -26,9 +27,8 @@ static const char *serial_port; static Nmra buf; -static void badusage(const char *why) { - fprintf(stderr,"bad usage: %s\n",why); exit(8); -} +void die_hook(void) { } +void die_vprintf_hook(const char *fmt, va_list al) { } static void pahex(const char **argv) { const char *data_string; diff --git a/hostside/measure-speeds b/hostside/measure-speeds new file mode 100755 index 0000000..cf3b434 --- /dev/null +++ b/hostside/measure-speeds @@ -0,0 +1,261 @@ +#!/usr/bin/tclsh8.3 + +# Put loco on track segment X5 ish, facing anticlockwise +# ./measure-speeds /dev/ttyUSB1 2 0 `seq 1 126` |tee santafe.speeds +# ^reverse + +load chiark_tcl_hbytes-1.so + +proc debug_r {m} { puts -nonewline stderr "$m\r" } +proc debug {m} { puts stderr $m } + +proc send-now {str} { + global rwy + puts -nonewline $rwy [hbytes h2raw $str] +} + +proc startup {} { + global port rwy buf last_fast_speed noise funcs_msgs loco + set rwy [open $port r+] + fconfigure $rwy -blocking no -buffering none -encoding binary \ + -translation binary + send-now 10 + after 1000 + read -nonewline $rwy + send-now 11 + fileevent $rwy readable readable + set buf {} + set last_fast_speed 0 + set noise {} + for {set i 0} {$i < 256} {incr i} { append noise \\x [hbytes random 1] } + foreach f {funcs0to4 funcs5to8} { + set m [exec ./hostside-old -s/dev/stdout $f $loco 0x1fff] + set m [hbytes raw2h $m] + lappend funcs_msgs ffff$m {} + } + lappend funcs_msgs {} +} + +proc readable {} { + global rwy buf + append buf [hbytes raw2h [read $rwy]] + while {[regexp {^((?:[89a-f].)*[0-7].)(.*)$} $buf dummy msg buf]} { + proc-msg $msg + } +} + +proc settle-cancel-after {} { + global settle_after + catch { + after cancel $settle_after + unset settle_after + } +} + +proc proc-msg {msg} { + global until detend settle_detend settle_time settle_after + switch -glob $msg { + 90?? { + if {[lsearch $settle_detend $msg]<0} { debug_r "o $msg"; return } + debug_r "= $msg" + settle-cancel-after + set settle_after [after $settle_time { set until 1}] + return + } + 01 - 02 - 03 return + 28 return + 98?? { + if {[lsearch $detend $msg]<0} { debug_r "I $msg"; return } + debug_r "* $msg" + if {$settle_time>0} { + settle-cancel-after + } else { + set until 1 + } + return + } + } + puts stderr "huh? $msg" + exit 1 +} + +# +# A6 06 +# A5 14 +# +# X7 02 +# X5 04 +# X6 0a +# X8 09 +# + +proc speed-msg {speed} { + global loco reverse + list speed126 $loco [expr {abs($speed)}] [expr {$speed<0 != $reverse}] +} + +proc run-until {speed new_detend} { + global loco until detend settle_time settle_detend + set detend $new_detend + set settle_detend {} + set settle_time -1 + catch { unset until } + xmit [speed-msg $speed] + vwait until +} + +proc run-until-not {speed new_detend new_settle} { + global loco until detend settle_time settle_detend + set detend $new_detend + set settle_detend $new_detend + hbytes xor settle_detend 0800 + set settle_time $new_settle + catch { unset until } + xmit [speed-msg $speed] + vwait until +} + +proc instruct-speed-for {speed ms} { + global loco until detend settle_detend + catch { unset until } + set detend {} + set settle_detend {} + xmit [speed-msg $speed] + after $ms { set until 1 } + vwait until +} + +proc instruct-stop-for {ms} { + instruct-speed-for 0 $ms +} + +proc bgerror {m} { + global errorInfo errorCode + puts stderr "----------\nBGERROR\n$m\n$errorInfo\n$errorCode\n----------" +} + +proc xmit-now {} { + global xmit_after xmit_msg funcs_msgs + set funcs_msg [lindex $funcs_msgs 0] + set funcs_msgs [lreplace $funcs_msgs 0 0] + lappend funcs_msgs $funcs_msg + send-now $xmit_msg$funcs_msg + set xmit_after [after 10 xmit-now] +} +proc xmit {nmral} { + global xmit_msg xmit_after detend settle_detend settle_time + set msg [eval [list exec ./hostside-old -s/dev/stdout] $nmral] + set xmit_msg [hbytes raw2h $msg] + debug "> $xmit_msg $nmral (E $detend $settle_detend $settle_time)" + catch { after cancel $xmit_after } + xmit-now +} + +proc now-ms {} { + clock clicks -milliseconds +} + +proc now-ms-click {{returnthis {}}} { + if {[string length $returnthis]} { return $returnthis } + global noise + set now [now-ms] + set f [open |[list sh -c {"$@" >/dev/dsp} x printf $noise]] + fileevent $f readable [list close $f] + return $now +} + +proc record-mm-per-s {how speed mm ms} { + debug " $how $speed: $mm / $ms" + set mmpers [expr {$mm*1.0/$ms}] + puts [format "%s %3d %g" $how $speed $mmpers] +} +proc timing-start {{now {}}} { + global start + set start [now-ms-click $now] +} +proc timing-finish {{now {}}} { + global start + set finish [now-ms-click $now] + return [expr {$finish-$start}] +} + +proc goto-slow-start-position {speed} { + global last_fast_speed + if {$last_fast_speed >= 0} { + run-until -100 {9804 980a} + set last_fast_speed -1 + } + run-until -40 9802 + run-until-not -40 9804 600 + instruct-speed-for -40 [expr {$speed*$speed}] + instruct-stop-for 1000 +} + +proc slow-speed-test {speed} { + debug "==================== $speed S ====================" + goto-slow-start-position $speed + run-until $speed 9804 + timing-start + run-until $speed 980a + record-mm-per-s S $speed 231 [timing-finish] + instruct-stop-for 100 +} + +proc fast-speed-test {speed} { + global last_fast_speed + debug "==================== $speed F ====================" + set start [now-ms] + set acceltime [expr {abs($last_fast_speed-$speed)*100}] + debug "A $acceltime" + set finish [expr {$start + $acceltime}] + while 1 { + # we go round and round, keeping track of roughly where we + # are, until there has been enough time to get the speed right + run-until $speed 9809 + run-until $speed 9814 + set deficit [expr {$finish - [now-ms]}] + debug "W $deficit" + if {$deficit < 0} break + } + set last_fast_speed $speed + set ms 0; set mm 0 + set mslaps 0; set mmlaps 0 + while {$ms < 2000} { + run-until $speed 9804 + set now [now-ms-click] + if {$ms} { + incr mslaps [timing-finish $now] + incr mmlaps 3624 + debug "++ $mmlaps / $mslaps" + } + timing-start $now + + run-until $speed 980a + + incr ms [timing-finish] + incr mm 231 + debug "+- $mm / $ms" + run-until-not $speed 980a 600 + } + record-mm-per-s F $speed $mm $ms + if {$mslaps} { record-mm-per-s L $speed $mmlaps $mslaps } +} + +proc speed-test {speed} { + if {$speed<50} { + slow-speed-test $speed + } else { + fast-speed-test $speed + } +} + +set port [lindex $argv 0] +set loco [lindex $argv 1] +set reverse [lindex $argv 2] + +startup + +foreach s [lrange $argv 3 end] { + speed-test $s +} +instruct-stop-for 500 diff --git a/hostside/multiplex.c b/hostside/multiplex.c new file mode 100644 index 0000000..1d30794 --- /dev/null +++ b/hostside/multiplex.c @@ -0,0 +1,4 @@ +void vbadcmd(ParseState *ps, const char *fmt, va_list al) { + voerror(&ps->ci->out,fmt,al); +} + diff --git a/hostside/multiplex.h b/hostside/multiplex.h new file mode 100644 index 0000000..0c66f2c --- /dev/null +++ b/hostside/multiplex.h @@ -0,0 +1,44 @@ +/* + * multiplexer daemon + * declarations + */ + +#ifndef MULTIPLEX_H +#define MULTIPLEX_H + +#include "daemons.h" + +/*---------- from output.c ----------*/ + +typedef unsigned long Selector; +#include "selectors.h" + +void sovprintf(Selector sel, const char *fmt, va_list al) + __attribute__((format(printf,2,0))); +void soprintf(Selector sel, const char *fmt, ...) + __attribute__((format(printf,2,3))); +void sowrite(Selector sel, const char *data, int l); + +void serial_transmit(const PicInsn *pi); + +void output_hex(Selector sel, const char *work, + const Byte *command, int length); + + + + + unredacted + +struct ClientList { Client *head, *tail; }; +extern struct ClientList clients; + +struct Client { + CommandInput ci; + Client *back, *next; + Selector sel; +}; + + + + +#endif /*MULTIPLEX_H*/ diff --git a/hostside/obc.c b/hostside/obc.c index bc272de..040ee6a 100644 --- a/hostside/obc.c +++ b/hostside/obc.c @@ -1,4 +1,7 @@ -/**/ +/* + * daemons + * output buffer chains + */ #include #include @@ -6,7 +9,7 @@ #include #include -#include "hostside.h" +#include "daemons.h" #include "../layout/dlist.h" struct OutBuffer { @@ -15,20 +18,15 @@ struct OutBuffer { int l; }; -static void *writeable(oop_source *evts, int fd, - oop_event evt, void *ch_v) { - OutBufferChain *ch= ch_v; +int obc_tryflush(OutBufferChain *ch) { OutBuffer *ob; int r; - assert(fd == ch->fd); - assert(evt == OOP_WRITE); - for (;;) { ob= ch->obs.head; if (!ob) { - events->cancel_fd(events, fd, OOP_WRITE); - return OOP_CONTINUE; + events->cancel_fd(events, ch->fd, OOP_WRITE); + return 0; } if (ch->done_of_head == ob->l) { LIST_UNLINK(ch->obs, ob); @@ -40,8 +38,9 @@ static void *writeable(oop_source *evts, int fd, r= write(ch->fd, ob->m + ch->done_of_head, ob->l - ch->done_of_head); if (r==-1) { if (errno==EINTR) continue; - if (errno==EWOULDBLOCK) return OOP_CONTINUE; + if (errno==EWOULDBLOCK) return errno; ch->error(ch,"write",strerror(errno)); + return errno; } assert(r>=0); ch->done_of_head += r; @@ -49,6 +48,15 @@ static void *writeable(oop_source *evts, int fd, } } +static void *writeable(oop_source *evts, int fd, + oop_event evt, void *ch_v) { + OutBufferChain *ch= ch_v; + assert(fd == ch->fd); + assert(evt == OOP_WRITE); + obc_tryflush(ch); + return OOP_CONTINUE; +} + static void addlink(OutBufferChain *ch, OutBuffer *ob) { if (!ch->obs.head) events->on_fd(events, ch->fd, OOP_WRITE, writeable, ch); @@ -56,7 +64,10 @@ static void addlink(OutBufferChain *ch, OutBuffer *ob) { } void obc_init(OutBufferChain *ch) { + int r; ch->done_of_head= 0; + r= oop_fd_nonblock(ch->fd, 1); + if (r) diee("nonblock(OutBufferChain->fd,1)"); LIST_INIT(ch->obs); } @@ -75,6 +86,12 @@ void oprintf(OutBufferChain *ch, const char *msg, ...) { va_end(al); } +void voerror(OutBufferChain *ch, const char *fmt, va_list al) { + oprintf(ch,"error "); + ovprintf(ch,fmt,al); + owrite(ch,"\n",1); +} + void owrite(OutBufferChain *ch, const char *data, int l) { OutBuffer *ob; ob= mmalloc(sizeof(*ob)); diff --git a/hostside/output.c b/hostside/output.c index b432222..e69de29 100644 --- a/hostside/output.c +++ b/hostside/output.c @@ -1,70 +0,0 @@ -/* - * transmissions to clients - */ - -#include -#include - -#include "hostside.h" -#include "auproto-pic.h" - -#define FOR_OOS(s) do{ \ - Client *cl, *next_cl; \ - for (cl= clients.head; \ - cl; \ - cl= next_cl) { \ - OutBufferChain *ch= &cl->ch; \ - next_cl= cl->next; \ - if (!(sel & cl->sel)) continue; \ - s; \ - } \ - }while(0) - -void oovprintf(Selector sel, const char *fmt, va_list al) { - FOR_OOS( ovprintf(ch, fmt, al) ); -} - -void ooprintf(Selector sel, const char *fmt, ...) - { va_list al; va_start(al,fmt); oovprintf(sel,fmt,al); va_end(al); } - -void oowrite(Selector sel, const char *data, int l) { - FOR_OOS( owrite(ch, data, l) ); -} - -void output_hex(Selector sel, const char *word, - const Byte *command, int length) { - ooprintf(sel, "%s", word); - while (length) { - ooprintf(sel, " %02x", *command++); - length--; - } - ooprintf(sel, "\n"); -} - -void serial_transmit(const PicInsn *pi) { - const PicInsnInfo *pii; - unsigned val; - - pii= pi->l > 0 ? lookup_byopcode(pi->d[0], pic_command_infos) : 0; - - if (pii) { - val= pi->d[0]; - if (pii->argbits <= 6) { - if (pi->l != 1) pii= 0; - } else { - if (pi->l == 2) { val <<= 8; val |= pi->d[1]; } - else pii= 0; - } - } - - if (!pii) - ooprintf(sel_picio, "picio out unknown\n"); - else if (!pii->argbits) - ooprintf(sel_picio, "picio out %s\n", pii->name); - else - ooprintf(sel_picio, "picio out %s %u\n", pii->name, - val & ((1u << pii->argbits) - 1)); - - output_hex(sel_picioh, "picioh out", pi->d, pi->l); - serial_transmit_now(pi->d, pi->l); -} diff --git a/hostside/parse-proto-spec b/hostside/parse-proto-spec index 1698eba..c35319e 100755 --- a/hostside/parse-proto-spec +++ b/hostside/parse-proto-spec @@ -92,7 +92,13 @@ sub process_line () { $v{yn}= $yval; $v{dname}= $dname; $v{cname}= $cname; + $v{noiselevel}= + ($cname =~ m/nmradone/ ? 3 : + $cname =~ m/p[io]ng/ ? 2 : + $cname =~ m/detect/ ? 1 : + 0); $v{cnameyn}= $cname.$yval; + $v{cnameynu}= uc($cname.$yval); $v{opcode}= b2xh($opcode, 0); $v{opcodeyn}= b2xh($opcode, $ybit * $yval); $v{opcodemask}= b2xh($opcodemask, 0); diff --git a/hostside/parseutils.c b/hostside/parseutils.c index 99e1385..7cb962a 100644 --- a/hostside/parseutils.c +++ b/hostside/parseutils.c @@ -1,11 +1,14 @@ -/**/ +/* + * common + * utilities for helping parsing + */ #include #include #include #include -#include "hostside.h" +#include "common.h" void badcmd(ParseState *ps, const char *fmt, ...) { va_list al; @@ -90,6 +93,27 @@ int ps_needhextoend(ParseState *ps, Byte *d, int *len_io) { return 1; } + +const void *any_lookup(ParseState *ps, const void *inf, size_t sz) { + const char *tname; + + for (; + (tname= *(const char *const*)inf); + inf= (const char*)inf + sz) + if (!thiswordstrcmp(ps,tname)) + return inf; + return 0; +} + +const void *any_needword_lookup(ParseState *ps, const void *infs, + size_t sz, const char *what) { + const void *r; + if (!ps_needword(ps)) return 0; + r= any_lookup(ps,infs,sz); + if (!r) { badcmd(ps,"unknown %s",what); return 0; } + return r; +} + int lstrstrcmp(const char *a, int la, const char *b) { int lb, minl, r; diff --git a/hostside/realtime.c b/hostside/realtime.c new file mode 100644 index 0000000..4aad568 --- /dev/null +++ b/hostside/realtime.c @@ -0,0 +1,374 @@ +/* + * main program for realtime control + */ + +#include "realtime.h" + +const char *progname= "realtime"; + +/*---------- global variables ----------*/ + +CommandInput cmdi; +int picio_send_noise= 1; + +static const char *device= "/dev/ttya0"; + +/*---------- general event handling ----------*/ + +static void comms_error(const char *ch, const char *e1, + const char *e2 /* may be 0 */) { + if (!e1 && !e2) e1= "end of file"; + die("communications error: %s: %s%s%s", ch, e1, e2?": ":"", e2?e2:""); +} + +static void *read_exception(oop_source *evts, int fd, + oop_event evt, void *cl_v) { + const char *ch; + char bufc; + int r; + + ch= (fd==UPO->fd ? UPO->desc : + fd==serial_fd ? "serial port" : + 0); + + r= read(fd, &bufc, 1); + if (r==-1) comms_error(ch, "read error", strerror(errno)); + else if (r==0) comms_error(ch, "reports exception, reads EOF", 0); + else comms_error(ch, "reports exception, but readable", 0); + + return OOP_CONTINUE; +} + +/*---------- logging etc. ----------*/ + +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 *trapn, *segpn; + char *s; + int r; + + segpn= seg ? seg->i->pname : 0; + trapn= tra ? tra->pname : 0; + + r= asprintf(&s, "%s%s%s%s%s", + segpn||trapn ? ":" : "", + segpn ? " @" : "", + segpn ? segpn : "", + trapn ? " " : "", + trapn ? trapn : ""); + if (r<0) diee("vasprintf failed in transegn2suffixstring"); + return s; +} + +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 (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, Train *tra, const SegmentInfo *segi, + const char *fmt,...) { + va_list al; + va_start(al,fmt); + vlogmsg(ec,tra,segi,fmt,al); + va_end(al); +} + +void safety_vpanic(Train *tra, Segment *seg,const char *fmt,va_list al) { + char *msg, *where; + PicInsn piob; + + enco_pic_off(&piob); + serial_transmit(&piob); + + if (vasprintf(&msg,fmt,al) < 0) + diee("vasprintf failed in safety_vpanic fmt=\"%s\"", fmt); + + where= transegn2suffixstring(tra,seg); + die("fatal safety problem: %s%s", msg, where); +} + +void safety_panic(Train *tra, Segment *seg, const char *fmt, ...) { + va_list al; + va_start(al,fmt); + safety_vpanic(tra, seg, fmt, al); +} + +ErrorCode safety_problem(Train *tra, Segment *seg, const char *fmt,...) { + va_list al; + va_start(al,fmt); + vlogmsg(EC_Safety, tra, seg?seg->i:0, fmt, al); + va_end(al); + return EC_Safety; +} + +/*---------- printing nmra data ----------*/ + +typedef struct { + const Byte *i, *o; + int npending; + unsigned datapending; +} NmraDecCtx; + +static void nmradec_init(NmraDecCtx *d, const PicInsn *pi) { + d->i= pi->d; + d->o= pi->d + pi->l; + d->npending= 0; + d->datapending= 0; +} + +static void nmradec_getbits(NmraDecCtx *d, int *nbits_io, unsigned *bits_r) { + int use= *nbits_io; + while (d->npending < use) { + if (d->i >= d->o) break; + d->datapending <<= 7; + d->datapending |= *d->i & 0x7f; + d->npending += 7; + if (!(*d->i++ & 0x80)) break; + } + if (d->npending < use) use= d->npending; + d->npending -= use; + *bits_r= (d->datapending >> d->npending) & ~(~0u << use); + *nbits_io= use; +} + +static void nmra_decodeforpic(const PicInsn *pi, + void (*on_idle)(void *u, int), + void (*on_packet)(void *u, const Nmra*), + void (*on_error)(void *u, int reasonchar), + void *u) { + /* reasonchars: + * # - bad checksum + * ! - too little idle + * $ - truncated + */ + NmraDecCtx d; + Nmra n; + int need_idle=7, got_idle=0; + unsigned got, csum; + int nbits; + + nmradec_init(&d,pi); + for (;;) { + nbits=1; nmradec_getbits(&d,&nbits,&got); + if (got) { got_idle++; continue; } + if (got_idle) on_idle(u,got_idle); + if (!nbits) return; + if (got_idle < need_idle) on_error(u,'!'); + got_idle= 0; + + n.l=0; csum=0; + do { + nbits=8; nmradec_getbits(&d,&nbits,&got); + n.d[n.l++]= got; + csum ^= got; + if (nbits<8) on_error(u,'$'); + + nbits=1; nmradec_getbits(&d,&nbits,&got); + if (nbits<1) on_error(u,'$'); + } while (nbits && !got); + + if (csum) on_error(u,'#'); + else n.l--; + + on_packet(u,&n); + need_idle= 14; + } +} + +static void opn_idle(void *u, int idle) { + static const char dots[]= "......."; /* at least base/2 */ + const int base= 14; + int largers= idle / base; + int units= idle % base; + + oprintf(UPO," %.*s%.*s%.*s", + units/2, dots, + largers, ":::::::" /* enough */, + (units+1)/2, dots); +} +static void opn_error(void *u, int rc) { oprintf(UPO," %c",rc); } +static void opn_packet(void *u, const Nmra *n) { + int i; + const char *delim= " <"; + for (i=0; il; i++) { + oprintf(UPO,"%s%02x",delim,n->d[i]); + delim=" "; + } + oprintf(UPO,">"); +} + +static void oprint_nmradata(const PicInsn *pi) { + oprintf(UPO,"picio out nmradata"); + nmra_decodeforpic(pi, opn_idle,opn_packet,opn_error, 0); + oprintf(UPO,"\n"); +} + +/*---------- command channel handling (oop_read, obc) ----------*/ + +static void command_doline(ParseState *ps, CommandInput *cmdi) { + const CmdInfo *ci; + ci= some_needword_lookup(ps, toplevel_cmds, "command"); + if (ci) ci->fn(ps,ci); +} + +void oupicio(const char *dirn, const PicInsnInfo *pii, int objnum) { + if (!pii->argbits) + oprintf(UPO, "picio %s %s\n", dirn, pii->name); + else + oprintf(UPO, "picio %s %s %u\n", dirn, pii->name, objnum); +} + +void vbadcmd(ParseState *ps, const char *fmt, va_list al) { + voerror(UPO,fmt,al); +} + +static void obc_error(OutBufferChain *ch, const char *e1, const char *e2) { + comms_error(ch->desc, e1, e2); +} + +void ouhex(const char *word, const Byte *command, int length) { + oprintf(UPO, "%s", word); + while (length) { + oprintf(UPO, " %02x", *command++); + length--; + } + oprintf(UPO, "\n"); +} + +void die_vprintf_hook(const char *fmt, va_list al) { + ovprintf(UPO, fmt, al); +} + +void die_hook(void) { + int e; + e= obc_tryflush(UPO); + if (e) fprintf(stderr,"(unwritten command output: %s)\n",strerror(e)); +} + +/*---------- serial input (via oop) ----------*/ + +static PicInsn serial_buf; + +static void *serial_readable(oop_source *evts, int fd, + oop_event evt, void *u0) { + int r, buf_used; + + r= read(serial_fd, serial_buf.d + serial_buf.l, + sizeof(serial_buf.d) - serial_buf.l); + + if (r==0) die("serial port - eof"); + if (r==-1) { + if (errno == EWOULDBLOCK || errno == EINTR) + return OOP_CONTINUE; + diee("serial port - read error"); + } + assert(r>0); + + buf_used= serial_buf.l + r; + + for (;;) { + serial_buf.l= buf_used; + serial_moredata(&serial_buf); + if (!serial_buf.l) break; + buf_used -= serial_buf.l; + memmove(serial_buf.d, serial_buf.d + serial_buf.l, buf_used); + if (!buf_used) break; + } + serial_buf.l= buf_used; + return OOP_CONTINUE; +} + +/*---------- serial port output (not via liboop) ----------*/ + +void serial_transmit(const PicInsn *pi) { + const PicInsnInfo *pii; + int objnum, suppress=0; + + if ((pi->d[0] & 0xf8) == 0x90) { + SegmentNum segn; + const SegmentInfo *segi; + const char *delim; + + oprintf(UPO,"picio out polarity <"); + for (segn=0, segi=info_segments, delim=""; + segn < info_nsegments; + segn++, segi++) { + if (!segi->invertible) continue; + if (!picinsn_polarity_testbit(pi,segi)) continue; + oprintf(UPO,"%s%s", delim, segi->pname); + delim= ","; + } + oprintf(UPO,">\n"); + } else if (pi->d[0] == 0xff) { + if (picio_send_noise < 3) + suppress= 1; + else + oprint_nmradata(pi); + } else { + picinsn_decode(pi, pic_command_infos, &pii, &objnum); + if (!pii) + oprintf(UPO, "picio out unknown\n"); + else if (pii->noiselevel > picio_send_noise) + suppress= 1; + else + oupicio("out",pii,objnum); + } + + if (!suppress && picio_send_noise >= 2) + ouhex("picioh out", pi->d, pi->l); + + /* note that the serial port is still in nonblocking mode. if + * we ever buffer up far enough that the kernel wants to make us + * block, we should die! */ + serial_transmit_now(pi->d, pi->l); +} + +/*---------- initialisation ----------*/ + +int main(int argc, const char **argv) { + oop_source_sys *sys_events; + const char *arg; + int r; + + 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 'v': picio_send_noise= atoi(arg); break; + default: badusage("unknown option"); + } + } + + records_parse(argv); + + 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); + + sta_startup(); + oop_sys_run(sys_events); + abort(); +} + +DEFINE_ERRORCODELIST_DATA diff --git a/hostside/realtime.h b/hostside/realtime.h new file mode 100644 index 0000000..94413e8 --- /dev/null +++ b/hostside/realtime.h @@ -0,0 +1,111 @@ +/* + * declarations for realtime daemon + */ + +#ifndef REALTIME_H +#define REALTIME_H + +#include "daemons.h" +#include "auproto-pic.h" +#include "dliste.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../layout/layout-data.h" + +typedef struct Segment Segment; +typedef struct Train Train; + +/*---------- from retransmit.c ----------*/ + +typedef struct RetransmitRelaxedNode RetransmitRelaxedNode; +typedef union RetransmitUrgentNode RetransmitUrgentNode; +typedef unsigned Retransmit__Time; + + /* Contents of the retransmission nodes is generally all for use by + * retransmit.c only; as a special exception, caller may edit pi + * directly. Normally, though, pi is set by supplying an NMRA + * command to one of the _queue functions; iff the Nmra* is + * non-null, _queue will add an NMRA checksum and update pi. + */ +struct RetransmitRelaxedNode { + PicInsn pi; + DLIST_NODE(RetransmitRelaxedNode) rr; +}; +union RetransmitUrgentNode { + PicInsn pi; + struct { + RetransmitRelaxedNode relaxed; + int ix; + Retransmit__Time when; + DLIST_NODE(RetransmitUrgentNode) queue; + } u; +}; + +void retransmit_start(void); +void retransmit_something(void); + +void retransmit_relaxed_queue(RetransmitRelaxedNode *rn, Nmra *n); +void retransmit_relaxed_requeue(RetransmitRelaxedNode *rn, Nmra *n); +void retransmit_relaxed_cancel(RetransmitRelaxedNode *rn); + +void retransmit_urgent_queue(RetransmitUrgentNode *rn, Nmra *n); +void retransmit_urgent_queue_relaxed(RetransmitUrgentNode *urg, Nmra *n); +void retransmit_urgent_requeue(RetransmitUrgentNode *rn, Nmra *n); +void retransmit_urgent_cancel(RetransmitUrgentNode *rn); + + /* ... NB: these are NOT idempotent. Use _requeue it's queued; + * _requeue is just _cancel followed by queue. */ + +/*---------- global variables, in realtime.c ----------*/ + +extern CommandInput cmdi; +extern int picio_send_noise; + +#define UPO (&(cmdi.out)) + +/*---------- from/for startup.c ----------*/ + +#include "stastate.h" + +void sta_startup(void); +void serial_moredata(PicInsn *buf); + +extern StartupState sta_state; +extern const char *const stastatelist[]; + +/*---------- from/for record.c ----------*/ + +void records_parse(const char **argv); + +/*---------- from/for realtime.c ----------*/ + +void oupicio(const char *dirn, const PicInsnInfo *pii, int objnum); +void ouhex(const char *word, const Byte *command, int length); + +void serial_transmit(const PicInsn *pi); + +/*---------- from actual.c ----------*/ + +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. */ + +/*---------- tbi ----------*/ + +void abandon_run(void); + + +#include "safety.h" + + +#endif /*REALTIME_H*/ diff --git a/hostside/record-l.l b/hostside/record-l.l new file mode 100644 index 0000000..20f845b --- /dev/null +++ b/hostside/record-l.l @@ -0,0 +1,39 @@ +/* -*- fundamental -*- */ + +%{ +#include "record.h" +%} + +%option warn +%option batch +%option noyywrap +%option yylineno +%option prefix="record_yy" +%option header-file="record-l.h" + +%{ +#define STR record_yylval.name= record_tempzone_strdup(yytext); return +%} + +%% + +train { STR TRAIN; } +seg { STR SEG; } +is { STR IS; } +at { STR AT; } +has { STR HAS; } +step { STR STEP; } +end { STR END; } + +[A-Za-z][A-Za-z0-9_]+ { STR IDENT; } + +[0-9]{0,5} { record_yylval.num= strtoul(yytext,0,10); return NUM; } +[0-9]{6} { record_yyerror("number too long"); } + +[-+:=~/] { record_yylval.name= 0; return yytext[0]; } + +\#.*\n|\n { record_yylval.name= 0; return NL; } + +[ \t] { } + +. { record_yyerror("lexically invalid input"); } diff --git a/hostside/record-y.y b/hostside/record-y.y new file mode 100644 index 0000000..5ba3d1c --- /dev/null +++ b/hostside/record-y.y @@ -0,0 +1,61 @@ +/* -*- fundamental -*- */ + +%{ +#include "record.h" +#include "record-l.h" +%} + +%union { + const char *name; + Train *train; + Segment *seg; + int num; +} + +%token TRAIN SEG IS AT HAS STEP END IDENT +%token NL +%token NUM + +%type ident +%type train +%type seg +%type backwards + +%defines +%error-verbose +%name-prefix="record_yy" + +%% + +file: end + | line NL { record_tempzone_clear(); } file + +line: /* empty */ + | TRAIN train IS NUM NUM '+' NUM '+' NUM + { 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 + { 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 { $$= record_pname2seg($1); } +train: ident { $$= record_pname2train($1); } + +end: END NL + +%% diff --git a/hostside/record.c b/hostside/record.c new file mode 100644 index 0000000..45bcca6 --- /dev/null +++ b/hostside/record.c @@ -0,0 +1,268 @@ +/* + * ASCII-format record and config file reader/writer + * + * File format: + * + * max-trains num + * train at [-]:+- + * train is ++ + * train step = / + * seg has [-]|$ + * seg at + */ + +#include + +#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", 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; tranpname, 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; + ii->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; + iaccel.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; + iaccel.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; + imovposcomb= -1; + seg->i= segi; + } + + trains= (void*)(segments + NUM_SEGMENTS); + for (i=0, tra=trains, trap=train_pnames; + ipname= *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(); +} diff --git a/hostside/record.h b/hostside/record.h new file mode 100644 index 0000000..366bd29 --- /dev/null +++ b/hostside/record.h @@ -0,0 +1,27 @@ +/* + */ + +#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*/ diff --git a/hostside/retransmit-table.h.gen b/hostside/retransmit-table.h.gen new file mode 100755 index 0000000..469d230 --- /dev/null +++ b/hostside/retransmit-table.h.gen @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +$count= 20; +$exp= 1.3; +$first= 1.0; + +print <> 1) + +static PicInsn linefill; + +static DLIST2_HEAD(RetransmitRelaxedNode) relaxed; +static Retransmit__Time elapsed; +static PerSpeedyTrans speedies[] = SPEEDIESINIT; + +static void retransmit_this(const PicInsn *pi) { + serial_transmit(pi); + elapsed++; +} + +void retransmit_start(void) { + Nmra n; + + enco_nmra_idle(&n); + nmra_addchecksum(&n); + nmra_encodeforpic(&n, &linefill); + + retransmit_something(); + retransmit_something(); + retransmit_something(); } -void retransmit_cancel(RetransmitNode *rn) { +void retransmit_something(void) { + PerSpeedyTrans *spd; + RetransmitUrgentNode *urg; + RetransmitRelaxedNode *rlx; + int ix; + + for (ix=0, spd=speedies; + ix < SPEEDYCOUNT; + ix++, spd++) { + urg= spd->queue.head; + if (!urg) continue; + if (elapsed - urg->u.when > RETRANSMIT_TIME_SIGNED_MAX) continue; + + /* found one to transmit: */ + DLIST2_REMOVE(spd->queue,urg,u.queue); + if (++ix < SPEEDYCOUNT) { + urg->u.ix= ix; + urg->u.when= elapsed + spd->interval; + spd++; + DLIST2_APPEND(spd->queue,urg,u.queue); + } else { + urg->u.ix= -1; + } + retransmit_this(&urg->pi); + return; + } + + rlx= relaxed.head; + if (rlx) { + DLIST2_REMOVE(relaxed,rlx,rr); + DLIST2_APPEND(relaxed,rlx,rr); + retransmit_this(&rlx->pi); + return; + } + + serial_transmit(&linefill); +} + +void retransmit_relaxed_queue(RetransmitRelaxedNode *rn, Nmra *n) { + if (n) { nmra_addchecksum(n); nmra_encodeforpic(n, &rn->pi); } + DLIST2_PREPEND(relaxed,rn,rr); +} +void retransmit_relaxed_requeue(RetransmitRelaxedNode *rn, Nmra *n) { + retransmit_relaxed_cancel(rn); + retransmit_relaxed_queue(rn, n); +} +void retransmit_relaxed_cancel(RetransmitRelaxedNode *rlx) { + DLIST2_REMOVE(relaxed,rlx,rr); +} + +void retransmit_urgent_queue(RetransmitUrgentNode *urg, Nmra *n) { + retransmit_relaxed_queue(&urg->u.relaxed, n); + urg->u.ix= 0; + urg->u.when= elapsed; + DLIST2_APPEND(speedies[0].queue,urg,u.queue); +} +void retransmit_urgent_queue_relaxed(RetransmitUrgentNode *urg, Nmra *n){ + retransmit_relaxed_queue(&urg->u.relaxed, n); + urg->u.ix= -1; + urg->u.when= elapsed; +} +void retransmit_urgent_requeue(RetransmitUrgentNode *rn, Nmra *n) { + retransmit_urgent_cancel(rn); + retransmit_urgent_queue(rn, n); +} +void retransmit_urgent_cancel(RetransmitUrgentNode *urg) { + if (urg->u.ix >= 0) + DLIST2_REMOVE(speedies[urg->u.ix].queue,urg,u.queue); + retransmit_relaxed_cancel(&urg->u.relaxed); } diff --git a/hostside/safety.c b/hostside/safety.c index 935a0ab..179f0e4 100644 --- a/hostside/safety.c +++ b/hostside/safety.c @@ -1,94 +1,92 @@ /* + * realtime + * safety core algorithm */ #include #include -#include "layoutinfo.h" -#include "safety.h" +#include "realtime.h" -static void seg_clear_stale(SegmentState *seg) { +int n_trains; +Train *trains; +Segment *segments; + +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[1]; /* count of (switchable) segments, + int invert_count[2]; /* count of (switchable) segments, * invert_count[0]: inverted from train's pov * 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) { - State *s= &safety_state; - 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; @@ -101,9 +99,8 @@ static void lay_train_pass(LayTrainState *l, } static void lay_train_inversions(LayTrainState *l) { - State *s= &safety_state; SegmentNum segn; - SegmentState *seg; + Segment *seg; const SegmentInfo *segi; int train_be_inverted, seg_be_inverted; @@ -114,28 +111,27 @@ static void lay_train_inversions(LayTrainState *l) { 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) { - State *s= &safety_state; 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; } @@ -144,28 +140,23 @@ static void lay_train_done(LayTrainState *l) { } } -static ErrorCode lay_train(TrainNum tran, long added_slop) { - State *s= &safety_state; - TrainState *tra= &s->trains[tran]; - const TrainInfo *trai= &info_trains[tran]; - 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; - head= tra->backwards ? trai->tail : trai->head; + head= tra->backwards ? tra->tail : tra->head; headslop= head + added_slop; /* 1st pass: @@ -186,8 +177,8 @@ static ErrorCode lay_train(TrainNum tran, long added_slop) { trackloc_reverse(&tloc); seg->tr_updated= 0; /* we're about to do this one again */ - tail= tra->backwards ? trai->head : trai->tail; - taildet= trai->detectable + tail; + tail= tra->backwards ? tra->head : tra->tail; + taildet= tra->detectable + tail; lay_train_pass(&l, tloc, taildet, 0, 1, 1); @@ -197,104 +188,83 @@ static ErrorCode lay_train(TrainNum tran, long added_slop) { return l.ec; } -static void setspeed(TrainNum tran, Speed newspeed) { - /* does not lay the train, caller must do that (or have done it, - * in which case they should already have set tra->speed). */ - State *s= &safety_state; - TrainState *tra= &s->trains[tran]; - - tra->speed= newspeed; - actual_setspeed(tran); - speedmanager_speedchange_notify(tran); -} - -void safety_notify_detection(SegmentNum segn) { - State *s= &safety_state; - SegmentState *seg= &s->segments[segn]; - TrainNum tran= seg->owner; - TrainState *tra= &s->trains[tran]; +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) { - if (tra->speed < AUTOSTOP_MAXSPEED && - 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 - * (ie, just buffers next). */ - tra->maxinto= AUTOSTOP_UNCERTAINTY; + if (seg->cm_autostop) { seg->cm_autostop= 0; - setspeed(tran, 0); + if (!tra->estopping) { + 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 + * (ie, just buffers next). */ + tra->maxinto= AUTOSTOP_UNCERTAINTY; + } } tra->uncertainty= tra->maxinto; - - ec= lay_train(tran, 0); - if (ec) { - logmsg(ec, tran, segn, "emergency stop"); - safety_emergencystop(tran); + + if (!tra->estopping) { + ec= lay_train(tra, 0); + if (ec) { + logmsg(ec,tra,seg->i, "emergency stop"); + safety_emergencystop(tra); + } } } -void safety_emergencystop(TrainNum tran) { - State *s= &safety_state; +void safety_emergencystop(Train *tra) { ErrorCode ec; - TrainState *tra= &s->trains[tran]; - tra->speed= 0; - actual_emergencystop(tran); - ec= lay_train(tran, ESTOP_UNCERTAINTY); - if (ec) safety_panic(tran, NOTA(Segment), "emergency stop forbidden!"); - speedmanager_speedchange_notify(tran); + if (tra->estopping) return; + + ec= lay_train(tra, ESTOP_UNCERTAINTY); + if (ec) safety_panic(tra,0, "emergency stop forbidden!"); + speedmanager_emergencystop(tra); } -void safety_requestspeed(TrainNum tran, long newspeed) { - State *s= &safety_state; +ErrorCode safety_requestspeed(Train *tra, long newspeed) { long oldspeed; - TrainState *tra= &s->trains[tran]; 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"); } - setspeed(tran, oldspeed); - ec= lay_train(tran, 0); - if (ec) - safety_panic(tran, NOTA(Segment), "countermanding" + oprintf(UPO, "countermanded %s %ld %ld\n", + tra->pname, oldspeed, newspeed); + revert_ec= lay_train(tra, 0); + if (revert_ec) + safety_panic(tra,0, "countermanding" " speed change insufficient!"); - return; } - - setspeed(tran, newspeed); -} - - -int main(void) { - printf("%d\n",(int)sizeof(State)); - return 0; + return ec; } diff --git a/hostside/safety.h b/hostside/safety.h index 2408248..d741131 100644 --- a/hostside/safety.h +++ b/hostside/safety.h @@ -3,13 +3,15 @@ #ifndef SAFETY_H #define SAFETY_H +#include + #include "../layout/layout-data.h" #include "layoutinfo.h" +#include "errorcodes.h" /*========== more basic types etc. ==========*/ typedef short TimeInterval; /*ms*/ -typedef int ErrorCode; /*---------- units and scales ----------*/ @@ -25,48 +27,75 @@ typedef int ErrorCode; /*========== state of the layout ==========*/ typedef struct { - SegmentNum foredetect; /* train's detectable part is at most maxinto */ + 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 speed; -} TrainState; + 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 { + 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; +}; -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 logmsg(ErrorCode ec, TrainNum tran, SegmentNum segn, const char *fmt,...) - __attribute__((format(printf,4,5))); +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))); -void safety_panic(TrainNum tran, SegmentNum segn, const char *fmt,...) +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)); -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 */ /*========== safety.c ==========*/ /* @@ -75,26 +104,38 @@ ErrorCode safety_problem(TrainNum tran, SegmentNum segn, const char *fmt,...) * etc.). */ -void safety_emergencystop(TrainNum); +void safety_emergencystop(Train*); /* Callable directly in response to application command. */ -void 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. - * Will result in a call to speedmanager_speedchange_notify (and of - * course to actual_setspeed. Speed manager must apply accel/decel - * curve so that if safety.c agrees the command, the actual speed of - * the train changes straight away (at least for decel). + * user request. Any error will have been logged. On success, + * ->speed has been updated. Speed manager is responsible for + * calling actual_setspeed. */ -void safety_notify_detection(SegmentNum segn); - /* To be called by actual.c when new train detection occurs. */ +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. */ /*========== speedmgr.c ==========*/ -void speedmanager_speedchange_notify(TrainNum tran); - /* To be called only by safety.c, whenever speed is actually set. - * New speed has already been recorded in State. */ +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. @@ -102,15 +143,15 @@ void speedmanager_speedchange_notify(TrainNum tran); * repeating the NMRA commands and redacting detection information. * * In general, when State information is shared between actual.c - * and safety.c, actual.c is responsible for modifying State, and + * and safety.c, safety.c is responsible for modifying State, and * 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 @@ -127,7 +168,7 @@ void actual_inversions_done(void); 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 */ }; @@ -164,8 +205,9 @@ const SegmentLinkInfo *trackloc_segmentlink(const TrackLocation *tloc, #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) diff --git a/hostside/santafe-1.speeds b/hostside/santafe-1.speeds new file mode 100644 index 0000000..2cdbd4a --- /dev/null +++ b/hostside/santafe-1.speeds @@ -0,0 +1,126 @@ + 1 0.00228663 + 2 0.00228588 + 3 0.00228015 + 4 0.00228989 + 5 0.00389486 + 6 0.00388647 + 7 0.00540288 + 8 0.0068705 + 9 0.00832372 + 10 0.00831953 + 11 0.00985117 + 12 0.0097981 + 13 0.0112743 + 14 0.0127921 + 15 0.0142566 + 16 0.0157841 + 17 0.0171722 + 18 0.0171722 + 19 0.0187576 + 20 0.020087 + 21 0.0216535 + 22 0.0231463 + 23 0.0246426 + 24 0.0261164 + 25 0.0276647 + 26 0.0289401 + 27 0.0320255 + 28 0.0335074 + 29 0.0364698 + 30 0.0378069 + 31 0.0394602 + 32 0.0407983 + 33 0.0438997 + 34 0.0454188 + 35 0.0506801 + 36 0.0534846 + 37 0.0564103 + 38 0.0577789 + 39 0.0609338 + 40 0.0622642 + 41 0.0653465 + 42 0.0668596 + 43 0.0697885 + 44 0.072573 + 45 0.0756137 + 46 0.0756385 + 47 0.0789474 + 48 0.0815966 + 49 0.0849577 + 50 0.0962901 + 51 0.0886416 + 52 0.0914127 + 53 0.100962 + 54 0.0976331 + 55 0.10026 + 56 0.10026 + 57 0.103125 + 58 0.108553 + 59 0.111111 + 60 0.1155 + 61 0.114583 + 62 0.11837 + 63 0.122417 + 64 0.125646 + 65 0.130656 + 66 0.130141 + 67 0.133141 + 68 0.138241 + 69 0.142329 + 70 0.147368 + 71 0.145833 + 72 0.14884 + 73 0.15359 + 74 0.158654 + 75 0.162276 + 76 0.162219 + 77 0.166008 + 78 0.173034 + 79 0.176134 + 80 0.181675 + 81 0.181675 + 82 0.186365 + 83 0.190045 + 84 0.195101 + 85 0.200695 + 86 0.199138 + 87 0.204878 + 88 0.209239 + 89 0.212219 + 90 0.218957 + 91 0.21875 + 92 0.225806 + 93 0.22928 + 94 0.232941 + 95 0.240625 + 96 0.242054 + 97 0.24479 + 98 0.249101 + 99 0.256287 +100 0.262699 +101 0.262599 +102 0.267361 +103 0.272406 +104 0.277867 +105 0.285185 +106 0.283552 +107 0.291054 +108 0.294643 +109 0.300912 +110 0.309513 +111 0.307453 +112 0.313859 +113 0.321131 +114 0.325658 +115 0.328125 +116 0.336081 +117 0.341379 +118 0.343921 +119 0.348154 +120 0.352403 +121 0.352268 +122 0.356619 +123 0.358974 +124 0.361361 +125 0.360938 +126 0.36122 diff --git a/hostside/santafe-2.speeds b/hostside/santafe-2.speeds new file mode 100644 index 0000000..10e0f4a --- /dev/null +++ b/hostside/santafe-2.speeds @@ -0,0 +1,126 @@ + 1 0.00229244 + 2 0.00225063 + 3 0.00224505 + 4 0.00226582 + 5 0.00382273 + 6 0.00381471 + 7 0.00528834 + 8 0.00683816 + 9 0.00826712 + 10 0.00824323 + 11 0.00978482 + 12 0.00980517 + 13 0.0112914 + 14 0.0127133 + 15 0.0142014 + 16 0.0156823 + 17 0.0170719 + 18 0.0170103 + 19 0.018635 + 20 0.0201149 + 21 0.0215888 + 22 0.0230723 + 23 0.0243132 + 24 0.0259288 + 25 0.0275065 + 26 0.0270428 + 27 0.0300312 + 28 0.0332757 + 29 0.0362922 + 30 0.0375122 + 31 0.0391393 + 32 0.040569 + 33 0.0436261 + 34 0.045126 + 35 0.0503158 + 36 0.053497 + 37 0.0562044 + 38 0.0575342 + 39 0.0604396 + 40 0.0622474 + 41 0.0647422 + 42 0.0665514 + 43 0.0694111 + 44 0.0725503 + 45 0.0756137 + 46 0.0756137 + 47 0.0784647 + 48 0.0815678 + 49 0.0844607 + 50 0.0886076 + 51 0.0886416 + 52 0.0914127 + 53 0.0944013 + 54 0.0975919 + 55 0.10026 + 56 0.09625 + 57 0.103171 + 58 0.107743 + 59 0.111058 + 60 0.114583 + 61 0.115993 + 62 0.118431 + 63 0.123397 + 64 0.126126 + 65 0.129557 + 66 0.130068 + 67 0.133758 + 68 0.137541 + 69 0.142285 + 70 0.145972 + 71 0.147321 + 72 0.15044 + 73 0.153693 + 74 0.158654 + 75 0.164121 + 76 0.164179 + 77 0.167939 + 78 0.171939 + 79 0.177283 + 80 0.180539 + 81 0.180469 + 82 0.186365 + 83 0.191225 + 84 0.195184 + 85 0.199138 + 86 0.199138 + 87 0.20506 + 88 0.209429 + 89 0.213889 + 90 0.218854 + 91 0.21875 + 92 0.222222 + 93 0.229167 + 94 0.23668 + 95 0.242138 + 96 0.242054 + 97 0.246269 + 98 0.252 + 99 0.256382 +100 0.261017 +101 0.260919 +102 0.265925 +103 0.272406 +104 0.277756 +105 0.286838 +106 0.286957 +107 0.28875 +108 0.296661 +109 0.302885 +110 0.311601 +111 0.311881 +112 0.316438 +113 0.320833 +114 0.328125 +115 0.33063 +116 0.333173 +117 0.33871 +118 0.34375 +119 0.343921 +120 0.352268 +121 0.350133 +122 0.356619 +123 0.35443 +124 0.356619 +125 0.356481 +126 0.360938 diff --git a/hostside/selectors.h.gen b/hostside/selectors.h.gen index ec5dbb7..5950668 100755 --- a/hostside/selectors.h.gen +++ b/hostside/selectors.h.gen @@ -1,9 +1,10 @@ #!/usr/bin/perl -$bit= 1; foreach $f (qw( picio picioh + picdebug )) { - printf "#define sel_%-10s 0x%08lxLU\n", $f, $bit; + printf "#define sel_%-10s 0x%08lxLU\n", $f, $bit + or die $!; $bit <<= 1; } diff --git a/hostside/selectout.c b/hostside/selectout.c new file mode 100644 index 0000000..4f470d0 --- /dev/null +++ b/hostside/selectout.c @@ -0,0 +1,33 @@ +/* + * mulitplexer daemon + * transmissions to clients of their selected messages + */ + +#include +#include + +#include "multiplex.h" +#include "auproto-pic.h" + +#define FOR_CLS(s) do{ \ + Client *cl, *next_cl; \ + for (cl= clients.head; \ + cl; \ + cl= next_cl) { \ + OutBufferChain *ch= &cl->ch; \ + next_cl= cl->next; \ + if (!(sel & cl->sel)) continue; \ + s; \ + } \ + }while(0) + +void sovprintf(Selector sel, const char *fmt, va_list al) { + FOR_CLS( ovprintf(ch, fmt, al) ); +} + +void soprintf(Selector sel, const char *fmt, ...) + { va_list al; va_start(al,fmt); sovprintf(sel,fmt,al); va_end(al); } + +void sowrite(Selector sel, const char *data, int l) { + FOR_CLS( owrite(ch, data, l) ); +} diff --git a/hostside/serialio.c b/hostside/serialio.c index 1075de9..f1b9a7c 100644 --- a/hostside/serialio.c +++ b/hostside/serialio.c @@ -1,4 +1,5 @@ /* + * common * general serial i/o and system interface etc. */ diff --git a/hostside/skelproto-pic.c b/hostside/skelproto-pic.c index 5e2339c..92c847d 100644 --- a/hostside/skelproto-pic.c +++ b/hostside/skelproto-pic.c @@ -7,29 +7,35 @@ * and related functions */ -#include "hostside.h" +#include + +#include "common.h" #include "auproto-pic.h" -extern void enco_pic_anyinsn(PicInsn *out, const PicInsnInfo *pii, - int objnum) { +extern void enco_pic_any(PicInsn *out, int opcode, int argbits, int objnum) { unsigned long as= objnum; int i; - out->l= 1 + pii->argbits/7; + assert(!(as & (~0UL << argbits))); + out->l= 1 + argbits/7; for (i= out->l - 1; i >= 0; i--, as >>= 7) out->d[i]= (as & 0x07fUL) | 0x080UL; out->d[out->l - 1] &= ~0x080UL; - out->d[0] |= pii->opcode; + out->d[0] |= opcode; } -#if 0 -const char *pi_getarg(const PicInsn *pi, const PicInsnInfo *pi, long *a_r) { - -const PicInsn *pi, long *arg_o, -#endif - +#define C , +#define ENCO(w, xa, opcode, argbits, objnum) \ + extern void enco_pic_##w(PicInsn *out xa) { \ + return enco_pic_any(out, opcode, argbits, objnum); \ + } + +ENCO(pii, C const PicInsnInfo *pii C int objn,pii->opcode,pii->argbits,objn) +ENCO(@cnameyn@, , @opcodeyn@, 0,0) @h2p@ @arglentf=0@ +ENCO(@cnameyn@, C int objnum, @opcodeyn@, @arglen@,objnum) @h2p@ @arglentf=1@ + const PicInsnInfo *lookup_byopcode(Byte byte0, const PicInsnInfo *table) { const PicInsnInfo *pi; for (pi= table; @@ -40,12 +46,33 @@ const PicInsnInfo *lookup_byopcode(Byte byte0, const PicInsnInfo *table) { return 0; } +void picinsn_decode(const PicInsn *pi, const PicInsnInfo *table, + const PicInsnInfo **pii_r, int *objnum_r) { + const PicInsnInfo *pii; + unsigned val= 0; + + pii= pi->l > 0 ? lookup_byopcode(pi->d[0], table) : 0; + + if (pii) { + val= pi->d[0]; + if (pii->argbits <= 6) { + if (pi->l != 1) pii= 0; + } else { + if (pi->l == 2 && !(pi->d[1] & 0x80u)) { val <<= 7; val |= pi->d[1]; } + else pii= 0; + } + } + + if (objnum_r && pii) *objnum_r= val & ((1u << pii->argbits) - 1); + *pii_r= pii; +} + const PicInsnInfo pic_command_infos[]= { - { "@cnameyn@", @opcodeyn@, @opcodemaskyn@, @arglen@ }, @h2p@ + { "@cnameyn@", @opcodeyn@, @opcodemaskyn@, @arglen@, @noiselevel@, 0 }, @h2p@ { 0 } }; const PicInsnInfo pic_reply_infos[]= { - { "@cnameyn@", @opcodeyn@, @opcodemaskyn@, @arglen@ }, @p2h@ + { "@cnameyn@", @opcodeyn@, @opcodemaskyn@, @arglen@, @noiselevel@, on_pic_@cnameyn@ },@p2h@ { 0 } }; diff --git a/hostside/skelproto-pic.h b/hostside/skelproto-pic.h index e42b48c..ee63e64 100644 --- a/hostside/skelproto-pic.h +++ b/hostside/skelproto-pic.h @@ -11,25 +11,31 @@ #define AUPROTO_PIC_H typedef struct PicInsnInfo PicInsnInfo; +typedef void PicInputFn(const PicInsnInfo *pii, const PicInsn *pi, int objnum); -void enco_pic_@cname@(PicInsn *out); @h2p@ @arglentf=0@ -void enco_pic_@cname@(PicInsn *out, int objum); @h2p@ @arglentf=1@ - -void on_pic_@cnameyn@(void); @p2h@ @arglentf=0@ -void on_pic_@cnameyn@(int objnum); @p2h@ @arglentf=1@ +void enco_pic_@cnameyn@(PicInsn *out); @h2p@ @arglentf=0@ +void enco_pic_@cnameyn@(PicInsn *out, int objum); @h2p@ @arglentf=1@ +PicInputFn on_pic_@cnameyn@; @p2h@ +#define PICMSG_@cnameynu@ @opcodeyn@ +#define PICMSG_@cnameynu@_M @opcodemaskyn@ +#define PICMSG_@cnameynu@_P(v) (((v) & @opcodemaskyn@) == @opcodeyn@) extern void enco_pic_polarity_begin(PicInsn *out); extern void enco_pic_polarity_setbit(PicInsn *out, int objnum); -extern void on_pic_debug(int ch); -extern void enco_pic_anyinsn(PicInsn *out, const PicInsnInfo *pii, int objnum); +extern void enco_pic_pii(PicInsn *out, const PicInsnInfo *pii, int objnum); +extern void enco_pic_any(PicInsn *out, int opcode, int argbits, int objnum); const PicInsnInfo *lookup_byopcode(Byte byte0, const PicInsnInfo *table); +void picinsn_decode(const PicInsn *pi, const PicInsnInfo *table, + const PicInsnInfo **pii_r, int *objnum_r); +void oopicio(const char *dirn, const PicInsnInfo *pii, int objnum); struct PicInsnInfo { const char *name; Byte opcode, mask; - int argbits; + int argbits, noiselevel; + PicInputFn *input_fn; }; extern const PicInsnInfo pic_command_infos[], pic_reply_infos[]; diff --git a/hostside/speed.c b/hostside/speed.c new file mode 100644 index 0000000..8f5a88d --- /dev/null +++ b/hostside/speed.c @@ -0,0 +1,163 @@ +/* + * realtime + * speed manager + */ + +#include "realtime.h" +#include "nmra.h" + +static void changereq_internal(Train *tra, int newcommanded, int inautostop); +static void accel_more(TimeoutEvent *toev); + +void speedmanager_reset_train(Train *tra) { + Nmra n; + ErrorCode ec; + + tra->estopping= 0; + tra->speed= 0; + tra->accel.target= tra->accel.commanded= 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(tra, 0); + if (ec) + safety_panic(tra, 0, "position reset impossible!"); +} + +static void xmit_speed(Train *tra) { + Nmra n; + enco_nmra_speed126(&n, tra->addr, + tra->accel.curve[tra->accel.commanded].step, + tra->backwards); + retransmit_urgent_requeue(&tra->accel.rn, &n); +} + +void speedmanager_autostop(Train *tra) { + if (tra->speed < AUTOSTOP_MAXSPEED) { + toev_stop(&tra->accel.more); + tra->accel.commanded= tra->accel.target= tra->speed= 0; + xmit_speed(tra); + } else { + changereq_internal(tra, 0, 1); + } +} + +static void estop_done(TimeoutEvent *toev) { + Train *tra= (void*)((char*)toev - offsetof(Train, accel.more)); + retransmit_urgent_cancel(&tra->accel.rn); + speedmanager_reset_train(tra); +} + +void speedmanager_emergencystop(Train *tra) { + Nmra n; + tra->estopping= 1; + toev_stop(&tra->accel.more); + enco_nmra_estop1(&n, tra->addr); + retransmit_urgent_requeue(&tra->accel.rn, &n); + + toev_stop(&tra->accel.more); + tra->accel.more.callback= estop_done; + tra->accel.more.duration= ESTOP_DEADTIME; + toev_start(&tra->accel.more); +} + +static void adjust_next(Train *tra, int inautostop) { + long newspeed; + + if (tra->accel.target > tra->accel.commanded) { + tra->accel.commanded++; + 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->accel.curve[tra->accel.commanded].speed; + tra->accel.commanded--; + tra->accel.more.duration= tra->accel.curve[tra->accel.commanded].downwait; + } else { + return; + } + + if (!inautostop) { + ErrorCode ec= safety_requestspeed(tra, newspeed); + if (ec) { + assert(newspeed > tra->speed); + assert(tra->accel.target >= tra->accel.commanded); + tra->accel.target= --tra->accel.commanded; + toev_stop(&tra->accel.more); + return; + } + } else { + tra->speed= newspeed; + } + + toev_stop(&tra->accel.more); + tra->accel.more.callback= accel_more; + toev_start(&tra->accel.more); + xmit_speed(tra); +} + +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 (!tra->accel.more.running) { + assert(tra->accel.commanded == tra->accel.target); + adjust_next(tra, 0); + } else { + if (tra->accel.target > tra->accel.commanded) { + newold= tra->accel.commanded+1; + reverse= newcommanded < newold; + } else if (tra->accel.target < tra->accel.commanded) { + newold= tra->accel.commanded-1; + reverse= newcommanded > newold; + } else { + abort(); + } + tra->accel.target= newcommanded; + + if (reverse) { + /* switch from accel to decel or vice versa */ + toev_stop(&tra->accel.more); + tra->accel.commanded= newold; + adjust_next(tra, 0); + } + } +} + +void speedmanager_speedchange_request(Train *tra, long speed) { + changereq_internal(tra, speed, 0); + 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=tra->accel.curvesz; + a < b; + ) { + try= (a + b) >> 2; + if (curve[try].speed > speed) b= try; else a= try+1; + } + /* Loop postconditions: + * a==b + * either a=try+1 never executed ie all curve[].speed > speed + * hence a=0 + * or curve[a-1].speed <= speed + * either b=try never executed ie all curve[].speed <= speed + * hence b=trai->nsteps + * or curve[b].speed > speed + */ + found= a>1 ? a-1 : speed ? 1 : 0; + + 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); +} diff --git a/hostside/startup.c b/hostside/startup.c new file mode 100644 index 0000000..955a443 --- /dev/null +++ b/hostside/startup.c @@ -0,0 +1,218 @@ +/* + * realtime + * startup state machine + */ + +#include "realtime.h" + +const char *const stastatelist[]= DEFINE_STASTATE_DATA; +StartupState sta_state; + +static TimeoutEvent sta_toev; +static TimeoutEvent ping_toev; + +static int ping_seq; +static PicInsn piob; +static void sta_goto(StartupState new_state); + +static void timedout_onward(TimeoutEvent *toev) { + assert(sta_state != Sta_Run); + if (sta_state == Sta_Settling) { + enco_pic_off(&piob); + serial_transmit(&piob); + } + sta_goto(sta_state == Sta_Flush ? Sta_Ping : sta_state + 1); +} + +static void timedout_ping(TimeoutEvent *toev) { + assert(sta_state >= Sta_Ping); + sta_goto(Sta_Off); +} + +static void timefor_ping(TimeoutEvent *toev) { + enco_pic_ping(&piob, ping_seq); + serial_transmit(&piob); + ping_toev.callback= timedout_ping; + toev_start(&ping_toev); +} + +static void initial_ping(void) { + struct timeval now; + + mgettimeofday(&now); + ping_seq= (now.tv_sec & 0x1fU) << 5; /* bottom 5bi of secs: period 32s */ + ping_seq |= (now.tv_usec >> 15); /* top 5bi of 20bi us: res.~2^15us */ + ping_toev.duration= 300; + timefor_ping(0); +} + +void sta_startup(void) { sta_goto(Sta_Flush); } + +static void sta_goto(StartupState new_state) { + toev_stop(&sta_toev); + sta_toev.callback= timedout_onward; + sta_toev.duration= -1; + + if (new_state < Sta_Ping) { + toev_stop(&ping_toev); + } else if (new_state == Sta_Ping) { + initial_ping(); + } else { + assert(sta_state >= Sta_Ping); + } + + switch (new_state) { + case Sta_Flush: sta_toev.duration= 300; break; + case Sta_Off: break; + case Sta_Ping: break; + case Sta_Fault: break; + case Sta_Settling: sta_toev.duration= 750; break; + case Sta_Resolving: sta_toev.duration= 500; break; + case Sta_Run: break; + } + + piob.l= 0; + switch (new_state) { + case Sta_Flush: break; + case Sta_Off: if (sta_state > Sta_Ping) enco_pic_off(&piob); break; + case Sta_Ping: break; + case Sta_Fault: break; + case Sta_Settling: enco_pic_off(&piob); break; + case Sta_Resolving: enco_pic_on(&piob); break; + case Sta_Run: retransmit_start(); break; + } + if (piob.l) serial_transmit(&piob); + + toev_start(&sta_toev); + sta_state= new_state; + + /* notify various people: */ + oprintf(UPO, "stastate %s\n", stastatelist[sta_state]); + /* ... add others here. */ +} + +void serial_moredata(PicInsn *buf) { + const PicInsnInfo *pii; + int objnum, suppress; + Byte *ep; + + /* Called when more data is received from PICs. + * On entry, buf->l is amount of data available. + * Does one of the following: + * - determines that there is no complete message; sets buf->l = 0 + * - finds and handles one PicInsn message, sets buf->l = message length + * - handles some series of bytes structured some other way, + * sets buf->l to the numer of bytes handled. + */ + assert(buf->l > 0); + + if (sta_state == Sta_Flush) { + toev_start(&sta_toev); + ouhex("picioh in junk", buf->d, buf->l); + return; /* junk absolutely everything */ + } + if (PICMSG_AAARGH_P(buf->d[0])) { + ouhex("picioh in aaargh", buf->d, buf->l); + die("PIC sent us AAARGH!"); + } + if (PICMSG_HELLO_P(buf->d[0])) { + ouhex("picioh in hello", buf->d, 1); + abandon_run(); + sta_goto(Sta_Flush); + buf->l= 1; + return; + } + if (sta_state == Sta_Off) { + ouhex("picioh in off", buf->d, 1); + buf->l= 1; + return; + } + + assert(sta_state >= Sta_Ping); + /* So, we are expecting properly formed messages. */ + + for (ep= buf->d; ep < buf->d + buf->l; ep++) + if (!(*ep & 0x80u)) + goto found_end; + + if (buf->l == sizeof(buf->d)) { + ouhex("picioh in toolong", buf->d, buf->l); + die("PIC sent packet too long"); + } + buf->l= 0; /* message not yet finished, so consume nothing */ + return; + + found_end: + /* Aha! */ + buf->l= ep - buf->d + 1; + picinsn_decode(buf, pic_reply_infos, &pii, &objnum); + suppress= pii && pii->noiselevel > picio_send_noise; + if (!suppress && picio_send_noise >= 2) + ouhex("picioh in msg", buf->d, buf->l); + if (!pii) { oprintf(UPO, "picio in unknown\n"); return; } + if (!suppress) + oupicio("in",pii,objnum); + pii->input_fn(pii,buf,objnum); +} + +void on_pic_pong(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { + if (objnum != ping_seq) + die("PIC sent wrong ping response (0x%x, wanted 0x%x)", objnum, ping_seq); + + ping_toev.duration= 1000; + ping_toev.callback= timefor_ping; + toev_start(&ping_toev); + + if (sta_state == Sta_Ping) + sta_goto(Sta_Settling); +} + +void on_pic_fixed(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { + if (sta_state >= Sta_Resolving) die("PIC sent unexpected FIXED"); + if (sta_state == Sta_Fault) sta_goto(Sta_Resolving); +} + +void on_pic_fault(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { + if (sta_state <= Sta_Ping) return; + if (sta_state == Sta_Fault) die("PIC sent two FAULTs"); + sta_goto(Sta_Fault); +} + +void on_pic_wtimeout(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { + if (sta_state <= Sta_Settling) return; + if (sta_state == Sta_Resolving) die("PIC sent WTIMEOUT in Resolving"); + oprintf(UPO, "warning watchdog \"PIC watchdog timer triggered\"\n"); +} + +void on_pic_hello(const PicInsnInfo *pii, const PicInsn *pi, int objnum) + { abort(); } +void on_pic_aaargh(const PicInsnInfo *pii, const PicInsn *pi, int objnum) + { abort(); } + +void on_pic_detect1(const PicInsnInfo *pii, const PicInsn *pi, int segn) { + if (segn >= NUM_SEGMENTS) die("PIC sent detect @#%d out of range",segn); + if (sta_state < Sta_Run) { + oprintf(UPO, "warning fixme ignored non-Run detection @%s\n", + info_segments[segn].pname); + return; + } + safety_notify_detection(&segments[segn]); +} + +/*---------- fixme move these to where they want to go ----------*/ + +void on_pic_nmradone(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { + if (!objnum) die("PIC sent NUL!"); + if (sta_state <= Sta_Settling) return; + if (sta_state != Sta_Run) die("PIC sent NMRADONE in Resolving"); + + while (objnum--) + retransmit_something(); +} + +void on_pic_detect0(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { +} + +void abandon_run(void) { + /* fixme do something here */ +} diff --git a/hostside/stastate.h.gen b/hostside/stastate.h.gen new file mode 100755 index 0000000..faa07fb --- /dev/null +++ b/hostside/stastate.h.gen @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e +Sta () { l="$l $1"; } + + # sta_toev ping_toev + Sta Flush # R 300 I ? + Sta Off # I ? I ? + Sta Ping # I ? I ? + Sta Fault # I ? R set + Sta Settling # I ? R set + Sta Resolving # I ? R set + Sta Run # I ? R set + +echo 'typedef enum {' +for s in $l; do echo " Sta_$s,"; done +echo '} StartupState; +#define DEFINE_STASTATE_DATA \' +printf ' { ' +for s in $l; do printf '"%s",' $s; done +echo '0 }' diff --git a/hostside/trackloc.c b/hostside/trackloc.c index 4a17714..0818c6b 100644 --- a/hostside/trackloc.c +++ b/hostside/trackloc.c @@ -3,15 +3,13 @@ #include -#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, @@ -44,7 +42,7 @@ void trackloc_further(TrackLocation *tloc, long *remain_io) { 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; } diff --git a/hostside/utils.c b/hostside/utils.c index b6c4494..5e08244 100644 --- a/hostside/utils.c +++ b/hostside/utils.c @@ -1,4 +1,7 @@ -/**/ +/* + * common + * general utility functions + */ #include #include @@ -6,15 +9,35 @@ #include #include -#include "hostside.h" +#include "common.h" + +static void vdie_vprintf(const char *fmt, va_list al) { + va_list al_copy; + va_copy(al_copy,al); + vfprintf(stderr,fmt,al_copy); + die_vprintf_hook(fmt,al); +} + +static void vdie_printf(const char *fmt, ...) { + va_list al; + va_start(al,fmt); + vdie_vprintf(fmt,al); + va_end(al); +} void vdie(const char *fmt, int ev, va_list al) { - vfprintf(stderr,fmt,al); - if (ev) fprintf(stderr,": %s",strerror(ev)); - fputc('\n',stderr); + vdie_printf("%s: fatal: ", progname); + vdie_vprintf(fmt,al); + if (ev) vdie_printf(": %s",strerror(ev)); + vdie_printf("\n"); + die_hook(); 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, ...) @@ -30,6 +53,14 @@ void *mmalloc(size_t sz) { return p; } +void *mrealloc(void *o, size_t sz) { + void *r; + if (!sz) { free(o); return 0; } + r= realloc(o,sz); + if (!r) diem(); + return r; +} + char *mstrdupl(const char *s, int l) { char *p; p= mmalloc(l+1); @@ -39,3 +70,9 @@ char *mstrdupl(const char *s, int l) { } 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"); +} diff --git a/layout/Makefile b/layout/Makefile index 0066e8d..47ae957 100644 --- a/layout/Makefile +++ b/layout/Makefile @@ -40,9 +40,13 @@ extras: ours-a.ps ours-al.ps dualjn-a.ps parts.ps include ours.dgram.m include segencolayers.m -NETPBM = -lnetpbm +#NETPBM = -lnetpbm ifeq (,$(shell test -f /usr/lib/libppm.so || echo no)) NETPBM += -lppm +else +ifeq (,$(shell test -f /usr/lib/libnetpbm.so || echo no)) +NETPBM += -lnetpbm +endif endif REDACT= consistency movfeatsplitedges \ diff --git a/layout/data2safety b/layout/data2safety index ce477e9..0bd5708 100755 --- a/layout/data2safety +++ b/layout/data2safety @@ -30,16 +30,15 @@ our (%nodes); our ($maxptixln2) = 5; -our ($nextboardnum,@boardtype,%numboards,$nreverses,@sensesin,@sensesbase); -our (@objkinds,%pin_used); -# @boardtype[$boardnum] -# $numboards{$type} -# $nreverses +our ($nextboardnum,@boardtype,@sensesin,$maxreverseobjnum); +our (@reversersboardnum,@sensesbase,@objkinds,%pin_used); +# $boardtype[$boardnum] # $sensesin[$page] +# $maxreverseobjnum +# $reversersboardnum[$boardnum] # undef => none; -1 => not yet determined # $sensesbase[$boardnum]= ($page << 7) | $baselsbyte -# %pin_used{$objkind}[$objnum] = [ $boardnum, $pin_info, $objonboard ] +# $pin_used{$objkind}[$objnum] = [ $boardnum, $pin_info, $objonboard ] $nextboardnum= 0; -$nreverses= 0; $sensesin[0]= 0; @objkinds= qw(pt sense reverse); @@ -104,13 +103,15 @@ sub line_segment () { m/^\s+(\w+)\s+(\d+\.\d+)$/ or return syntaxerror(); ($seg,$boob)=($1,$2); mistake("duplicate topology for $seg") if exists $segs{$seg}; + $boob= pa_boob($boob); $segs{$seg}= { - BoOb => pa_boob($boob), + BoOb => $boob, Inv => $invertible, Posns => 1, Feats => { }, FeatCount => 0 }; + &{"line_segment_".($invertible?'invertible':'vanilla')}($boob); } sub begin_endwiring () { @@ -127,7 +128,6 @@ sub line_boards () { $nextboardnum++; $boardtype[$num]= $type; - $numboards{$type}++; require "./$type.pin-info"; my ($sense_count, $page); @@ -146,15 +146,13 @@ sub line_boards () { &{"line_boards_$type"}($num); } -sub line_boards_reversers { - my ($num) = @_; - my ($i,$objnum); - for ($i=0; $i<6; $i++) { - $objnum= boob2objnum($num,$i,'reverse',0); - $nreverses= $objnum+1 if $objnum >= $nreverses; - } -} +sub line_boards_reversers { } sub line_boards_detectors { } +sub line_segment_vanilla ($) { } +sub line_segment_invertible ($) { + my ($boob) = @_; + $reversersboardnum[$boob->[0]]= -1; +} sub mistake ($) { my ($m) = @_; @@ -247,45 +245,56 @@ sub boob2objnum_pt { } sub boob2objnum_reverse { - my ($boardnum,$obj,$boardtype)=@_; - + my ($orgboardnum,$obj,$boardtype)=@_; # Converts board and object number (in canonical pic number plus - # and reverse0...reverse5 as seen on pinout diagrams), to - # object number for POLARITY command numbered as shown in + # and reverse0...reverse5 as seen on pinout diagrams), to the + # segment number for POLARITY command numbered as shown in # README.protocol. # # There are three basic stages: # # * We invert the on-board mapping; ie, we untangle the # tangling between the message from master to slave pic - # and the actual pins (see reverse.asm, polarity_do_here) + # and the actual pins (see reverse.asm, polarity_local_do) # # * We figure out which bit of which message byte the - # object corresponds to. (see reverse.asm, polarity_decode_message) + # object corresponds to. (see reverse.asm, command_polarity) # - # * We compute the README.protocol bit and byte number. + # * We compute the README.protocol segment number. - my ($cycle,$boardincycle,$cyclebasebyte,$byte,$bit); + my ($cycle,$boardincycle,$cyclebasebyte,$byte,$bit,$boardnum,$rv); + $boardnum= $reversersboardnum[$orgboardnum]; + die "$orgboardnum $boardnum" unless defined $boardnum; + die "$orgboardnum $boardnum" unless $boardnum >= 0; die unless $boardtype eq 'reversers'; die $obj if $obj > 5; +#print STDERR "data2safety $boardnum.$obj "; $obj = sprintf '%d', $obj; $obj =~ y/302154/543210/; # mapping due to polarity_do_here +#print STDERR " obj=$obj"; $cycle= int(($boardnum+3) / 7); +#print STDERR " cycle=$cycle"; $boardincycle= ($boardnum+3) - $cycle*7; +#print STDERR " boardin=$boardincycle"; $cyclebasebyte= $cycle*6 - 2; +#print STDERR " baseby=$cyclebasebyte"; if ($boardnum==2 && $obj > 2) { $byte= 0; $bit= $obj-3; - return 3 - $bit; # only these three in byte 0, a special case + $rv= 3 - $bit; # only these three in byte 0, a special case; +#print STDERR " special bit=$bit => $rv\n"; + return $rv; } elsif ($boardincycle<5) { - $byte= $cyclebasebyte + $boardincycle; $bit= $obj; + $byte= $cyclebasebyte + $boardincycle; $bit= $obj + 1; } elsif ($boardincycle==6) { - $byte= $cyclebasebyte + 5; $bit= $obj; + $byte= $cyclebasebyte + 5; $bit= $obj + 1; } elsif ($boardincycle==5) { - $byte= $cyclebasebyte + 5 - $bit; $bit= 6; + $byte= $cyclebasebyte + 5 - $bit; $bit= 0; } else { die; } - return $byte*7 + 3 - $bit; + $rv= $byte*7 + 3 - $bit; +#print STDERR " ordinary byte=$byte bit=$bit => $rv\n"; + return $rv; } sub boob2objnum_sense { @@ -362,6 +371,21 @@ sub mainread () { } } +sub redaction () { + my ($num,$mappednum,$i,$objnum); + $maxreverseobjnum= 0; + for ($num=0, $mappednum=0; $num<@boardtype; $num++) { + next unless defined $reversersboardnum[$num]; + die if $reversersboardnum[$num] != -1; + $reversersboardnum[$num]= $mappednum; + for ($i=0; $i<6; $i++) { + $objnum= boob2objnum($mappednum,$i,'reverse',0); + $maxreverseobjnum= $objnum+1 if $objnum >= $maxreverseobjnum; + } + $mappednum++; + } +} + sub nummap ($) { my ($p) = @_; $p =~ s/\d{1,6}/ sprintf "%06d%d",$&,$& /ge; @@ -379,7 +403,6 @@ sub writeout () { push @segs, $seg; } o(sprintf - "#define NUM_TRAINS 1000000\n". "#define NUM_SEGMENTS %s\n\n". "#include \"layout-data.h\"\n\n", scalar @segs); @@ -398,7 +421,7 @@ sub writeout () { } o("$delim\n"); o(sprintf " { %-8s %4d", - '"'.$seg.(length $pi ? '/' : '').$pi.'",', + '"'.$pi.'",', $segr->{Dist}[$comb]); for ($end=0; $end<2; $end++) { o(", { "); @@ -446,7 +469,7 @@ sub writeout () { $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=','; } @@ -467,6 +490,7 @@ sub writeout () { $delim= ','; } o("\n};\n"); + o("const BoardObject info_maxreverse= $maxreverseobjnum;\n"); } # writeasm_KIND() @@ -616,5 +640,6 @@ sub writeasm () { o("\n end\n"); } mainread(); +redaction(); writeout(); writeasm(); diff --git a/layout/layout b/layout/layout index cf5f9e4..d47a57c 100755 --- a/layout/layout +++ b/layout/layout @@ -656,7 +656,7 @@ sub segment_used__print ($) { sub segment_used__len ($$) { my ($used,$pt) = @_; - $segused_incurrent++; + $segused_incurrent += $used; return if @segments < 3; $segments[1] -= $used; diff --git a/layout/layout-data.h b/layout/layout-data.h index 84391c7..4bb569a 100644 --- a/layout/layout-data.h +++ b/layout/layout-data.h @@ -14,16 +14,13 @@ /*========== 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, @@ -61,26 +58,18 @@ typedef struct { 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 * .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 SegmentInfo info_segments[]; -extern const BoardObject info_nreverses; /* max. reverse + 1 */ +extern const BoardObject info_maxreverse; /* max. reverse + 1 */ -#define NOTA(x) (~(x##Num)0) +#define NOTA(x) (-1) +#define SOMEP(x) ((x) >= 0) #endif /*LAYOUT_DATA_H*/ diff --git a/pic.make b/pic.make index d445442..dbd6cc8 100644 --- a/pic.make +++ b/pic.make @@ -1,6 +1,6 @@ # recommended programming order # one test on PIC#0 -# first time: erase, write FOO-entire0.hex +# first time: erase, write FOO+entire0.hex # subsequently if only FOO.asm changed: update FOO.hex # for all pics # first time: for each individually erase, write perpicNUM.hex @@ -8,10 +8,16 @@ # subsequently: for all update FOO.hex # other possibilities are not ruled out # +# For actual program from detpic, we program (with odyssey-train): +# odyssey-train write program+code.hex +# and then later +# write/update program+program/code.hex +# etc. +# # filename conventions - contents of hex files # FOO.hex FOO.o (only) # FOO-withcfg.hex FOO.o config.o -# FOO-entire0.hex FOO.o idlocs0.o config.o +# FOO+entire0.hex FOO.o idlocs0.o config.o # perpicNUMBER.hex idlocsNUMBER.o config.o #ASFLAGS= -Dmclock=20000 -Dsclock=20000 -- 2.30.2