chiark / gitweb /
wip multiplex - much better now, including some message changes
authorian <ian>
Sun, 25 May 2008 20:35:55 +0000 (20:35 +0000)
committerian <ian>
Sun, 25 May 2008 20:35:55 +0000 (20:35 +0000)
hostside/.cvsignore
hostside/README.commands
hostside/commands.c
hostside/errorcodes.h.gen
hostside/multiplex
hostside/multiplex-config
hostside/safety.c
hostside/startup.c

index eb8ad251cfb8367e349ebd84c6354d09f28ed2c6..c634ab7aede18a0dcb4f2365f59e15e39bd7bbf5 100644 (file)
@@ -21,5 +21,6 @@ record-l.[ch]
 record-y.[ch]
 +persist*
 +*.log
++realtime.stderr
 *+dflags.h
 *.new
index 2fac063f351ff9e815eae0b223ac6a95a30c7a6f..7580a79fbdc004533d443eb178d9bc1b11a7699b 100644 (file)
@@ -1,13 +1,20 @@
 Protocol over new hostside stdin and to multiplexer:
 
- U< ?...   means realtime prints that message without any ?
-           when reflected by multiplexer, one of - + &
-           is prepended, as follows
-                  -   result of command by another client
-                  +   result of command by us
-                  &   asynchronous
-           other prefixes
-                 =   related to this actual connection
+ U<   possibly-asynchronous messages from realtime
+ R<   ditto, but saved for replay too
+ S    messages to simulation log
+
+ C>   commands (reflected to other clients)
+ L>   commands implemented by multiplexer and not reflected to other clients
+ O<   framming output from commands
+
+Messages from the multiplexer to its clients are always prefixed
+with a relevance character, shown as ? below.  In messages from
+realtime, the ? is absent.  ? may be
+      -   result of command by another client
+      +   result of command by us
+      &   asynchronous, not related to a specific command
+      =   related to this actual multiplexer client connection
 
 ======================================================================
 
@@ -24,13 +31,13 @@ POSSIBLY-ASYNCHRONOUS REPORTING OF MESSAGES TO/FROM (MASTER) PIC
  U< ?picioh in toolong <byte> [<byte>...]
  U< ?picioh in msg     <byte> [<byte>...]
 
U< ?picio out polarity <[<segment>[,...]]>    literal < and > bracket segs
R< ?picio out polarity <[<segment>[,...]]>    literal < and > bracket segs
  U< ?picio out unknown                         data printed in assoc'd picioh
 
  U< ?picio in <messagename> [<objectnum>]
  U< ?picio out <messagename> [<objectnum>]
 
- U< ?picio in-info detect 0|1 <segment-name>   decoded `picio in detect[01]'
+ U< ?detect <segment-name> 0|1         decoded `picio in detect[01]'
 
  Suppression (see parse-proto-spec and realtime.c:serial_transmit)
        nmra data       ping/pong       special in      other out
@@ -42,29 +49,32 @@ POSSIBLY-ASYNCHRONOUS REPORTING OF MESSAGES TO/FROM (MASTER) PIC
 
  U< ?resolving <some message about resolution algorithm>
 
S< ?resolution inexplicable <segment>
S< ?resolution mispositioned head|tail <train> <crush-ending-seg> <distance>
S< ?resolution movpos-change-failed <segment>/<poscombname>
S< ?resolution problems <number-of-problems>
R< ?resolution inexplicable <segment>
R< ?resolution mispositioned head|tail <train> <crush-ending-seg> <distance>
R< ?resolution movpos-change-failed <segment>/<poscombname>
R< ?resolution problems <number-of-problems>
 
S< ?stastate <state>
R< ?stastate <state>
 
  U< ?warning <type>[ <arguments>] :  <warning message>
  U< ?warning watchdog : PIC watchdog timer triggered
  U< ?warning spurious <count> : spurious short circuit (fault) etc.
