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