chiark / gitweb /
merge changes from trunk into our branch; cvs up -j branchpoint-hostside-wip-2006...
authorian <ian>
Fri, 25 Jan 2008 22:19:00 +0000 (22:19 +0000)
committerian <ian>
Fri, 25 Jan 2008 22:19:00 +0000 (22:19 +0000)
66 files changed:
TODO
cebpic/README.protocol
cebpic/morse-generator
cprogs.make
detpic/Makefile
detpic/common.inc
detpic/morse.messages
detpic/nmra-stream.asm
detpic/panic.asm
detpic/program.asm
detpic/program.clocks
detpic/reverse.asm
hostside/.cvsignore
hostside/Makefile
hostside/README
hostside/Structure [new file with mode: 0644]
hostside/TODO
hostside/actual.c [new file with mode: 0644]
hostside/cdumgr.c [new file with mode: 0644]
hostside/client.c
hostside/cmdinput.c [new file with mode: 0644]
hostside/commands.c
hostside/common.h
hostside/daemons.h [new file with mode: 0644]
hostside/dliste.h [new file with mode: 0644]
hostside/dummy-trains.c [new file with mode: 0644]
hostside/errorcodes.h.gen [new file with mode: 0755]
hostside/eventhelp.c [new file with mode: 0644]
hostside/gui-plan.c
hostside/hostside.c
hostside/hostside.h
hostside/main.c
hostside/measure-speeds [new file with mode: 0755]
hostside/multiplex.c [new file with mode: 0644]
hostside/multiplex.h [new file with mode: 0644]
hostside/obc.c
hostside/output.c
hostside/parse-proto-spec
hostside/parseutils.c
hostside/realtime.c [new file with mode: 0644]
hostside/realtime.h [new file with mode: 0644]
hostside/record-l.l [new file with mode: 0644]
hostside/record-y.y [new file with mode: 0644]
hostside/record.c [new file with mode: 0644]
hostside/record.h [new file with mode: 0644]
hostside/retransmit-table.h.gen [new file with mode: 0755]
hostside/retransmit.c
hostside/safety.c
hostside/safety.h
hostside/santafe-1.speeds [new file with mode: 0644]
hostside/santafe-2.speeds [new file with mode: 0644]
hostside/selectors.h.gen
hostside/selectout.c [new file with mode: 0644]
hostside/serialio.c
hostside/skelproto-pic.c
hostside/skelproto-pic.h
hostside/speed.c [new file with mode: 0644]
hostside/startup.c [new file with mode: 0644]
hostside/stastate.h.gen [new file with mode: 0755]
hostside/trackloc.c
hostside/utils.c
layout/Makefile
layout/data2safety
layout/layout
layout/layout-data.h
pic.make

diff --git a/TODO b/TODO
index 902e32b9f25be27c99f1898a74eff5599a1e3555..f3d6d468ba19b57526667f2f56e4cef04763e441 100644 (file)
--- 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:
index df31395c25d08045bf0ff54cc05282694f8f2360..14938cf8e25bc6ba374911ef907b798a2f666eb4 100644 (file)
@@ -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
index 622ba7a82dfbe3cb8e8804710464c4d2f97a2171..590f5e4178f91a18970535c4792197803a4fd7ae 100755 (executable)
@@ -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;
index e6fd70cd360aad6f777278cd628809ad5b1ec8cd..00bf7e5040c673850466d17579f85afa8adbad8b 100644 (file)
@@ -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 $@ $<
index 3b04fa2e99d038c6f389f51ce75be93cf887d4d4..b947e9eb97303753b2077536ed85c5ae1a736da7 100644 (file)
@@ -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)
index 8f276a9bd245e4c27c0aa3616428b0a89c417747..c3537b231564c3a303d62dee0481904126a2cc6a 100644 (file)
@@ -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
 ;
index e317f0c79d1b44c4bcdd3d0434e813ca59d2df00..8194ad33220c866f0a8636a77757218b14c2c8e5 100644 (file)
@@ -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
index 1331f233dfc42d82a688bef7d65e86cb45e29d71..7693f2b0a770a7d3412b5bdb2b137484ed83c88b 100644 (file)
@@ -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
index 663d7fe443fe2b33109e52aba34b013fe1d5e871..d083b2042d5a0e2d8f733bd216110de7f8ba4bde 100644 (file)
@@ -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 urgentwe might get interrupted while
 ;  panicing which would be bad because we might forget to panic).
 
         mov_wf   panic_address
