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