chiark / gitweb /
stopgap controller program
[trains.git] / hostside / stopgap-controller
1 #!/usr/bin/tclsh8.4
2
3 set testonly 1
4 set port /dev/ttya0
5 # unset always
6
7 set m xx
8 set segs xx
9 set polarity 908000
10 set pq {} ;# unset: cdu charged and waiting
11 # unset pointpos($point)
12 # unset segdetect($seg) ;# unset: shown D0; {}: shown D1; or: after id, D1->0
13
14 proc gui {m} {
15     puts "GUI $m"
16 }
17
18 proc gui_init {} {
19     global watchdog polarity segdetect
20     gui "M A2 0"
21     gui "M A5 0 J"
22     gui "M A6 0 J"
23     if {[info exists watchdog]} { gui "P 1" }
24     if {![regexp {^90} $polarity]} { gui_polarity }
25     foreach seg [array names segdetect] {
26         gui "D1 $seg"
27     }
28 }
29
30 proc debug {m} {
31     puts $m
32 }
33
34 proc tellpic {m} {
35     global p testonly
36     puts ">> $m"
37     if {$testonly} return
38     set b [binary format H* $m]
39     puts -nonewline $p $b
40 }
41
42 proc bgerror {m} {
43     if {[catch {
44         global errorInfo errorCode
45         puts stderr "$m\n$errorCode\n$errorInfo"
46         fail "bgerror $m"
47     } emsg]} {
48         exit 127
49     }
50 }
51
52 proc fail_now {} {
53     global p
54     debug "failing now"
55     fconfigure $p -blocking yes
56     gui "P 0"
57     tellpic 20
58     exit 1
59 }
60
61 proc fail {m} {
62     global watchdog p
63     catch { after cancel $watchdog; unset watchdog }
64     puts "failing $m"
65     tellpic a001 ;# 16ms
66     after 2000 fail_now
67     fileevent $p readable {}
68 }
69
70 proc gui_polarity {} {
71     foreach seg {
72         X8
73         X9
74         X10
75         X1
76         X2
77         X3
78         X4
79         X5
80         X6
81         X7
82     } {
83         gui "R $seg"
84     }
85 }
86
87 proc polarity {m} {
88     global polarity
89     debug "polarising $m"
90     tellpic $m
91     if {[string compare $m $polarity]} {
92         gui_polarity
93     }
94     set polarity $m
95 }
96 proc polarity_l {} { polarity 908000 }
97 proc polarity_x {} { polarity 97ff7f }
98
99 proc pt_now {how point pos xtra} {
100     set msg a0[lindex $point $pos]
101     debug "$how point $point pos=$pos msg=$msg$xtra"
102     gui "M [lindex $point 2] [expr {!$pos}]"
103     tellpic $msg
104 }
105 proc pt_must {point newpos} {
106     upvar #0 pointpos($point) pos
107     global pq
108     if {[info exists pos] && $pos == $newpos} return
109     set pos $newpos
110     if {[info exists pq]} {
111         lappend pq [list $point $pos]
112         debug "queue point $point pos=$pos l=[llength $pq]"
113         return
114     }
115     pt_now immed $point $pos {}
116     set pq {}
117 }
118
119 proc pm_charged {} {
120     global pq
121     if {[llength $pq]} {
122         set v [lindex $pq 0]
123         set pq [lrange $pq 1 end]
124         pt_now nowdo [lindex $v 0] [lindex $v 1] " l=[llength $pq]"
125     } else {
126         debug "cdu-charged"
127         unset pq
128     }
129 }
130
131 proc pt_maybe {point} {
132     global always rand
133     if {[info exists always]} {
134         set pos $always
135     } else {
136         set c [read $rand 1]; if {![string length $c]} { error "eof on rand" }
137         binary scan $c H* x
138         set pos [expr [regexp {^[89a-f]} $x] ? 1 : 0]
139         debug "chose point $point pos=$pos (x=$x)"
140     }
141     pt_must $point $pos
142 }
143
144 proc s0 {seg} {
145     upvar #0 segdetect($seg) segd
146     if {![info exists segd]} {
147         debug "segment $seg = already"
148     } elseif {[string length $segd]} {
149         debug "segment $seg = pending already"
150     } else {
151         debug "segment $seg = soon"
152         set segd [after 100 s0t $seg]
153     }
154 }
155 proc s0t {seg} {
156     upvar #0 segdetect($seg) segd
157     debug "segment $seg = now"
158     unset segd
159     gui "D0 $seg"
160 }
161 proc s1 {seg} {
162     upvar #0 segdetect($seg) segd
163     if {![info exists segd]} {
164         debug "segment $seg ! (overwrites =)"
165     } elseif {[string length $segd]} {
166         debug "segment $seg ! (cancels =)"
167         after cancel $segd
168     } else {
169         debug "segment $seg ! already"
170         return
171     }
172     gui "D1 $seg"
173     set segd {}
174 }       
175
176 proc pm_maydetect {d seg} {
177     switch -exact $seg {
178         06 { s$d X10 }
179         09 { s$d X8 }
180         0a { s$d X6 }
181         04 { s$d X5 }
182         02 { s$d X7 }
183         07 { s$d X9 }
184         14 { s$d A5 }
185         20 { s$d A6 }
186         1a { s$d A4 }
187         10 { s$d A2 }
188         03 { s$d X1 }
189         05 { s$d X3 }
190         16 { s$d A3 }
191         1c { s$d A1 }
192         08 { s$d X2 }
193         0b { s$d X4 }
194     }
195 }
196
197 proc pm_detect {seg} {
198     global segs
199     switch -exact $seg {
200         07 - 06 { polarity_l }
201         16 - 1c - 1a - 10 - 03 - 05 - 08 - 0b { polarity_x }
202     }
203     switch -exact $seg {
204         14 - 20 { pt_must "02 03 A5" 1; pt_must "42 43 A6" 1 }
205         04 - 0a { pt_must "00 01 X7" 1; pt_must "40 41 X8" 1 }
206         03 - 05 { pt_must "00 01 X7" 0 }
207         08 - 0b { pt_must "40 41 X8" 0 }
208         16 - 1c { pt_must "02 03 A5" 0 }
209         1a - 10 { pt_must "42 43 A6" 0 }
210     }
211     if {[lsearch -exact $segs $seg] < 0} {
212         set segs [list [lindex $segs end] $seg]
213     }
214     switch -exact [join $segs -] {
215         07-02 { pt_maybe "00 01 X7" }
216         02-07 { pt_maybe "02 03 A5" }
217         06-09 { pt_maybe "40 41 X8" }
218         09-06 { pt_maybe "42 43 A6" }
219     }
220 }
221
222 proc watchdog {} {
223     global watchdog testonly
224     if {$testonly} return
225     catch { after cancel $watchdog }
226     set watchdog [after 50 watchdog]
227     tellpic 9808 ;# 128ms
228 }
229
230 proc pm_hello {} {
231     debug "got hello, starting up"
232     tellpic 21
233     gui "P 1"
234     watchdog
235 }
236
237 proc frompic {m} {
238     debug "<< $m"
239     switch -glob [lindex $m 0] {
240         09 { pm_hello }
241         28 { pm_charged }
242         9[0-7] { pm_maydetect 0 [lindex $m 1] }
243         9? { pm_detect [lindex $m 1]; pm_maydetect 1 [lindex $m 1] }
244         0a - [234567]? { puts "pic debug $m" }
245         * { fail "pic unknown $m" }
246     }
247 }
248
249 proc onreadp_test {} {
250     if {![gets stdin m]} { return }
251     frompic $m
252 }
253
254 proc onreadp {} {
255     global p m
256     while 1 {
257         set c [read $p 1]
258         if {![string llength $c]} {
259             if {[eof $p]} { error "eof on device" }
260             return
261         }
262         binary scan $c H* x
263         lappend m $x
264         if {[regexp {^[89a-f]} $x]} {
265             if {![regexp {^x} $m]} {
266                 frompic $m
267             }
268             set m {}
269         }
270     }
271 }
272
273 proc setup {} {
274     global port p rand testonly
275     if {!$testonly} {
276         set p [open $port {RDWR NONBLOCK} 0]
277     
278         exec stty -F $port min 1 time 0 -istrip -ocrnl -onlcr -onocr -opost \
279                        -ctlecho -echo -echoe -echok -echonl -iexten -isig \
280                        -icanon -icrnl \
281             9600 clocal cread crtscts -hup -parenb cs8 -cstopb \
282             -ixoff bs0 cr0 ff0 nl0 -ofill -olcuc
283
284         fconfigure $p -encoding binary -translation binary \
285                 -blocking false -buffering none
286
287         fileevent $p readable onreadp
288     } else {
289         set p stdin
290         fconfigure stdin -blocking false
291         fileevent stdin readable onreadp_test
292     }
293
294     set rand [open /dev/urandom {RDONLY} 0]
295     fconfigure $rand -encoding binary -translation binary
296 }
297
298 setup
299 gui_init
300 vwait end