index 2a87efe0ef751959c3d61307f7b9d78ea12ec0f4..7baba7365ecb7dfe008503b7bdc93658e1103b34 100644 (file)
@@ -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<<TMR0ON) | startwait_master_t0scale
+       mov_wf  T0CON
+startwait_loop
+       bt_f_if0 INTCON,TMR0IF
+       bra     startwait_loop
+       ;...done.
+       return
+
 ;----------------------------------------------------------------------
 serialrx_table_section code    0x2000
 
index 8584d2e1e5f1d420991c2ad731ef9507b1eaa251..fc70a0c3a5ca3f4283679e871177ce0a6dc5f42e 100644 (file)
@@ -1,12 +1,17 @@
 # Each line is:
-#  <name>      M|S|MS          how     interval
+#  <name>      M|S|MS[T]       how     interval
 # where how is one of
-#  T1ov T3ov
+#  T0ov8 T0ov16 T1ov T3ov
 #      set  T[13]CON to  <stuff> | <name>_{master,slave}_t[13]scale
 #      load TMR[13][HL] with  <name>_{master,slave}_t[13]init[hl]
 #       or to put it another way, TMR[13] with
 #              65535 - <name>_{master,slave}_t[13]cycles
 #      then time to overflow will be specified time
+#  T2period
+#      set T2CON to  <stuff> | <name>_{master,slave}_t2scale
+#      set PR2 to  <name>_{master,slave}_t2cycles
+# M and S mean generate <name>_{master,slave}_<things> respectively
+# T means generate <name>_{us,ms}
 
 points         MS      T3ov            10ms
 tick           MST     T2period        1ms
index 725edfc4908ab077e5cc033fc3db3ed5a40638dc..ea1e3ab5b68b4330465aa9faf384777245dd450b 100644 (file)
@@ -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 <board><segment> where <segment>
 ;              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
index e285fdd2f71cee71005a66238d66069b2f505ff3..1962df45c328568298fe2bcb459c00b6120cff7d 100644 (file)
@@ -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]
index a56c4609a7c600437694c6a1bd48ee085fe6e678..589bffa9d133ed8122484f5f2611feab5bfbbae6 100644 (file)
@@ -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
index bf67f482b42c0a184e57e20ea13df4aa3353a9f3..39d169536a2777d9acd4cde4d37e73fa70a55117 100644 (file)
@@ -32,14 +32,15 @@ To test:
 
 Protocol over new hostside stdin:
                                             Example (always the same msg):
- P> nmra [*<slot>] [<nmra-command [<nmra-args>...]]    nmra speed28 3 13 1
- P> nmra [*<slot>] [=<nmra-bytes>]                     nmra =0348
- P> nmra [*<slot>] [:<nmra-bytes-with-csum>]           nmra :03484b
- P> nmra [*<slot>] [_<pic-literal-bytes>]              nmra _7f7f00644197
-   in each case *<slot> indicates that the message should be put in
-   the retransmission cycle, and cancels any previous message with the
-   same *<slot>.  <slot> may be empty.
- P> nmra *<slot>
+ P> nmra [<slot>] [<nmra-command [<nmra-args>...]]    nmra speed28 3 13 1
+ P> nmra [<slot>] [=<nmra-bytes>]                     nmra =0348
+ P> nmra [<slot>] [:<nmra-bytes-with-csum>]           nmra :03484b
+ P> nmra [<slot>] [_<pic-literal-bytes>]              nmra _7f7f00644197
+   in each case <slot> (if present) is *<slotname> or %<slotname> and
+   indicates that the message should be put in the retransmission
+   cycle, and cancels any previous message with the same <slotname>.
+   <slotname> may be empty.  * indicates urgent
+ P> nmra <slot>
    cancels the relevant retransmission.
 
                                             Example (always the same msg):
diff --git a/hostside/Structure b/hostside/Structure
new file mode 100644 (file)
index 0000000..538e1ad
--- /dev/null
@@ -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
index c08e6f4a8c07e4026f12eee7adef4884508b65e8..1c8443e6bb59351a094f627e7bc11a5872ce3fff 100644 (file)
@@ -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 (file)
index 0000000..21eef04
--- /dev/null
@@ -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 (file)
index 0000000..db6f8ba
--- /dev/null
@@ -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 */
+}
index 989f782af553abcfe9eb607b6b20083780e3b50e..8b3a24ade01a448fa2006ad281a67c92c4b90331 100644 (file)
 #include <stdlib.h>
 #include <errno.h>
 
