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