+ U< ?warning realtime-stopped SIG*|exit*|E*|... : <explanation>
+ U< ?warning realtime-stderr : <emsg>
+ U< ?warning realtime-stderr-unavailable : <emsg>
 
  U< ?train <train> signalling-problem ....
  U< ?train <train> signalling-problem <problematic-segment> : <message>
 
- S< ?train <train> forwards|backwards at [-]<segment>:<maxinto>+-<uncertainty>
S< ?train <train> has <markchar>[-]<segment>[/<movposcomb>]....
+ R< ?train <train> at [-]<segment>:<maxinto>+-<uncertainty> forwards|backwards
R< ?train <train> has <markchar>[-]<segment>[/<movposcomb>]....
        <markchar> is * for det_expected
                      ! for foredetect
                      @ for det_ignore
 
S< ?movpos <segment> feat <feat><posn> point|relay
S< ?movpos <segment> position <overallposn> moving
S< ?movpos <segment> position <overallposn> stable
R< ?movpos <segment> feat <feat><posn> point|relay
R< ?movpos <segment> position <overallposn> moving
R< ?movpos <segment> position <overallposn> stable
             <overallposn> may be ? for unknown
 
  U< ?debug <context> : <debug message>
@@ -72,27 +82,27 @@ POSSIBLY-ASYNCHRONOUS REPORTING OF MESSAGES TO/FROM (MASTER) PIC
 
 MESSAGES TO SIMULATION LOG
 
L  picioh in suppressed <byte> [<byte>...]
L  picioh in suppressed-detect <byte> [<byte>...]
L  command-in <command> <args>....
L  timestamp <seconds>.<microseconds>
L  timer-event <class>.<instance>
L  0|1                   detection, same segment as last suppressed-detect
S  picioh in suppressed <byte> [<byte>...]
S  picioh in suppressed-detect <byte> [<byte>...]
S  command-in <command> <args>....
S  timestamp <seconds>.<microseconds>
S  timer-event <class>.<instance>
S  0|1                   detection, same segment as last suppressed-detect
 
 ======================================================================
 
 COMMANDS AND RESPONSES
 
P> <command> [<arguments>...]
C> <command> [<arguments>...]
   results in:
R< ?nak unknown-command|permission-denied|invalid-syntax [: <message...>]
- R< ?executing <command>
O< ?nak <errorcode> [<remaining info>] [: <message...>]
+ O< ?executing <command> [<arguments>...]
   consequential messages including picio, signalling problems etc.
-  then one of these
R< ?ack <command> ok
R< ?ack <command> <errorcode>
R< ?ack <command> <errorcode> [<remaining info>] : <remaining error msg>...
R< ?ack <command> <errorcode> SignallingPredictedProblem \
+  which may be U< R< then one of these
O< ?ack <command> ok
O< ?ack <command> <errorcode>
O< ?ack <command> <errorcode> [<remaining info>] : <remaining error msg>...
O< ?ack <command> <errorcode> SignallingPredictedProblem \
           <problematic-train> <problematic-segment>|? : <error message>...
 
     when these come through the multiplexer, everything which is a
@@ -107,18 +117,17 @@ COMMANDS AND RESPONSES
 
 MULTIPLEXER FACILITIES
 
- M< =  indicates messages from the multiplexer about this
- M> =   specific client connection
-
- M< =connected
- M< =permission normal|super
-  or
- M< =denied
+ U< =connected                           } on
+ U< =permission normal|super             } initial
+  or                                     }  connection
+ U< =denied                              }
   and later perhaps
- A< =failed : <message>           regardless of selected message patterns
-  otherwise there is no need to quit - just send eof
+ U< =failed : <message>              } regardless of
+ U< =failed client-io : <message>    }   selected message patterns
+                                     }  and then connection closed
+  There is no need to ask to quit - just send eof
 
M> select [~]<pattern>...
L> select [~]<pattern>...
     first match wins; ~ discards the message.
     in pattern
         -+./:0-9a-zA-Z   match themselves
@@ -129,19 +138,20 @@ MULTIPLEXER FACILITIES
     if no match, it is as if the following patterns were appended
        ?info ?warning ~-* ~&* ~+debug *
 