-#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 (file)
index 0000000..51cec79
--- /dev/null
@@ -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");  
+}
index b14ed033052579e1c4e53162f871744372c3bccc..9dd543409b2d11bc6e4df818ccbfcea1768ae21e 100644 (file)
@@ -6,7 +6,7 @@
 #include <assert.h>
 #include <string.h>
 
-#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[]= {
index a1b163dc8e9295e387d197fc2d50f9ee749c9ea9..b44acbd2966764003d3e1e2f058d109ab2720139 100644 (file)
@@ -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 <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+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 (file)
index 0000000..0db7963
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * declarations common to
+ *  - realtime daemon
+ *  - multiplexer daemon
+ */
+
+#ifndef DAEMONS_H
+#define DAEMONS_H
+
+#include <oop.h>
+#include <oop-read.h>
+
+#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 (file)
index 0000000..bade3d4
--- /dev/null
@@ -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 (file)
index 0000000..f03295a
--- /dev/null
@@ -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 (executable)
index 0000000..7608367
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+
+@f= qw(
+       OK
+       Invalid
+       Safety
+       );
+
+
+    
+$decl= "const char *const errorcodelist[]";
+
+print <<END
+typedef enum {
+END
+    ."  ".(join ",\n  ", map {
+       $f=$_; $f =~ s/\-//g; "EC_$f";
+    } @f).<<END
+
+} ErrorCode;
+
+extern $decl;
+#define DEFINE_ERRORCODELIST_DATA \\
+$decl= { \\
+END
+    ."  ".(join ", \\\n  ", map {
+       '"'.(lc).'"';
+    } @f).<<END
+ \\
+};
+END
+    or die $!;
diff --git a/hostside/eventhelp.c b/hostside/eventhelp.c
new file mode 100644 (file)
index 0000000..4471240
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * daemons
+ * helpers for event handling
+ */
+
+#include <assert.h>
+#include <limits.h>
+
+#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);
+}
index 1df8766ae440a660557820dafe3a770010f353df..9e1f5922ba34ab74cb76076efef3bb9127f17695 100644 (file)
@@ -15,7 +15,7 @@
 #include <X11/Xlib.h>
 #include <X11/xpm.h>
 
-#include "hostside.h"
+#include "common.h"
 #include "../layout/plan-data-format.h"
 
 #include <oop.h>
@@ -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,
index 6b92d8669de80341d14d775427969727e7c34db0..813cab370e026da66e664b2d5c2a4b9e8fffb655 100644 (file)
@@ -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();
-}
index db981f5284bd30951ae9926f0121324995617584..d350c070916cafcdf8be38e6db671b8efda1a899 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * OBSOLETE FILE
  * declarations for hostside controller daemon
  */
 
@@ -7,54 +8,6 @@
 
 #include "common.h"
 
-#include <oop.h>
-#include <oop-read.h>
-
-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
index 7f2cc6d14edc71e51f9140b95d678da53572a427..1a8ffbd16c3ab2b6e63d3ec18c5ed04e03068733 100644 (file)
@@ -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 (executable)
index 0000000..cf3b434
--- /dev/null
@@ -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 (file)
index 0000000..1d30794
--- /dev/null
@@ -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 (file)
index 0000000..0c66f2c
--- /dev/null
@@ -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*/
index bc272de697ba1000aba52dcaa20e20031ba7fe0f..040ee6a5c50c0765c792648c32505fc6ff3bf585 100644 (file)
@@ -1,4 +1,7 @@
-/**/
+/*
+ * daemons
+ * output buffer chains
+ */
 
 #include <assert.h>
 #include <stdlib.h>
@@ -6,7 +9,7 @@
 #include <stdarg.h>
 #include <string.h>
 
-#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));
index b432222839ee90c4f747296eff9e77b8f312ca5c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,70 +0,0 @@
-/*
- * transmissions to clients
- */
-
-#include <string.h>
-#include <stdarg.h>
-
-#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);
-}
index 1698ebab49f899f0f25c00868ce834cd2dbf6b57..c35319ee7d5327565ffd24bcef66972de482dadc 100755 (executable)
@@ -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);
index 99e138504ca6da39bba686d2097009b6def1b570..7cb962a42aff5844fe92cf4fff406a4d6ce61fa9 100644 (file)
@@ -1,11 +1,14 @@
-/**/
+/*
+ * common
+ * utilities for helping parsing
+ */
 
 #include <stdarg.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 
