chiark / gitweb /
fixes from testing session
[trains.git] / hostside / stopgap-controller
1 #!/usr/bin/tclsh8.2
2 # used like this:
3 # liberator:hostside> ssh bessar 'cd things/trains-bessar/hostside && ./stopgap-controller' | ./gui-displayer -
4
5 set testonly 0
6 #set testonly 1
7 set port /dev/ttya0
8 #set port /dev/ttyS0
9
10 set ch(funcsevery) 10
11 set ch(speeddirnevery) 30
12 set ch(scale) 1
13
14 set ch(minint) 5000
15
16 #unset pointasked
17 # 0 1 (settings) M0 M1 (manual, settings) unset (random) M (manual)
18
19 set nmrawhich 0
20 set lastptchosen xx
21
22 set polmsg(l) 908000
23 set polmsg(x) 90f802
24 set polmsg(y) 90807c
25 set pname l
26 set m {}
27 set nmradiv 0
28 set segs {xx yy}
29 set segsasgot {xx yy}
30 set pq {} ;# unset: cdu charged and waiting
31 set speeddirn ff7f
32 #set speeddirn ffff80c3fbcced7f
33 #set speeddirn_fixed {speed126 2 80 0}
34 set speeddirn_fixed {}
35 set funcs ff7f
36 # unset pointpos($point)
37 # unset segdetect($seg) ;# unset: shown D0; {}: shown D1; or: after id, D1->0
38
39 proc gui {m} {
40     puts "GUI $m"
41 }
42
43 proc gui_init {} {
44     global watchdog polarity segdetect
45     gui "M A2 0"
46     gui "M A5 0 J"
47     gui "M A6 0 J"
48     gui "EOE"
49     if {[info exists watchdog]} { gui "P 1" }
50     gui_polarity
51     foreach seg [array names segdetect] {
52         gui "D1 $seg"
53     }
54 }
55
56 proc debug {m} {
57     puts $m
58 }
59
60 proc tellpic_q {m} {
61     global p testonly
62     if {$testonly} return
63     set b [binary format H* $m]
64     puts -nonewline $p $b
65 }
66
67 proc tellpic {m} {
68     puts ">> $m"
69     tellpic_q $m
70 }
71
72 proc bgerror {m} {
73     if {[catch {
74         global errorInfo errorCode
75         puts stderr "$m\n$errorCode\n$errorInfo"
76         fail "bgerror $m"
77     } emsg]} {
78         exit 127
79     }
80 }
81
82 proc fail_now {} {
83     global p
84     debug "failing now"
85     fconfigure $p -blocking yes
86     gui "P 0"
87     tellpic 10
88     exit 1
89 }
90
91 proc fail {m} {
92     global watchdog p
93     catch { after cancel $watchdog; unset watchdog }
94     puts "failing $m"
95     tellpic 9801 ;# 16ms
96     after 1000 fail_now
97     fileevent $p readable {}
98 }
99
100 proc gui_polarity {} {
101     global pname
102     set 1 {}
103     switch -exact $pname {
104         l { lappend 0 X1 X3 X5 X7 X9; lappend 0 X2 X4 X6 X8 X10 }
105         x { lappend 1 X1 X3 X5 X7 X9; lappend 0 X2 X4 X6 X8 X10 }
106         y { lappend 0 X1 X3 X5 X7 X9; lappend 1 X2 X4 X6 X8 X10 }
107     }
108     foreach v {0 1} {
109         foreach seg [set $v] {
110             gui "R $v $seg"
111         }
112     }
113 }
114
115 proc polarity {newpname} {
116     global pname polmsg
117     debug "polarising $newpname"
118     if {![string compare $pname $newpname]} return
119     tellpic $polmsg($newpname)
120     set pname $newpname
121     gui_polarity
122 }
123
124 proc pt_now {how point pos xtra} {
125     set msg a0[lindex $point $pos]
126     debug "$how point $point pos=$pos msg=$msg$xtra"
127     gui "M [lindex $point 2] [expr {!$pos}]"
128     tellpic $msg
129 }
130 proc pt_must {point newpos} {
131     upvar #0 pointpos($point) pos
132     global pq
133     if {[info exists pos] && $pos == $newpos} return
134     set pos $newpos
135     if {[info exists pq]} {
136         lappend pq [list $point $pos]
137         debug "queue point $point pos=$pos l=[llength $pq]"
138         return
139     }
140     pt_now immed $point $pos {}
141     set pq {}
142 }
143 proc pt_ifthenmust {ifpoint ifposwant thenpoint thenpos} {
144     upvar #0 pointpos($ifpoint) ifpos
145     if {![info exists ifpos] || $ifpos != $ifposwant} return
146     pt_must $thenpoint $thenpos
147 }
148
149 proc badwatchdog {} {
150     global pq
151     puts "watchdog - oh well"
152     if {![info exists pq]} { set pq {} }
153 }
154
155 proc pm_charged {} {
156     global pq
157     if {[llength $pq]} {
158         set v [lindex $pq 0]
159         set pq [lrange $pq 1 end]
160         pt_now nowdo [lindex $v 0] [lindex $v 1] " l=[llength $pq]"
161     } else {
162         debug "cdu-charged"
163         unset pq
164     }
165 }
166
167 proc randbyte {} {
168     global rand
169     set c [read $rand 1]; if {![string length $c]} { error "eof on rand" }
170     binary scan $c H* x
171     return $x
172 }
173
174 proc pt_maybe {point oneisright} {
175     global pointasked lastptchosen
176     if {![info exists pointasked]} {
177         if {![string compare $point $lastptchosen]} return
178         set lastptchosen $point
179         set x [randbyte]
180         set pos [expr [regexp {^[89a-f]} $x] ? 1 : 0]
181         debug "chose point $point pos=$pos (x=$x)"
182     } elseif {[regexp {^M([01])$} $pointasked dummy pos]} {
183         if {[lsearch -exact {40 02} [lindex $point 0]] >= 0} {
184             set pos [expr {!$pos}]
185             debug "chose point $point pos=$pos manual-rl"
186         } else {
187             debug "chose point $point pos=$pos manual-lr"
188         }
189         set pointasked M
190     } elseif {![string compare $pointasked M]} {
191         debug "leave point $point manual"
192         return
193     } else {
194         set pos $pointasked
195         debug "fixed point $point pos=$pos"
196     }
197     pt_must $point $pos
198 }
199
200 proc s0 {v seg} {
201     upvar #0 segdetect($seg) segd
202     if {![info exists segd]} {
203         debug "segment $seg = already"
204     } elseif {[string length $segd]} {
205         debug "segment $seg = pending already"
206     } else {
207         debug "segment $seg = soon"
208         set segd [after 100 s0t $seg]
209     }
210 }
211 proc s0t {seg} {
212     upvar #0 segdetect($seg) segd
213     debug "segment $seg = now"
214     unset segd
215     gui "D0 $seg"
216 }
217 proc s1 {v seg} {
218     upvar #0 segdetect($seg) segd
219     if {![info exists segd]} {
220         pm_detect $v
221         debug "segment $seg ! (overwrites =)"
222     } elseif {[string length $segd]} {
223         debug "segment $seg ! (cancels =)"
224         after cancel $segd
225     } else {
226         debug "segment $seg ! already"
227         return
228     }
229     gui "D1 $seg"
230     set segd {}
231 }       
232
233 proc pm_maydetect {d seg} {
234     switch -exact $seg {
235         06 { s$d $seg X10 }
236         09 { s$d $seg X8 }
237         0a { s$d $seg X6 }
238         04 { s$d $seg X5 }
239         02 { s$d $seg X7 }
240         07 { s$d $seg X9 }
241         14 { s$d $seg A5 }
242         20 { s$d $seg A6 }
243         1a { s$d $seg A4 }
244         10 { s$d $seg A2 }
245         03 { s$d $seg X1 }
246         05 { s$d $seg X3 }
247         16 { s$d $seg A3 }
248         1c { s$d $seg A1 }
249         08 { s$d $seg X2 }
250         0b { s$d $seg X4 }
251     }
252 }
253
254 #proc pm_nodetect {seg} {
255 #    global segsasgot
256 #    if {![string compare $seg [lindex $segsasgot 1]]} {
257 #       set segsasgot [list [lindex $segsasgot 1] [lindex $segsasgot 0]]
258 #    }
259 #}
260
261 proc pm_detect {seg} {
262     global segs pname segsasgot
263     if {[string compare $seg [lindex $segsasgot 1]]} {
264         set segsasgot [list [lindex $segsasgot 1] $seg]
265     }
266     if {[lsearch -exact $segs $seg] < 0} {
267         set segs $segsasgot
268     }
269     debug "pm_detect $seg ($segsasgot) ($segs) $pname$seg"
270 #    if {[lsearch -exact {
271 #       06 09 0a 04 02 07 14 20
272 #       0b 08 1c 16
273 #       1a 10 03 05
274 #    } $seg] < 0} return
275     switch -exact $pname$seg {
276         l16 - l1c - l08 - l0b { polarity y }
277         l10 - l1a - l03 - l05 { polarity x }
278         x07 - x04 - x0a { polarity l }
279         x16 - x1c - x14 - x0b { polarity y }
280         y06 - y04 - y0a { polarity l }
281         y20 - y10 - y1a - y05 { polarity x }
282     }
283     switch -exact $seg {
284         04 - 0a { pt_must "00 01 X7" 1; pt_must "40 41 X8" 1 }
285         05 { pt_must "00 01 X7" 0 }
286         0b { pt_must "40 41 X8" 0 }
287         16 - 1c { pt_must "02 03 A5" 0 }
288         1a - 10 { pt_must "42 43 A6" 0 }
289         14 { pt_ifthenmust "02 03 A5" 1 "42 43 A6" 1 }
290         20 { pt_ifthenmust "42 43 A6" 1 "02 03 A5" 1 }
291     }
292     switch -exact [join $segs -] {
293         02-07 { pt_maybe "02 03 A5" 1 }
294         07-02 { pt_maybe "00 01 X7" 0 }
295         09-06 { pt_maybe "42 43 A6" 0 }
296         06-09 { pt_maybe "40 41 X8" 1 }
297     }
298 }
299
300 proc tellnmra {m} {
301 #    global nmrawhich speeddirn funcs
302 #    set m 0x$m
303 #    for {set i 0} {$i < $m} {incr i} {
304 #       tellpic_q [lindex [list $speeddirn $funcs] $nmrawhich]
305 #       set nmrawhich [expr {!$nmrawhich}]
306 #    }
307 }
308
309 proc watchdog {} {
310     global watchdog testonly speeddirn funcs nmradiv
311     catch { after cancel $watchdog }
312     set watchdog [after 50 watchdog]
313     tellpic_q 9808 ;# 128ms
314     if {[incr nmradiv] > 35} {
315         tellpic_q $speeddirn$funcs
316         set nmradiv 0
317     }
318 }
319
320 proc pm_hello {} {
321     debug "got hello, starting up"
322     tellpic 11
323     gui "P 1"
324     watchdog
325     changewhat
326     tellnmra 01
327 }
328
329 proc fp {m} {
330     debug "<< $m"
331 }
332
333 proc frompic {m} {
334     set v [lindex $m 1]
335     switch -glob [lindex $m 0] {
336         01 - 02 { tellnmra $m }
337         09 { fp $m; pm_hello }
338         07 { puts "short circuit"; exit 1 }
339         0d { fp $m; badwatchdog }
340         28 { fp $m; pm_charged }
341         9[0-7] { fp $m; pm_maydetect 0 $v }
342         9? { fp $m; pm_maydetect 1 $v }
343         0a - [234567]? { puts "pic debug $m" }
344         * { fp $m; fail "pic unknown $m" }
345     }
346 }
347
348 proc onreadp_test {} {
349     if {![gets stdin m]} { return }
350     frompic $m
351 }
352
353 proc onreadp {} {
354     global p m rand
355     while 1 {
356         set c [read $p 1]
357         if {![string length $c]} {
358             if {[eof $p]} { error "eof on device" }
359             return
360         }
361         binary scan $c H* x
362         if {![info exists rand]} {
363             fp ...$x
364             return
365         }
366         lappend m $x
367         if {[regexp {^[0-7]} $x]} {
368             if {![regexp {^x} $m]} {
369                 frompic $m
370             }
371             set m {}
372         }
373     }
374 }
375
376 proc newspeeddirn {} {
377     set b1 0x[randbyte]
378     set speed [expr {round(($b1 * $b1) / 65535.0 * 100.0 + 26.0)}]
379     set b2 0x[randbyte]
380     set dirn [expr {$b2 / 128}]
381     set dirn 0
382     debug "speeddirn b1=$b1 speed=$speed b2=$b2 dirn=$dirn"
383     return "speed126 2 $speed $dirn"
384 }
385
386 proc newfuncs {} {
387     set b3 0x[randbyte]
388     set value [expr {($b3 & 127) * 16}]
389     debug "funcs b3=$b3 value=[format %x $value]"
390     return "funcs5to8 2 $value"
391 }
392
393 proc maybechange {thing force} {
394     global $thing ch
395     upvar #0 ${thing}_fixed fixed
396     if {![info exists fixed]} {
397         if {$force} {
398             debug "maybechange $thing forced ..."
399         } else {
400             set rb 0x[randbyte][randbyte]
401             if {
402                 $rb / 65536.0 >
403                 1.0 / (($ch(${thing}every) - $ch(minint)*0.001) * $ch(scale))
404             } {
405                 debug "maybechange $thing rb=$rb no"
406                 return 0
407             }
408             debug "maybechange $thing rb=$rb yes ..."
409         }
410         set l [new$thing]
411     } else {
412         debug "fixed $thing $fixed"
413         set l $fixed
414         if {![llength $l]} { return 0 }
415     }
416     set bin [eval exec ./hostside-old -s/dev/stdout $l]
417     binary scan $bin H* x
418     debug "changed $thing=$x"
419     set $thing ff$x
420     return 1
421 }
422
423 proc changewhat {} {
424     global ch chwa
425     catch { after cancel $chwa }
426     if {[maybechange speeddirn 0] || [maybechange funcs 0]} {
427         set interval $ch(minint)
428     } else {
429         set interval 1000
430     }
431     set chwa [after $interval changewhat]
432 }
433
434 proc onreadcmd {} {
435     if {[gets stdin l] < 0} {
436         if {[eof stdin]} {
437             puts "GUI exit 0"
438             fail "stopgap-controller got eof, quitting"
439             fileevent stdin readable {}
440         }
441         return
442     }
443     eval $l
444 }
445
446 proc setup {} {
447     global port p testonly
448     fconfigure stdout -buffering none
449     if {!$testonly} {
450         set p [open $port {RDWR NONBLOCK} 0]
451     
452         exec stty -F $port min 1 time 0 -istrip -ocrnl -onlcr -onocr -opost \
453                        -ctlecho -echo -echoe -echok -echonl -iexten -isig \
454                        -icanon -icrnl \
455             9600 clocal cread crtscts -hup -parenb cs8 -cstopb \
456             -ixoff bs0 cr0 ff0 nl0 -ofill -olcuc
457
458         fconfigure $p -encoding binary -translation binary \
459                 -blocking false -buffering none
460
461         fileevent $p readable onreadp
462         fconfigure stdin -blocking false
463         fileevent stdin readable onreadcmd
464     } else {
465         set p stdin
466         fconfigure stdin -blocking false
467         fileevent stdin readable onreadp_test
468     }
469
470     after 250 setup_complete
471 }
472
473 proc setup_complete {} {
474     global rand
475     set rand [open /dev/urandom {RDONLY} 0]
476     fconfigure $rand -encoding binary -translation binary
477     tellpic 0a
478 }
479
480
481 #----------
482 # for keyboard control
483
484 proc ask_speed_now {l} {
485     global speeddirn_fixed
486     set speeddirn_fixed $l
487     maybechange speeddirn 1
488 }
489
490 proc ask_fast {} { ask_speed_now {speed126 2 126 0} }
491 proc ask_slow {} { ask_speed_now {speed126 2 10 0} }
492 proc ask_randspeed {} {
493     global speeddirn_fixed; catch { unset speeddirn_fixed }
494     maybechange speeddirn 1
495 }    
496
497 proc ask_manual {rightp} { global pointasked; set pointasked M$rightp }
498 proc ask_figureeight {} { global pointasked; set pointasked 0 }
499 proc ask_loop {} { global pointasked; set pointasked 1 }
500 proc ask_randpath {} { global pointasked; catch { unset pointasked } }
501
502 setup
503 gui_init
504 vwait end