- M> replay [~]<glob-pattern>...
- M< +executing replay
- M< <all saved messages, ie those marked with S< above>
- M< +ack replay ok
+ L> replay [[~]<glob-pattern>...]     if none, uses result from select
+ O< +executing replay
+ R< +<all messages saved for replay, ie those marked with R< above,
+      matching the patterns, all of which will be prefixed with + this time>
+ O< +ack replay ok
 
 
 MULTIPLEXER-IMPLEMENTED FUNCTIONALITY AFFECTING WHOLE SYSTEM
 
P> [!]<command> <args>...
R< ?nak|executing...ack...  as above
C> [!]<command> <args>...
O< ?nak|executing...ack...  as above
 
- C> !realtime kill|finish|start|start-manual
- C> !save
+ C> !realtime kill|start|start-manual
+ C> !dump
 
 
 ======================================================================
@@ -149,20 +159,20 @@ MULTIPLEXER-IMPLEMENTED FUNCTIONALITY AFFECTING WHOLE SYSTEM
 DIRECT NMRA AND PIC INSTRUCTIONS
 
                                             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
C> !nmra [<slot>] [<nmra-command [<nmra-args>...]]    nmra speed28 3 13 1
C> !nmra [<slot>] [=<nmra-bytes>]                     nmra =0348
C> !nmra [<slot>] [:<nmra-bytes-with-csum>]           nmra :03484b
C> !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>
C> !nmra <slot>
    cancels the relevant retransmission.
 
                                             Example (always the same msg):