-#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 (file)
index 0000000..4aad568
--- /dev/null
@@ -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; i<n->l; 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 (file)
index 0000000..94413e8
--- /dev/null
@@ -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 <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stddef.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "../layout/layout-data.h"
+
+typedef struct Segment Segment;
+typedef struct Train Train;
+
+/*---------- from retransmit.c ----------*/
+
+typedef struct RetransmitRelaxedNode RetransmitRelaxedNode;
+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 (file)
index 0000000..20f845b
--- /dev/null
@@ -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 (file)
index 0000000..5ba3d1c
--- /dev/null
@@ -0,0 +1,61 @@
+/* -*- fundamental -*- */
+
+%{
+#include "record.h"
+#include "record-l.h"
+%}
+
+%union {
+  const char *name;
+  Train *train;
+  Segment *seg;
+  int num;
+}
+
+%token <name>  TRAIN  SEG IS AT HAS STEP END  IDENT
+%token <name>  NL
+%token <num>   NUM
+
+%type <name>   ident
+%type <train>  train
+%type <seg>    seg
+%type <num>    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 (file)
index 0000000..45bcca6
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * ASCII-format record and config file reader/writer
+ *
+ * File format:
+ *
+ *  max-trains num
+ *  train <trainpn> at [-]<foredetectpn>:<maxinto>+-<uncertainty>
+ *  train <trainpn> is <addr> <head>+<detectable>+<tail>
+ *  train <trainpn> step <step>=<speed> <upwait>/<downwait>
+ *  seg <segpn> has [-]<ownerpn>|$
+ *  seg <segpn> at <movposcomb>
+ */
+
+#include <sys/mman.h>
+
+#include "record.h"
+#include "record-l.h"
+
+/*---------- input and error handling ----------*/
+
+static const char *filename;
+
+void record_yyerror(const char *m) {
+  die("config: %s:%d: %s", filename, record_yylineno, m);
+}
+
+/*---------- pname lookup (also handles train counting) ----------*/
+
+static char **train_pnames;
+
+Train *record_pname2train(const char *pname) {
+  int tran;
+  char **trap;
+  
+  for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
+    if (!strcmp(*trap, pname)) goto found;
+  }
+  if (trains) record_yyerror("number of trains changed between passes!");
+  tran= n_trains++;
+  train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
+  train_pnames[tran]= mstrdup(pname);
+
+found:
+  if (!trains) return 0;
+  return trains + tran;
+}
+
+Segment *record_pname2seg(const char *pname) {
+  int i;
+  const SegmentInfo *segi;
+  
+  for (i=0, segi=info_segments;
+       i<NUM_SEGMENTS;
+       i++, segi++)
+    if (!strcmp(segi->pname, pname))
+      return segments + i;
+  
+  return 0; /* silently discard data for segments no longer in the layout */
+}
+
+/*---------- zone allocator for strings ----------*/
+
+typedef struct TempZoneBlock TempZoneBlock;
+struct TempZoneBlock {
+  TempZoneBlock *next; /* for deallocation */
+  char buf[1];
+};
+
+static TempZoneBlock *tempzones_head;
+static char *tempzone_ptr;
+static int tempzone_remain, tempzone_head_len;
+
+char *record_tempzone_strdup(const char *s) {
+  int l;
+  char *r;
+  
+  l= strlen(s) + 1;
+  if (l > tempzone_remain) {
+    TempZoneBlock *new;
+    assert(l < INT_MAX/20);
+    tempzone_head_len= tempzone_remain= l*10 + 65536;
+    new= mmalloc(sizeof(new) + tempzone_remain);
+    tempzone_ptr= (char*)new + sizeof(new);
+    new->next= tempzones_head;
+    tempzones_head= new;
+  }
+  r= tempzone_ptr;
+  tempzone_remain -= l;
+  tempzone_ptr += l;
+  memcpy(r,s,l);
+  return r;
+}
+
+void record_tempzone_clear(void) {
+  TempZoneBlock *clear, *clear_next;
+  if (!tempzones_head) return;
+
+  clear= tempzones_head->next;
+  while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
+  tempzones_head->next= 0;
+  tempzone_remain= tempzone_head_len;
+  tempzone_ptr= tempzones_head->buf;
+}
+
+/*---------- semantic actions ----------*/
+/*
+ * These are all called only during the second pass, when trains is non-0.
+ * Also, all Train* and Segment* arguments are known to be non-0.
+ */
+
+void record_train_is(Train *tra, int addr, int head, int det, int tail) {
+  tra->addr= addr;
+  tra->head= head;
+  tra->detectable= det;
+  tra->tail= tail;
+}
+
+void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
+  tra->foredetect= seg;
+  tra->maxinto= maxi;
+  tra->uncertainty= unc;
+  tra->backwards= backw;
+}
+  
+void record_seg_has(Segment *seg, int backw, Train *tra) {
+  seg->owner= tra;
+  seg->tr_backwards= backw;
+}
+
+void record_seg_at(Segment *seg, const char *movposcombpname) {
+  const SegPosCombInfo *spci;
+  int i;
+  
+  for (i=0, spci=seg->i->poscombs;
+       i<seg->i->n_poscombs;
+       i++, spci++)
+    if (!strcmp(spci->pname, movposcombpname))
+      goto found;
+  return;
+  
+found:
+  seg->movposcomb= i;
+}
+
+/*---------- speed curves ----------*/
+
+static SpeedCurveEntry *curvebuf;
+static int curvebufsz, curvebufused;
+
+void record_train_step_count(void) {
+  curvebufsz++;
+}
+
+void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
+  Train *other;
+  SpeedCurveEntry *new;
+  int i;
+  
+  if (curvebufused >= curvebufsz) record_yyerror("more speed points on 2nd pass!");
+
+  if (!tra->accel.curve) {
+    tra->accel.curve= curvebuf + curvebufused;
+  }
+
+  /* Make room for an extra speed step point for this train,
+   * by moving all of the other trains up.  First move the data: */
+  memmove(tra->accel.curve + tra->accel.curvesz + 1,
+         tra->accel.curve + tra->accel.curvesz,
+         (char*)(curvebuf + curvebufused)
+         - (char*)(tra->accel.curve + tra->accel.curvesz));
+  /* Then adjust everyone's pointers: */
+  for (i=0, other=trains;
+       i<n_trains;
+       i++, other++)
+    if (other != tra && other->accel.curve &&
+       other->accel.curve >= tra->accel.curve)
+      other->accel.curve++;
+  /* ... that whole thing is O(n^2) if the speed points aren't sorted
+   * by train; we hope they are, in which case we are always adding
+   * to the last train in the list and there is then no data to copy,
+   * which makes it O(n*t) where n is the number of speed points and
+   * t is the number of trains.  If we cared we could optimise away
+   * the loop in the common case where this train is right at the end
+   * but a special case seems like asking for a bug when we do have
+   * out of order speed points. */
+
+  new= &tra->accel.curve[tra->accel.curvesz++];
+  curvebufsz++;
+
+  new->step= step;
+  new->speed= speed;
+  new->upwait= upw;
+  new->downwait= downw;
+}
+
+static int speedcurveentry_compare(const void *av, const void *bv) {
+  const SpeedCurveEntry *a= av, *b= bv;
+  if (a->step == b->step)
+    record_yyerror("multiple speed curve points at same step");
+  return a->step - b->step;
+}
+
+static void sort_curves(void) {
+  int i;
+  Train *tra;
+  
+  for (i=0, tra=trains;
+       i<n_trains;
+       i++, tra++) {
+    if (tra->accel.curve) {
+      if (tra->accel.curvesz < 2)
+       die("config: speed curve too short for %s", tra->pname);
+      qsort(tra->accel.curve, tra->accel.curvesz,
+           sizeof(*tra->accel.curve), speedcurveentry_compare);
+      if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
+       die("config: speed curve missing zero point for %s", tra->pname);
+    }
+  }
+}
+
+/*---------- entrypoint from main, and its subroutines ----------*/
+
+static void alloc(void) {
+  Train *tra; Segment *seg; const SegmentInfo *segi;
+  char **trap;
+  int i;
+  
+  segments= mmap(0, sizeof(*segments)*NUM_SEGMENTS + sizeof(*trains)*n_trains,
+                PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1,0);
+  if (!segments) diee("mmap for shared region");
+
+  for (i=0, seg=segments, segi=info_segments;
+       i<NUM_SEGMENTS;
+       i++, seg++, segi++) {
+    seg->movposcomb= -1;
+    seg->i= segi;
+  }
+
+  trains= (void*)(segments + NUM_SEGMENTS);
+  for (i=0, tra=trains, trap=train_pnames;
+       i<n_trains;
+       i++, tra++, trap++) {
+    tra->pname= *trap;
+    tra->addr= -1;
+    tra->head= -1;
+  }
+
+  curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
+}
+
+static void parse_pass(const char **argv) {
+  FILE *f;
+  
+  while ((filename= *argv++)) {
+    f= fopen(filename,"r");
+    if (!f) diee("config: cannot open input file: %s", filename);
+    record_yyrestart(f);
+    record_yyparse();
+  }
+}
+
+void records_parse(const char **argv) {
+  parse_pass(argv); /* trains==0: counts trains and curve points. */
+  alloc();
+  parse_pass(argv); /* trains!=0: populates data area */
+  record_tempzone_clear();
+  sort_curves();
+}
diff --git a/hostside/record.h b/hostside/record.h
new file mode 100644 (file)
index 0000000..366bd29
--- /dev/null
@@ -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 (executable)
index 0000000..469d230
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+$count= 20;
+$exp= 1.3;
+$first= 1.0;
+
+print <<END or die $!;
+#define SPEEDYCOUNT $count
+
+typedef struct {
+  Retransmit__Time interval; /* interval after this retransmission */
+  DLIST2_HEAD(RetransmitUrgentNode) queue; /* msgs transmitted ix times */
+} PerSpeedyTrans;
+
+END
+
+for ($ix=0, $accum=0; $ix<$count; $ix++) {
+    $val= int($first * $exp**$ix + 0.49);
+    $accum += $val;
+    push @s, sprintf("  { %3d } /* %3s cum=%3d cumforother=%3d".
+                    " use=%3d%% cumuse=%3d%% */",
+                    $val, "#$ix", $accum, $accum-($ix+1),
+                    (100.0/$val), (100.0*($ix+1))/$accum);
+}
+
+print("#define SPEEDIESINIT { \\\n",
+      join(", \\\n", @s),
+      "  \\\n }\n")
+    or die $!;
index 54c4fda8efea87341c6d35a8004c9f7a7e891115..d6f16480b026b871cec9e6b9b84cafc0cf4669ce 100644 (file)
 /*
+ * realtime
  * nmra retransmission
  */
 
