#!/usr/bin/tclsh8.3 # used like this: # liberator:hostside> ssh bessar 'cd things/trains-bessar/hostside && ./stopgap-controller' | ./gui-displayer - set testonly 0 #set testonly 1 set port /dev/ttya0 #set port /dev/ttyS0 set locos {1 2 4} set locoix 0 set loco [lindex $locos $locoix] set ch(funcsevery) 10 set ch(speeddirnevery) 30 set ch(scale) 1 set ch(minint) 5000 set pointprobs {0 0x020 0x080 0x0e0 0x100} set pointprobix 0 set pointabs 1 ;# 0 or 1 set nmrawhich 0 set lastptchosen xx set pointprob [lindex $pointprobs $pointprobix] set polmsg(l) 908000 set polmsg(x) 90f802 set polmsg(y) 90807c set pname l set m {} set nmradiv 0 set segs {xx yy} set segsasgot {xx yy} set pq {} ;# unset: cdu charged and waiting set speeddirn ff7f set askspeedix -1 set askspeeds {1 30 50 80 100 126} #set speeddirn ffff80c3fbcced7f #set speeddirn_fixed {speed126 2 80 0} set speeddirn_fixed {} set funcs ff7f # unset pointpos($point) # unset segdetect($seg) ;# unset: shown D0; {}: shown D1; or: after id, D1->0 set funcsr1 {0x061 0x020 0x000 0x040 0x060} set funcsl1 {0x182 0x080 0x000 0x100 0x180} set funcsr2 {0x021 0x020 0x000} set funcsl2 {0x042 0x040 0x000} set funcsr4 {0x020 0x000} set funcsl4 {0x040 0x000} set funcsval 0x000 proc gui {m} { puts "GUI $m" } proc gui_init {} { global watchdog polarity segdetect gui "M A2 0" gui "M A5 0 J" gui "M A6 0 J" gui "EOE" if {[info exists watchdog]} { gui "P 1" } gui_polarity foreach seg [array names segdetect] { gui "D1 $seg" } } proc debug {m} { puts $m } proc tellpic_q {m} { global p testonly if {$testonly} return set b [binary format H* $m] puts -nonewline $p $b } proc tellpic {m} { puts ">> $m" tellpic_q $m } proc bgerror {m} { if {[catch { global errorInfo errorCode puts stderr "$m\n$errorCode\n$errorInfo" fail "bgerror $m" } emsg]} { exit 127 } } proc fail_now {} { global p debug "failing now" fconfigure $p -blocking yes gui "P 0" tellpic 10 exit 1 } proc fail {m} { global watchdog p catch { after cancel $watchdog; unset watchdog } puts "failing $m" tellpic 9801 ;# 16ms after 1000 fail_now fileevent $p readable {} } proc gui_polarity {} { global pname set 1 {} switch -exact $pname { l { lappend 0 X1 X3 X5 X7 X9; lappend 0 X2 X4 X6 X8 X10 } x { lappend 1 X1 X3 X5 X7 X9; lappend 0 X2 X4 X6 X8 X10 } y { lappend 0 X1 X3 X5 X7 X9; lappend 1 X2 X4 X6 X8 X10 } } foreach v {0 1} { foreach seg [set $v] { gui "R $v $seg" } } } proc polarity {newpname} { global pname polmsg debug "polarising $newpname" if {![string compare $pname $newpname]} return tellpic $polmsg($newpname) set pname $newpname gui_polarity } proc pt_now {how point pos xtra} { set msg a0[lindex $point $pos] debug "$how point $point pos=$pos msg=$msg$xtra" gui "M [lindex $point 2] [expr {!$pos}]" tellpic $msg } proc pt_must {point newpos} { upvar #0 pointpos($point) pos global pq if {[info exists pos] && $pos == $newpos} return set pos $newpos if {[info exists pq]} { lappend pq [list $point $pos] debug "queue point $point pos=$pos l=[llength $pq]" return } pt_now immed $point $pos {} set pq {} } proc pt_ifthenmust {ifpoint ifposwant thenpoint thenpos} { upvar #0 pointpos($ifpoint) ifpos if {![info exists ifpos] || $ifpos != $ifposwant} return pt_must $thenpoint $thenpos } proc badwatchdog {} { global pq puts "watchdog - oh well" if {![info exists pq]} { set pq {} } } proc pm_charged {} { global pq if {[llength $pq]} { set v [lindex $pq 0] set pq [lrange $pq 1 end] pt_now nowdo [lindex $v 0] [lindex $v 1] " l=[llength $pq]" } else { debug "cdu-charged" unset pq } } proc randbyte {} { global rand set c [read $rand 1]; if {![string length $c]} { error "eof on rand" } binary scan $c H* x return $x } proc pt_maybe {point oneisright} { upvar #0 pointpos($point) oldpos global lastptchosen pointprob pointabs if {![string compare $point $lastptchosen]} return set lastptchosen $point set x 0x[randbyte] set pos [expr {$x < $pointprob ? 1 : 0}] if {$pointabs} { debug "chose point $point $pos (abs x=$x prob=$pointprob)" set pos [expr {!$pos}] } elseif {[info exists oldpos] && !$oldpos} { debug "chose point $point $pos (0-> x=$x prob=$pointprob)" } else { set pos [expr {!$pos}] debug "chose point $point $pos (1-> x=$x prob=$pointprob)" } pt_must $point $pos } proc s0 {v seg} { upvar #0 segdetect($seg) segd if {![info exists segd]} { debug "segment $seg = already" } elseif {[string length $segd]} { debug "segment $seg = pending already" } else { debug "segment $seg = soon" set segd [after 100 s0t $seg] } } proc s0t {seg} { upvar #0 segdetect($seg) segd debug "segment $seg = now" unset segd gui "D0 $seg" } proc s1 {v seg} { upvar #0 segdetect($seg) segd if {![info exists segd]} { pm_detect $v debug "segment $seg ! (overwrites =)" } elseif {[string length $segd]} { debug "segment $seg ! (cancels =)" after cancel $segd } else { debug "segment $seg ! already" return } gui "D1 $seg" set segd {} } proc pm_maydetect {d seg} { switch -exact $seg { 06 { s$d $seg X10 } 09 { s$d $seg X8 } 0a { s$d $seg X6 } 04 { s$d $seg X5 } 02 { s$d $seg X7 } 07 { s$d $seg X9 } 14 { s$d $seg A5 } 20 { s$d $seg A6 } 1a { s$d $seg A4 } 10 { s$d $seg A2 } 03 { s$d $seg X1 } 05 { s$d $seg X3 } 16 { s$d $seg A3 } 1c { s$d $seg A1 } 08 { s$d $seg X2 } 0b { s$d $seg X4 } } } #proc pm_nodetect {seg} { # global segsasgot # if {![string compare $seg [lindex $segsasgot 1]]} { # set segsasgot [list [lindex $segsasgot 1] [lindex $segsasgot 0]] # } #} proc pm_detect {seg} { global segs pname segsasgot if {[string compare $seg [lindex $segsasgot 1]]} { set segsasgot [list [lindex $segsasgot 1] $seg] } if {[lsearch -exact $segs $seg] < 0} { set segs $segsasgot } debug "pm_detect $seg ($segsasgot) ($segs) $pname$seg" # if {[lsearch -exact { # 06 09 0a 04 02 07 14 20 # 0b 08 1c 16 # 1a 10 03 05 # } $seg] < 0} return switch -exact $pname$seg { l16 - l1c - l08 - l0b { polarity y } l10 - l1a - l03 - l05 { polarity x } x07 - x04 - x0a { polarity l } x16 - x1c - x14 - x0b { polarity y } y06 - y04 - y0a { polarity l } y20 - y10 - y1a - y05 { polarity x } } switch -exact $seg { 04 - 0a { pt_must "00 01 X7" 1; pt_must "40 41 X8" 1 } 05 { pt_must "00 01 X7" 0 } 0b { pt_must "40 41 X8" 0 } 16 - 1c { pt_must "02 03 A5" 0 } 1a - 10 { pt_must "42 43 A6" 0 } 14 { pt_ifthenmust "02 03 A5" 1 "42 43 A6" 1 } 20 { pt_ifthenmust "42 43 A6" 1 "02 03 A5" 1 } } switch -exact [join $segs -] { 02-07 { pt_maybe "02 03 A5" 1 } 07-02 { pt_maybe "00 01 X7" 0 } 09-06 { pt_maybe "42 43 A6" 0 } 06-09 { pt_maybe "40 41 X8" 1 } } } proc tellnmra {m} { # global nmrawhich speeddirn funcs # set m 0x$m # for {set i 0} {$i < $m} {incr i} { # tellpic_q [lindex [list $speeddirn $funcs] $nmrawhich] # set nmrawhich [expr {!$nmrawhich}] # } } proc watchdog {} { global watchdog testonly speeddirn funcs nmradiv catch { after cancel $watchdog } set watchdog [after 50 watchdog] tellpic_q 9808 ;# 128ms if {[incr nmradiv] > 35} { tellpic_q $speeddirn$funcs set nmradiv 0 } } proc pm_hello {} { debug "got hello, starting up" tellpic 11 gui "P 1" watchdog changewhat tellnmra 01 } proc fp {m} { debug "<< $m" } proc frompic {m} { set v [lindex $m 1] switch -glob [lindex $m 0] { 01 - 02 { tellnmra $m } 09 { fp $m; pm_hello } 07 { puts "short circuit"; exit 1 } 0d { fp $m; badwatchdog } 28 { fp $m; pm_charged } 9[0-7] { fp $m; pm_maydetect 0 $v } 9? { fp $m; pm_maydetect 1 $v } 0a - [234567]? { puts "pic debug $m" } * { fp $m; fail "pic unknown $m" } } } proc onreadp_test {} { if {![gets stdin m]} { return } frompic $m } proc onreadp {} { global p m rand while 1 { set c [read $p 1] if {![string length $c]} { if {[eof $p]} { error "eof on device" } return } binary scan $c H* x if {![info exists rand]} { fp ...$x return } lappend m $x if {[regexp {^[0-7]} $x]} { if {![regexp {^x} $m]} { frompic $m } set m {} } } } proc newspeeddirn {} { global loco askspeedix set maxspeed [expr {$askspeedix == -1 ? 126.0 : 50.0}] set minspeed 26.0 set b1 0x[randbyte] set speed [expr { round(($b1 * $b1) / 65535.0 * ($maxspeed - $minspeed) + $minspeed) }] set b2 0x[randbyte] set dirn [expr {$b2 / 128}] set dirn 0 debug "speeddirn b1=$b1 speed=$speed b2=$b2 dirn=$dirn" return "speed126 $loco $speed $dirn" } proc funcs_removebits {lr headent} { global funcsval set funcsval [format 0x%x [expr {$funcsval & ~$headent}]] } proc funcs_addbits {lr list} { global loco funcsval set headent [lindex $list 0] set val $funcsval set add $headent if {$add & 0x02} { set rand 0x[randbyte]0 set add [expr {$add & $rand}] set val [expr {$val | $add}] debug "funcs $lr v=$funcsval add=$add new=$val rand=$rand ($list)" } else { set val [expr {$val | $add}] debug "funcs $lr v=$funcsval add=$add new=$val ($list)" } set funcsval $val } proc funcsnmralist {} { global loco funcsval return "funcs5to8 $loco $funcsval" } proc newfuncs {} { global loco funcsval foreach lr {l r} { upvar #0 funcs${lr}${loco} list set now [lindex $list 0] funcs_removebits $lr $now funcs_addbits $lr $list } return [funcsnmralist] } proc nmrachange {thing argstring} { global $thing set bin [eval exec ./adhoc-test -s/dev/stdout $argstring] binary scan $bin H* x debug "changed $thing=$x ($argstring)" set $thing ff$x } proc maybechange {thing force} { global $thing ch upvar #0 ${thing}_fixed fixed if {![info exists fixed]} { if {$force} { debug "maybechange $thing forced ..." } else { set rb 0x[randbyte][randbyte] if { $rb / 65536.0 > 1.0 / (($ch(${thing}every) - $ch(minint)*0.001) * $ch(scale)) } { debug "maybechange $thing rb=$rb no" return 0 } debug "maybechange $thing rb=$rb yes ..." } set l [new$thing] } else { debug "fixed $thing $fixed" set l $fixed if {![llength $l]} { return 0 } } nmrachange $thing $l return 1 } proc changewhat {} { global ch chwa catch { after cancel $chwa } if {[maybechange speeddirn 0] + [maybechange funcs 0]} { set interval $ch(minint) } else { set interval 1000 } set chwa [after $interval changewhat] } proc onreadcmd {} { if {[gets stdin l] < 0} { if {[eof stdin]} { puts "GUI exit 0" fail "stopgap-controller got eof, quitting" fileevent stdin readable {} } return } eval $l } proc setup {} { global port p testonly stateshowpipe fconfigure stdout -buffering none if {!$testonly} { set p [open $port {RDWR NONBLOCK} 0] exec stty -F $port min 1 time 0 -istrip -ocrnl -onlcr -onocr -opost \ -ctlecho -echo -echoe -echok -echonl -iexten -isig \ -icanon -icrnl \ 9600 clocal cread crtscts -hup -parenb cs8 -cstopb \ -ixoff bs0 cr0 ff0 nl0 -ofill -olcuc fconfigure $p -encoding binary -translation binary \ -blocking false -buffering none fileevent $p readable onreadp fconfigure stdin -blocking false fileevent stdin readable onreadcmd set stateshowpipe [open /tmp/train-state w] fconfigure $stateshowpipe -buffering none } else { set p stdin fconfigure stdin -blocking false fileevent stdin readable onreadp_test set stateshowpipe [open /dev/null w] } after 250 setup_complete } proc setup_complete {} { global rand exec xset s off set rand [open /dev/urandom {RDONLY} 0] fconfigure $rand -encoding binary -translation binary tellpic 0a } #---------- # for keyboard control proc updownfromlist {wholelistv ixv updown} { upvar #0 $wholelistv wholelist upvar #0 $ixv ix set ll [llength $wholelist] if {![info exists ix]} { set old ? set ix [expr { int($ll * 0.5 - 0.5 + 0.5 * $updown) }] } else { set old $ix incr ix $updown if {$ix < 0} { set ix 0 } if {$ix >= $ll} { set ix [expr {$ll - 1}] } } set val [lindex $wholelist $ix] debug "updownfromlist ix:$old->$ix /$ll $val ($wholelist)" return $val } proc ask_speed {updown} { global speeddirn_fixed loco askspeedix if {$askspeedix < 0} { unset askspeedix } set speed [updownfromlist askspeeds askspeedix $updown] set speeddirn_fixed [list speed126 $loco $speed 0] maybechange speeddirn 1 } proc ask_randspeed {} { global speeddirn_fixed askspeedix set askspeedix [expr {$askspeedix == -1 ? -2 : -1}] catch { unset speeddirn_fixed } maybechange speeddirn 1 } proc ask_loco {} { global loco set loco [updownfromlist locos locoix 1] } proc ask_funcs {lr} { global loco upvar #0 funcs${lr}${loco} list set now [lindex $list 0] funcs_removebits $lr $now set list [concat [lrange $list 1 end] $now] funcs_addbits $lr $list nmrachange funcs [funcsnmralist] } proc ask_pointprob {updown} { global pointprob set pointprob [updownfromlist pointprobs pointprobix $updown] } proc ask_pointrelabs {} { global pointabs set pointabs [expr {!$pointabs}] } proc ask_show {} { global loco stateshowpipe pointprob pointabs askspeedix upvar #0 funcsr$loco fr upvar #0 funcsl$loco fl puts -nonewline $stateshowpipe [format \ "\nL$loco P%03x%s F%03x S%s" \ $pointprob [lindex {R A} $pointabs] \ [expr {[lindex $fr 0] | [lindex $fl 0]}] \ $askspeedix] } setup gui_init ask_show vwait end