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