-#include "hostside.h"
+#include "realtime.h"
 
-void retransmit_queue(RetransmitNode *rn) {
+/*
+ * There are two kinds of messages: urgent and relaxed.
+ *
+ * Urgent messages may be due for speedy transmission.  The due urgent
+ * message with the highest priority is always transmitted as soon as
+ * a slot is available.  This is known as `speedy transmission'.
+ * 
+ * When no urgent messages are due for speedy transmission, messages
+ * (urgent or relaxed) are transmitted in a round-robin fashion.  This
+ * round-robin transmission is completely independent of the speedy
+ * transmission.
+ *
+ * Each urgent message is due for speedy transmission as follows:
+ *   - When it is first queued and has not yet been
+ *     speedily transmitted:
+ *         immediately due, with maximum priority
+ *   - Following each speedy transmission:
+ *         due after an interval since the previous
+ *           transmission; this interval increases exponentially
+ *           for each transmission
+ *         with priority which decreases with later transmissions
+ *         until it has been speedily transmitted a certain number of times
+ *   - Thereafter:
+ *         no longer ever due; subject to round-robin transmission only.
+ * Intervals are measured in messages regardless of length.
+ */
+/*
+ * Implementation:
+ *  All messages are on the single relaxed queue.
+ *  There is one queue for each speady retransmission; messages are on
+ *  it in chronological order of dueness.
+ */
+
+#include "realtime.h"
+#include "nmra.h"
+#include "retransmit-table.h"
+
+#define RETRANSMIT_TIME_SIGNED_MAX ((~(Retransmit__Time)0) >> 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);
 }