P> !pic =<pic-bytes>                                   pic =a003
P> !pic <pic-command> [<pic-args...>]                  pic point 3
C> !pic =<pic-bytes>                                   pic =a003
C> !pic <pic-command> [<pic-args...>]                  pic point 3
        the latter for commands with no `...' in README.protocol only
 
  Keen readers will observe that
index 36386a0de684f118fa65619a1195c1ab18df924f..0fcc7a2d3636b02427975e58f6e432710d65a388 100644 (file)
@@ -307,19 +307,21 @@ static int cmd_invert(ParseState *ps, const CmdInfo *ci) {
  
 void command_doline(ParseState *ps, CommandInput *cmdi_arg) {
   int r;
+  const char *cmdline;
 
   simlog("command-in %s\n",ps->remain);
   simlog_flush();
   current_cmd= 0;
 
-  if (!ps->remain[0]) return;
+  cmdline= ps->remain;
+  if (!cmdline[0]) return;
   r= ps_word(ps);  assert(!r);
   current_cmd= some_lookup(ps,toplevel_cmds);
   if (!current_cmd) {
-    ouprintf("nack unknown-command\n");
+    ouprintf("nack UnknownCommand\n");
     return;
   }
-  ouprintf("executing %s\n",current_cmd->name);
+  ouprintf("executing %s\n",cmdline);
   if (sta_state < Sta_Run && !(current_cmd->xarg & CIXF_ANYSTA)) {
     ouprintf("ack %s InvalidState : layout not ready\n",current_cmd->name);
     return;
index d2aaf5b770c89c285157ef604f38297fa38ad18d..2eaf6307bed6084aecad1e31a0c705c2dae76544 100755 (executable)
        InvalidState
        SignallingPredictedProblem
        SignallingHorizonReached
+
        LimitExceeded
+       SystemFailed
+       UnknownCommand
+       PermissionDenied
+       HostSupportSystemsProblem
        );
 
 
index 6b8056ff7991cfb8391f752e8a151e96bd8c7f4c..5293905bd235c254436d88e2a4262134d5cd56e5 100755 (executable)
@@ -1,18 +1,44 @@
-#!/usr/bin/tclsh8.4
+#!/usr/bin/tcl
 
 # per connection:
 #    c/$conn(super)          0 or 1
 #    c/$conn(ipaddr)
 #    [msel/$conn "$msg "]    0 or 1
+#    [mreplay/$conn "$msg "] 0 or 1 during replay only
+#    c/$conn(q)              [list $inputline ...]
 #
 # globals:
-#    $cmdqueue         [list $conn $cmd ...]
-#    $master           socket
-#    $permissions      [list allow|super|deny $ipaddrhex $maskhex ...]
-
-#---------- i/o to realtime ----------
+#    $queueing            [list $conn ...]
+#    $master              socket
+#    $permissions         [list allow|super|deny $ipaddrhex $maskhex ...]
+#    $realtime            pipes
+#    $replay("$pri $key") $rhs     $pri is \d\d; causes replay of  "$key $rhs"
+#    $detect0($seg)       unset -> 1 or irrelevant; [after ...]
+#    $conns($conn)        1
+#
+# configs set directly in multiplex-config:
+#    $records          [list filename.record ...]
+#    $detectlag        <ms>
+#
+# $conn is in $queueing iff c/$conn(q) is nonempty
+#
+# globals relating to realtime's command execution state:
+#                           awaiting        awaiting  executing global
+#                   idle     executing/nak   ack       internal cmd     dead
+#
+#    $realtime       set      set            set          ?             unset
+#    $currentcmd     unset    $cmd           $cmd         $cmd          unset
+#    $currentconn    unset    $conn or {}    $conn or {}  $conn         unset
+#    $executing      0        0              1            1             0
+#
+# replay priorities and messages:
+#    10 stastate
+#    50 resolution ....
+#    60 movpos ...
+#       train ...
+#    70 detect <seg> 0|1
 
-proc cmdqueue-check {args} { }
+#---------- replay, general utilities, etc. ----------
 
 proc compile-glob-patterns {pats procname} {
     if {[llength $pats] > 20 || [string length $pats] > 200} {
@@ -36,20 +62,107 @@ proc compile-glob-patterns {pats procname} {
     append def {    {^[-&]\S|^\+debug\s} { return 0 }} "\n"
     append def "    }\n"
     append def "    return 1\n"
-    puts $def
     proc $procname {m} $def
 }
 
-proc cmd/select {conn args} {
+proc nargs {l {n 0}} {
+    if {[llength $l]!=$n} { cmderr BadCmd "wrong number of arguments" }
+}
+
+proc xmit-relevantly {m} {
+    global executing currentconn conns
+    if {$executing} {
+       puts "<* $m"
+       set myconn $currentconn
+       trapping xmit-only-noreport $currentconn +$m
+       set othersm -$m
+    } else {
+       puts "<& $m"
+       set myconn {}
+       set othersm &$m
+    }
+    foreach conn [array names conns] {
+       if {[string compare $myconn $conn]} {
+           trapping xmit-only-noreport $conn $othersm
+       }
+    }
+}
+
+#---------- multiplexer-implemented command ----------
+
+proc local/select {conn args} {
     upvar #0 c/$conn c
     compile-glob-patterns $args msel/$conn
 }
 
+proc global/!dump {conn args} {
+    nargs $args
+    if {[catch { save-dump } emsg]} {
+       cmderr HostSupportSystemsProblem "failed to save dump: $emsg"
+    }
+}
+
+proc local/replay {conn args} {
+    global replay
+    if {[llength $args]} {
+       rename msel/$conn mreplay/$conn ;# park it here for a moment
+       compile-glob-patterns $args msel/$conn
+    }
+    foreach pk [lsort [array names replay]] {
+       set m "+[string range $pk 3 end] $replay($pk)"
+       xmit-only $conn $m
+    }
+    if {[llength $args]} {
+       rename mreplay/$conn msel/$conn
+    }
+}
+
+proc global/!realtime {conn args} {
+    nargs $args 1
+    switch -exact [lindex $args 0] {
+       kill { realtime-failed killed "termination requested by command" }
+       start { realtime-start {} }
+       start-manual { realtime-start -m }
+       default { cmderr BadCmd "unknown !realtime subcommand" }
+    }
+}
+
 #---------- connection and commands ----------
 
 proc client-inputline {conn l} {
+    global queueing
+    upvar #0 c/$conn c
+    puts "$conn> $l"
+    client-disable-readable $conn
+    if {![llength $c(q)]} { lappend queueing $conn }
+    lappend c(q) $l
+    after idle process-queues
+}
+
+proc process-queues {} {
+    global queueing currentcmd
+    while {![info exists currentcmd] && [llength $queueing]} {
+       set conn [lindex $queueing 0]
+       set queueing [lrange $queueing 1 end]
+
+       upvar #0 c/$conn c
+       if {![llength $c(q)]} continue
+
+       set l [lindex $c(q) 0]
+       set c(q) [lrange $c(q) 1 end]
+       if {[llength $c(q)]} {
+           lappend queueing $conn
+       } else {
+           client-enable-readable $conn
+       }
+
+       trapping process-command $conn $l
+    }
+}
+
+proc process-command {conn l} {
+    global currentcmd currentconn realtime executing
     global errorInfo errorCode
-    global cmdqueue
     upvar #0 c/$conn c
     set cmd ?
     set r [catch {
@@ -57,35 +170,70 @@ proc client-inputline {conn l} {
        set l [string trim $l]
        if {![string length $l]} return
        if {![regexp {^((!?)[-a-z0-9]+)(?:\s.*)?$} $l dummy cmd priv]} {
-           error "improper command name" {} {TRAIN CMDNAK invalid-syntax}
+           error "improper command name" {} {TRAIN CMDNAK BadCmd}
        }
        if {[regexp {[^ \t!-~]} $l]} {
-           error "improper character" {} {TRAIN CMDNAK invalid-syntax}
+           error "improper character" {} {TRAIN CMDNAK BadCmd}
        }
        if {[string length $priv] && !$c(super)} {
-           error "" {} {TRAIN CMDNAK permission-denied}
+           error "" {} {TRAIN CMDNAK PermissionDenied}
        }
-       if {![catch { info args cmd/$cmd }]} {
-           xmit $conn "+executing $cmd"
-           eval [list cmd/$cmd $conn] [lrange [split $l] 1 end]
+
+       if {![catch { info args global/$cmd }]} {
+           set currentcmd $cmd
+           set currentconn $conn
+           set executing 1
+           xmit-relevantly "executing $cmd"
+           eval [list global/$cmd $conn] [lrange [split $l] 1 end]
+       } elseif {![catch { info args local/$cmd }]} {
+           xmit-only $conn "+executing $cmd"
+           eval [list local/$cmd $conn] [lrange [split $l] 1 end]
+       } elseif {![info exists realtime]} {
+           error "" {} {TRAIN CMDNAK realtime-not-running}
        } else {
-           lappend cmdqueue $conn $l
-           after idle cmdqueue-check
+           set currentcmd $cmd
+           set currentconn $conn
+           if {[catch {
+               puts ">> $l"
+               puts $realtime $l
+           } emsg]} {
+               if {[string match {POSIX EPIPE *} $errorCode]} {
+                   realtime-failed EPIPE ""
+               } elseif {[string match {POSIX *} $errorCode]} {
+                   realtime-failed [lindex $errorCode 1] \
+                           "write failed: [lindex $errorCode 2]"
+               } else {
+                   realtime-failed ?write-[lindex $errorCode 0] \
+                           "puts failed ($errorCode): $emsg"
+               }
+           }
+           return
        }
     } emsg]
     if {$r==2} return
-    if {$r==0} { xmit $conn "+ack $cmd ok"; return }
+    if {$r==0} {
+       set m "ack $cmd ok"
+       if {$executing} {
+           xmit-relevantly $m
+           set executing 0
+           unset currentconn
+           unset currentcmd
+       } else {
+           xmit-only $conn +$m
+       }
+       return
+    }
     switch -glob $errorCode {
        {TRAIN REPORTED*} {
        }
        {TRAIN CMDNAK*} {
            set el [concat [list +nack] [lrange $errorCode 2 end]]
            if {[string length $emsg]} { append el ": " $emsg }
-           xmit $conn "$el"
+           xmit-only $conn "$el"
        }
        {TRAIN CMDERR*} {
            set el [concat [list +ack $cmd] [lrange $errorCode 2 end]]
-           xmit $conn "$el : $emsg"
+           xmit-only $conn "$el : $emsg"
        }
        * {
            set ei $errorInfo; set ec $errorCode
@@ -95,7 +243,17 @@ proc client-inputline {conn l} {
     }
 }
 
-proc client-eof {conn} { kill-conn $conn "" }
+proc client-eof {conn} {
+    puts "$conn>\$"
+    kill-conn $conn ""
+}
+
+proc client-enable-readable {conn} {
+    fileevent $conn readable [list trapping readable client $conn]
+}
+proc client-disable-readable {conn} {
+    fileevent $conn readable {}
+}
 
 #---------- general IO ----------
 
@@ -104,26 +262,44 @@ proc readable {whatfor conn} {
     if {[eof $conn]} { $whatfor-eof $conn }
 }
 
-proc xmit-always {conn msg} { puts $conn $msg }
+proc xmit-puts {conn msg} {
+    global conns errorInfo
+    if {![info exists conns($conn)]} return
+    if {[catch { puts -nonewline $conn $msg } emsg]} {
+       kill-conn $conn "=failed client-io : $emsg"
+       error $emsg $errorInfo {TRAIN REPORTED}
+    }
+}
 
-proc xmit {conn msg} {
+proc xmit-only-always {conn msg} {
+    puts "<$conn $msg"
+    xmit-puts $conn "$msg\n"
+}
+proc xmit-only-noreport {conn msg} {
     append msg "\n"
-    if {[msel/$conn $msg]} { puts -nonewline $conn $msg }
+    if {[msel/$conn $msg]} { xmit-puts $conn $msg }
+}
+proc xmit-only {conn msg} {
+    puts "<$conn $msg"
+    xmit-only-noreport $conn $msg
 }
 
 #---------- error handling ----------
 
 proc kill-conn {conn msg} {
+    global conns queueing
     upvar #0 c/$conn c
-    global cmdqueue
-    if {[string length $msg]} { catch { xmit-always $conn $msg } }
+    catch { unset conns($conn) } ;# do this first to stop any recursion
+    puts "<$conn\$ closing : $msg"
+    if {[string length $msg]} { catch { xmit-only-always $conn "$msg" } }
     catch { close $conn }
+    if {[set ix [lsearch -exact $queueing $conn]] >= 0} {
+       set queueing [lreplace $queueing $ix $ix]
+    }
     catch { unset c }
     catch { rename msel/$conn {} }
+    catch { rename mreplay/$conn {} }
     set qn {}
-    foreach {tconn tl} $cmdqueue {
-       if {[string compare $tconn $tl]} { lappend qn $tconn $tl }
-    }
     set cmdqueue $qn
 }
 
@@ -154,7 +330,7 @@ proc trapping {proc conn args} {
     if {![catch { uplevel #0 [list $proc $conn] $args } r]} { return $r }
     switch -glob $errorCode {
        {TRAIN EXPECTED*} { kill-conn $conn "=failed : $r" }
-       {TRAIN SILENT*} { kill-conn $conn }
+       {TRAIN REPORTED*} { kill-conn $conn "" }
        * {
            report-unexpected {UNEXPECTED ERROR} $r
             kill-conn-ierr $conn
@@ -162,6 +338,164 @@ proc trapping {proc conn args} {
     }
 }      
 
+#---------- realtime subprocess ----------
+
+proc realtime-failed {k m} {
+    global realtime currentcmd currentconn executing
+    global errorInfo errorCode
+    # if $m is "", use wait status
+    if {![string length $m]} {
+       set r [catch {
+           fconfigure $realtime -blocking 1
+           close $realtime
+       } emsg]
+       if {!$r} {
+           set m "unexpectedly closed pipe ?!"
+           set k unexpected
+       } elseif {[string match {CHILDSTATUS*} $errorCode]} {
+           set m "exited with status [lindex $errorCode 2]"
+           set k "exit[lindex $errorCode 2]"
+       } elseif {[string match {CHILDKILLED*} $errorCode]} {
+           set m "killed by signal [lindex $errorCode 3]"
+           set k [lindex $errorCode 2]
+       } else {
+           set m "failed confusingly ($errorCode): $emsg"
+           set k ?wait-[lindex $errorCode 0]
+       }
+    } else {
+       catch { close $realtime }
+    }
+    puts "<<\$ $k : $m"
+    catch { unset realtime }
+    if {[catch {
+       set sef [open +realtime.stderr r]
+       while {[gets $sef l] >= 0} {
+           xmit-relevantly "warning realtime-stderr : $l"
+       }
+       close $sef
+       unset sef
+    } emsg]} {
+       if {![string match {POSIX ENOENT *} $errorCode]} {
+           xmit-relevantly "warning realtime-stderr-unreadable : $emsg"
+       }
+       catch { close $sef }
+    }
+    xmit-relevantly "warning realtime-stopped $k : $m"
+    if {[catch {
+       save-dump
+    } emsg]} {
+       xmit-relevantly "warning dump-failed : $emsg"
+    }
+    if {$executing} {
+       xmit-relevantly "ack $currentcmd SystemFailed realtime : $m"
+       set executing 0
+    } elseif {[info exists currentcmd]} {
+       xmit-relevantly "nak SystemFailed realtime : $m"
+       unset currentcmd
+       unset currentconn
+    }
+    realtime-notrunning-init
+}
+
+proc realtime-notrunning-init {} {
+    realtime-inputline dummy "stastate -" ;# fake this up
+}
+
+proc realtime-start {xopts} {
+    global realtime records
+    if {[info exists realtime]} {
+       cmderr InvalidState "realtime already running"
+    }
+    set cl [concat \
+           [list 2> +realtime.stderr ./realtime -v2] \
+           $xopts $records]
+    if {[catch {
+       set realtime [open |$cl r+]
+       fconfigure $realtime -translation binary -buffering line -blocking 0
+       fileevent $realtime readable [list readable realtime $realtime]
+    } emsg]} {
+       realtime-failed start $emsg
+       error "realtime failed : $emsg" "" {TRAIN REPORTED}
+    }
+}
+
+proc realtime-eof {dummy} { realtime-failed EOF "" }
+
+proc detect0timeout {seg} {
+    global replay
+    upvar #0 detect0($seg) d0
+    unset d0
+    set key "detect $seg"
+    set replay("70 $key") 0
+    xmit-relevantly "$key 0"
+}
+
+proc clear-replay-detects {} {
+    global replay
+    foreach k [array names replay] {
+       if {[regexp {^.. detect } $k]} { unset replay($k) }
+    }
+}
+
+proc realtime-inputline {dummy l} {
+    global detectlag replay
+
+    puts "<< $l"
+
+    if {[regexp {^(detect (\w+)) ([01])$} $l dummy key seg value]} {
+       upvar #0 detect0($seg) d0
+       catch { after cancel $d0 }
+       if {$value} {
+           if {[info exists d0]} { unset d0; return }
+       } else {
+           set d0 [after $detectlag detect0timeout $seg]
+           return
+       }
+       set pri 70
+    } elseif {[regexp {^(stastate) (.*)$} $l dummy key value]} {
+       switch -exact -- $value {
+           Settling { clear-replay-detects }
+           Resolving { catch { unset replay("50 resolution") } }
+       }
+       set pri 10
+    } elseif {[regexp {^(resolution) (.*)$} $l dummy key addvalue]} {
+       set pri 50
+    } elseif {[regexp -expanded {
+       ^( movpos \s \S+ \s (?: feat \s \S+ | position) |
+          train  \s \S+ \s (?: has | at ) |
+          picio  \s out \s polarity
+       ) \s (.*) $
+              } $l dummy key value]} {
+        set pri 60
+    }
+    if {[info exists key]} {
+       set pk "$pri $key"
+       upvar #0 replay($pk) rep
+       if {[info exists value]} {
+           set rep $value
+       } else {
+           append rep $value
+       }
+    }
+
+    switch -regexp -- $l {
+       {^executing\s|^nak\s} {
+           set executing 1
+       }
+    }
+
+    xmit-relevantly $l
+
+    switch -regexp -- $l {
+       {^ack\s|^nak\s} {
+           set executing 0
+           unset currentcmd
+           unset currentconn
+           after idle process-queues
+       }
+    }
+}
+
 #---------- new connections ----------
 
 proc find-permission {ipaddr} {
@@ -174,8 +508,11 @@ proc find-permission {ipaddr} {
 }
 
 proc connected {conn} {
+    global conns
     upvar #0 c/$conn c
     fconfigure $conn -blocking 0 -buffering none -translation auto
+    set c(q) {}
+    set conns($conn) 1
     set perm [find-permission $c(ipaddr)]
     switch -exact $perm {
        deny { finally $conn =denied; return }
@@ -185,14 +522,15 @@ proc connected {conn} {
     }
     compile-glob-patterns {} msel/$conn
     
-    xmit-always $conn =connected
-    xmit-always $conn "=permission [lindex {normal super} $c(super)]"
-    fileevent $conn readable [list trapping readable client $conn]
+    xmit-only-always $conn =connected
+    xmit-only-always $conn "=permission [lindex {normal super} $c(super)]"
+    client-enable-readable $conn
 }
 
 proc newconn {conn ipaddr port} {
     upvar #0 c/$conn c
     catch { unset c }
+    puts "$conn new-client $conn $ipaddr $port"
     set c(ipaddr) $ipaddr
     trapping connected $conn
 }
@@ -240,10 +578,13 @@ proc binding {addr blist} {
 }
 
 proc startup {} {
-    global cmdqueue
+    global queueing executing
     catch { close $master }; catch { unset master }
-    source multiplex-config
-    set cmdqueue {}
+    uplevel #0 source multiplex-config
+    set queueing {}
+    set executing 0
+    realtime-notrunning-init
+    commandloop -async -prompt1 { return "% " } -prompt2 { return "> " }
 }
 
 startup
index 13c47c41ceeec6ed1a6983dd62a4c591042a6531..4e87d3a94dd7f204a5669c051619c3032a3c632b 100644 (file)
@@ -10,3 +10,8 @@ binding 127.0.0.1 {
        super   127.0.0.1
        deny    0.0.0.0/32
 }
+set records {
+       shinkansen.speeds.record
+       homes.record
+}
+set detectlag 100
index fb0fd3f553cd0ec4c4c73295a8cf6f0a1c6e6a99..e67f85ae019fd3731d3ce60290664e691387b873 100644 (file)
@@ -965,10 +965,11 @@ ErrorCode predict(Train *tra, struct timeval tnow, unsigned flags,
 /*========== reporting position and ownership ==========*/
 
 void report_train_position(Train *tra) {
-  ouprintf("train %s %s at %s%s:%d+-%d\n",
-          tra->pname, tra->backwards ? "backwards" : "forwards",
+  ouprintf("train %s at %s%s:%d+-%d %s\n",
+          tra->pname,
           tra->foredetect->tr_backwards?"-":"",
-          tra->foredetect->i->pname, tra->maxinto, tra->uncertainty);
+          tra->foredetect->i->pname, tra->maxinto, tra->uncertainty,
+          tra->backwards ? "backwards" : "forwards");
 }  
   
 static int report_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
index dd6fd0cb3de94f232257ae5de689c7f4947d7144..0cdc9455f137920ccd50d5ca61b3e53ce3edd04c 100644 (file)
@@ -292,8 +292,7 @@ static SegmentNum on_pic_detect_prep(int detyn, int objnum) {
   if (!(picio_send_noise <= 1 &&
        segments[segn].owner &&
        segments[segn].det_ignore))
-    ouprintf_only("picio in-info detect %d %s\n",
-                 detyn, info_segments[segn].pname);
+    ouprintf_only("detect %s %d\n", info_segments[segn].pname, detyn);
 
   return segn;
 }