index 935a0ab46079d01365e5e12154ebc683f07eeb91..179f0e4bbdaed52285d3a8dceeb42058719d1603 100644 (file)
@@ -1,94 +1,92 @@
 /*
+ * realtime
+ * safety core algorithm
  */
 
 #include <stdio.h>
 #include <assert.h>
 
-#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;
 }
index 2408248a11ccbfe162e6c6eeae48e19e3170b748..d7411315e8d4dc5920e4d3ecddbd6d2bfe95c4a0 100644 (file)
@@ -3,13 +3,15 @@
 #ifndef SAFETY_H
 #define SAFETY_H
 
+#include <stdarg.h>
+
 #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 (file)
index 0000000..2cdbd4a
--- /dev/null
@@ -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 (file)
index 0000000..10e0f4a
--- /dev/null
@@ -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
index ec5dbb78a3c41176ca439230e363dd7ca8624e69..595066837b981d998d61a7b7d61369e958d4f72c 100755 (executable)
@@ -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 (file)
index 0000000..4f470d0
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * mulitplexer daemon
+ * transmissions to clients of their selected messages
+ */
+
+#include <string.h>
+#include <stdarg.h>
+
+#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) );
+}
index 1075de9f9d5fd7e0bd69713c0b2cbb502f271228..f1b9a7cdd5b342aa06c54a663a0aa439d3cfa95a 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * common
  * general serial i/o and system interface etc.
  */
 
index 5e2339c18de704a40c0df23fadc4e2a1a1ffc6f6..92c847d9caa787aba911f8cf09bd2c8ef2810be1 100644 (file)
@@ -7,29 +7,35 @@
  * and related functions
  */
 
-#include "hostside.h"
+#include <assert.h>
+
+#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 }
 };
index e42b48c747ecd2ae0a646d9153a2724f847dd963..ee63e6492b2f9061cbb4d4627dcf6acf76753765 100644 (file)
 #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 (file)
index 0000000..8f5a88d
--- /dev/null
@@ -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 (file)
index 0000000..955a443
--- /dev/null
@@ -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 (executable)
index 0000000..faa07fb
--- /dev/null
@@ -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 }'
index 4a1771448a2e110d3d3ba76251a70ad5db2eb09a..0818c6b751b976edf2cb54bf83c608539d505c27 100644 (file)
@@ -3,15 +3,13 @@
 
 #include <assert.h>
 
-#include "safety.h"
+#include "realtime.h"
 
 const SegPosCombInfo *trackloc_segposcomb(const TrackLocation *tloc) {
-  State *s= &safety_state;
-  SegmentState *seg= &s->segments[tloc->segn];
-  const SegmentInfo *segi= &info_segments[tloc->segn];
+  Segment *seg= tloc->seg;
 
-  assert(seg->movposcomb < segi->n_poscombs);
-  return &segi->poscombs[seg->movposcomb];
+  assert(seg->movposcomb < seg->i->n_poscombs);
+  return &seg->i->poscombs[seg->movposcomb];
 }
 
 const SegmentLinkInfo *trackloc_segmentlink(const TrackLocation *tloc,
@@ -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;
   }
index b6c44948f8f7a14fb9b0a4e706f21702c5b2bc9f..5e0824432d014d31ed90ab4403cc5f3678d752e3 100644 (file)
@@ -1,4 +1,7 @@
-/**/
+/*
+ * common
+ * general utility functions
+ */
 
 #include <stdarg.h>
 #include <errno.h>
@@ -6,15 +9,35 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#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");
+}
index 0066e8dfa44c4df98a68c851f473a70296986aa6..47ae957bbb81ccf831920c1dd1c076d8911e54be 100644 (file)
@@ -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   \
index ce477e9202868114fb2ca2fbb04402572598a5ad..0bd5708a755180812c9622da8363c94c90ad8ecb 100755 (executable)
@@ -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();
index cf5f9e46c676beb31aeacd09ae958ed3e4f45d1c..d47a57c9b6f068e636f4cca5e02febc34c4124d8 100755 (executable)
@@ -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;
index 84391c7b213026a1bd9aba351e0c16fd3b21cd16..4bb569a564bcdb8a9742dac8439680f52631f6c7 100644 (file)
 
 /*========== 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
  * <name-of-layout>.layout-data.c from the first #include onwards, and
- * this will give definitions of NUM_SEGMENTS and NUM_TRAINS.
+ * this will give a definition of NUM_SEGMENTS.
  */
 
 extern const SegmentNum info_nsegments;
-extern const SegmentInfo info_segments[]; 
-
-extern const TrainNum info_nsegments;
-extern const TrainInfo info_trains[];
+extern const 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*/
index d445442b7335a2ee17a4b33b84c7d9d2e41cf69f..dbd6cc8d0e1dc01f042ba515f9da79b41997876e 100644 (file)
--- 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 <n> 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