1 #*****************************************************************************
5 # Object Name : $RCSfile$
6 # Revision : $Revision$
9 # Created By : Robert Heller
10 # Created : Fri Jul 24 20:07:30 2015
11 # Last Modified : <150816.1126>
19 #*****************************************************************************
21 # Copyright (C) 2015 Robert Heller D/B/A Deepwoods Software
23 # Wendell, MA 01379-9728
25 # This program is free software; you can redistribute it and/or modify
26 # it under the terms of the GNU General Public License as published by
27 # the Free Software Foundation; either version 2 of the License, or
28 # (at your option) any later version.
30 # This program is distributed in the hope that it will be useful,
31 # but WITHOUT ANY WARRANTY; without even the implied warranty of
32 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 # GNU General Public License for more details.
35 # You should have received a copy of the GNU General Public License
36 # along with this program; if not, write to the Free Software
37 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
41 #*****************************************************************************
44 package require CmriSupport;# require the Cmri Support
package
45 package require snit;# require the SNIT OO framework
49 # @brief Switch (turnout) operation using a Chubb SMINI board and a Circuits4Tracks Quad OD for OS detection
51 # @image html switch-C4TSMINI-thumb.png
52 # @image latex switch-C4TSMINI.png "Switch controlled by a Chubb SMINI board with a Circuits4Tracks OS detection" width=5in
54 # Above is a typical switch (turnout) using an Chubb SMINI board to
55 # control a Circuitron Tortoise Switch Machine and to sense the point
56 # position and a Circuits4Track quad occupancy detector to sense
57 # occupation of the switch.
62 # # Connect to the cmribus through a USB RS485 adapter at /dev/ttyUSB0
63 # CmriSupport::CmriNode openport /dev/ttyUSB0
64 # # SMINI board at address 0
65 # CmriSupport::CmriNode SMINI0 -type SMINI -address 0
66 # # Switch 1 is controled by bits 0 and 1 of output port 0
67 # # Switch 1 points are sensed by bits 0 and 1 of input port 0
68 # # Switch 1 OS is detected on bit 0 of input port 1
69 # C4TSMINI_Switch switch1 -nodeobj SMINI0 -motorport 0 -motorbit 0 \
70 # -pointsenseport 0 -pointsensebit 0 \
71 # -plate SwitchPlate1 \
72 # -ossensorport 1 -osbit 0
73 # # Switch 2 is controled by bits 0 and 1 of output port 1
74 # # Switch 2 points are sensed by bits 2 and 3 of input port 0
75 # # Switch 2 OS is detected on bit 1 of input port 1
76 # C4TSMINI_Switch switch2 -nodeobj SMINI0 -motorport 1 -motorbit 0 \
77 # -pointsenseport 0 -pointsensebit 2 \
78 # -plate SwitchPlate2 \
79 # -ossensorport 1 -osbit 1
82 # For the track work elements use "switchN occupiedp" for the track work
83 # elements' occupied script and use "switchN pointstate" for the track
84 # work elements' state script. For the switch plate use
85 # "switchN motor normal" for the normal script and "switchN motor reverse"
86 # for the reverse script.
88 # Then in the Main Loop, you would have:
91 # MainWindow ctcpanel invoke Switch1
92 # MainWindow ctcpanel invoke Switch2
93 # MainWindow ctcpanel invoke SwitchPlate1
94 # MainWindow ctcpanel invoke SwitchPlate2
95 # update;# Update display
99 # @author Robert Heller \<heller\@deepsoft.com\>
101 # SMINI related options
103 option -nodeobj -readonly yes -
default {}
104 # Output port and bits for motor control
105 option -motorport -readonly yes -
default 0 -type {snit::integer -min 0}
106 option -motorbit -readonly yes -
default 0 -type {snit::integer -min 0 -max 6}
107 # Input port and bits for point sense
108 option -pointsenseport -readonly yes -
default 0 -type {snit::integer -min 0}
109 option -pointsensebit -readonly yes -
default 0 -type {snit::integer -min 0 -max 6}
110 # Input port and bit for OS sense
111 option -ossensorport -readonly yes -
default 0 -type {snit::integer -min 0}
112 option -osbit -readonly yes -
default 0 -type {snit::integer -min 0 -max 7}
114 # Signal related options
115 # The forward direction means entering at the point end.
116 option -direction -type {snit::enum -values {forward reverse}} \
117 -
default forward -configuremethod _settruedirection \
118 -cgetmethod _gettruedirection
119 # If the switch is installed opposite the overall traffic flow (eg it is
120 # a frog facing switch), then -forwarddirection needs to be set for
122 option -forwarddirection \
123 -type {snit::enum -values {forward reverse}} -
default forward \
125 # The forward signal is the signal protecting the points
126 option -forwardsignalobj -readonly yes -
default {}
127 # The previous block is the block connected to the points
128 option -previousblock -
default {}
129 # The reverse main signal is the signal protecting the straight frog end
130 option -reversemainsignalobj -readonly yes -
default {}
131 # The next main block is the block connected to the main frog end
132 option -nextmainblock -
default {}
133 # The reverse divergent signal is the signal protecting the divergent frog end
134 option -reversedivergentsignalobj -readonly yes -
default {}
135 # The next divergent block is the block connected to the divergent frog end
136 option -nextdivergentblock -
default {}
137 # Switch Plate name (if any).
138 option -plate -
default {}
141 ## @private SMINI node object
142 variable isoccupied no
143 ## @private Saved occupation state.
144 typevariable _motorbits -array {
148 ## @private Motor bit values
149 typevariable _pointsense -array {
155 ## @private Point sense bit values
157 typemethod validate {
object} {
158 ## Type validating code
159 # Raises an error if object is not either the empty string or a SR4_C4TSR4_Switch
161 # @param object Some object.
164 return $object;# Empty or
null objects are OK
165 } elseif {[
catch {$object info type} itstype]} {
166 error
"$object is not a $type";#
object is not a SNIT type
167 } elseif {$itstype eq $type} {
170 error
"$object is not a $type";#
object is something
else
175 ## @brief Constructor: initialize the switch object.
177 # Create a low level sensor object and install it as a component.
178 # Install the switch's signals, motor, and point sense objects.
180 # @param name Name of the switch object
181 # @param ... Options:
182 # @arg -nodeobj Cmri node object
183 # @arg -motorport Output port number for motor control.
184 # @arg -motorbit First (of two) motor control bits.
185 # @arg -pointsenseport Input port for point sense.
186 # @arg -pointsensebit First (of two) point sense bits.
187 # @arg -ossensorport Input port for OS sense.
188 # @arg -osbit This defines the input bit on the input port for OS
190 # @arg -direction The current direction of travel. Forward always
191 # means entering at the point end.
192 # @arg -forwarddirection The @e logial forward direction. Set this
193 # to reverse for a frog facing switch. Default is forward and it
194 # is readonly and can only be set during creation.
195 # @arg -forwardsignalobj The signal object protecting the points.
196 # Presumed to be a two headed signal, with the upper head relating to
197 # the main (straight) route and the lower head relating to the
198 # divergent route. The upper head has three colors: red, yellow, and
199 # green. The lower head only two: red and green.
200 # @arg -reversemainsignalobj The signal object protecting the straight
201 # frog end. Presumed to be single headed (with number plate).
202 # @arg -reversedivergentsignalobj The signal object protecting the
203 # divergent frog end. Presumed to be single headed (with number plate).
204 # @arg -previousblock The block connected to the point end.
205 # @arg -nextmainblock The block connected to the straight frog end.
206 # @arg -nextdivergentblock The block connected to the divergent frog
208 # @arg -plate The name of the switch plate for this switch.
211 # Prefetch the -forwarddirection option.
212 set options(-forwarddirection) [from args -forwarddirection]
213 ## Process any other options
214 $self configurelist $args
215 set node [$self cget -nodeobj]
217 error
"The -nodeobj option is required!"
219 CmriSupport::CmriNode validate $node
220 set forwardsignal [$self cget -forwardsignalobj]
221 set reversemainsignal [$self cget -reversemainsignalobj]
222 set reversedivergentsignal [$self cget -reversedivergentsignalobj]
225 method _settruedirection {option value} {
226 ## @private A method to fake direction for frog facing switches.
227 # @param option This is always -direction.
228 # @param value Either forward or reverse.
229 switch $options(-forwarddirection) {
231 set options($option) $value
235 forward {set options($option) reverse}
236 reverse {set options($option) forward}
241 method _gettruedirection {option} {
242 ## @private A method to fake direction for frog facing switches.
243 # @param option This is always -direction.
244 # @returns Either forward or reverse.
246 switch $options(-forwarddirection) {
247 forward {
return $options($option)}
249 switch $options($option) {
250 forward {
return reverse}
251 reverse {
return forward}
257 method occupiedp {} {
258 ## The occupiedp method returns yes or no (true or false) indicating
259 # block (OS) occupation.
260 # @returns Yes or no, indicating whether the OS is occupied.
262 # First read the current sensor state.
263 set inputs [$node inputs]
264 set port [lindex $inputs [$self cget -ossensorport]]
265 set bit [expr {($port >> [$self cget -osbit]) & 0x01}]
266 # The outputs of the OD are active negative, so we need to invert the
268 set bit [expr {(~$bit) & 0x01}]
271 # Already entered the OS.
274 # Just entered the OS
286 # OS still unoccupied
291 method pointstate {} {
292 ## The pointstate method returns normal if the points are aligned to
293 # the main route and reverse if the points are aligned to the divergent
294 # route. If the state cannot be determined, a value of unknown is
296 # @returns Normal or reverse, indicating the point state.
298 # Assume point state is unknown.
300 set inputs [$node inputs]
301 set port [lindex $inputs [$self cget -pointsenseport]]
302 set bits [expr {($port >> [$self cget -pointsensebit]) & 0x03}]
303 set result $_pointsense($bits)
304 if {[$self cget -plate] ne {}} {
305 set plate [$self cget -plate]
308 MainWindow ctcpanel seti $plate N on
309 MainWindow ctcpanel seti $plate C off
310 MainWindow ctcpanel seti $plate R off
313 MainWindow ctcpanel seti $plate N off
314 MainWindow ctcpanel seti $plate C off
315 MainWindow ctcpanel seti $plate R on
318 MainWindow ctcpanel seti $plate N off
319 MainWindow ctcpanel seti $plate C on
320 MainWindow ctcpanel seti $plate R off
327 ## @private Route check validation object.
329 set _routes [snit::enum _routes -values {normal reverse}]
332 method motor {route} {
333 ## The motor method sets the switch motor to align the points for the
335 # @param route The desired route. A value of normal means align the
336 # points to the main (straight) route and a value of reverse means
337 # align the points to the divergent route.
339 $_routes validate $route
340 set mask [expr {0x03 << [$self cget -motorbit]}]
341 set bits [expr {$_motorbits($route) << [$self cget -motorbit]}]
342 $node setbitfield [$self cget -motorport] $mask $bits
344 method _entering {} {
345 ## @protected Code to run when just entering the OS
346 # Sets the signal aspects and propagates signal state.
348 switch $options(-direction) {
350 # Forward direction, set point end signal and propagate back
352 if {$forwardsignal ne {}} {$forwardsignal setaspect {red red}}
353 if {[$self cget -previousblock] ne {}} {
354 [$self cget -previousblock] propagate yellow $self -direction [$self cget -direction]
359 switch [$self pointstate] {
361 # Set the main frog end signal and propagate down the
363 if {$reversemainsignal ne {}} {
364 $reversemainsignal setaspect red
366 if {[$self cget -nextmainblock] ne {}} {
367 [$self cget -nextmainblock] propagate yellow $self -direction [$self cget -direction]
371 # Set the divergent frog end signal and propagate down
372 # the divergent route.
373 if {$reversedivergentsignal ne {}} {
374 $reversedivergentsignal setaspect red
376 if {[$self cget -nextdivergentblock] ne {}} {
377 [$self cget -nextdivergentblock] propagate yellow $self -direction [$self cget -direction]
385 ## @protected Code to run when about to exit the OS
387 method propagate {aspect from args} {
388 ## @publicsection Method used to propagate distant signal states back down the line.
389 # @param aspect The signal aspect that is being propagated.
390 # @param from The propagating block.
391 # @param ... Options:
392 # @arg -direction The direction of the propagation.
394 set from [regsub {^::} $from {}]
395 $self configurelist $args
396 if {[$self occupiedp]} {
return}
398 switch $options(-direction) {
400 # Propagate back from the points.
401 switch [$self pointstate] {
403 # Points are normal, upper head is a logical block
404 # signal, but don't propagate against the points.
405 if {$from ne [regsub {^::} [$self cget -nextmainblock] {}]} {
return}
406 if {$forwardsignal ne {}} {
407 $forwardsignal setaspect [list $aspect red]
411 # Points are reversed, lower head is the controling
412 # head, but has no yellow.
413 # But don't propagate against the points.
414 if {$from ne [regsub {^::} [$self cget -nextdivergentblock] {}]} {
return}
415 if {$forwardsignal ne {}} {
416 $forwardsignal setaspect [list red green]
420 # Propagate back from the points.
421 if {$aspect eq
"yellow"} {
422 if {[$self cget -previousblock] ne {}} {
423 [$self cget -previousblock] propagate green $self -direction [$self cget -direction]
428 # Reverse direction, propagate towards the frog end.
429 switch [$self pointstate] {
431 # Points normal, propagate down the main.
432 if {$reversemainsignal ne {}} {
433 $reversemainsignal setaspect $aspect
435 if {$aspect eq
"yellow"} {
436 if {[$self cget -nextmainblock] ne {}} {
437 [$self cget -nextmainblock] propagate green $self -direction [$self cget -direction]
442 # Points reversed, propagate down the divergent route.
443 if {$reversedivergentsignal ne {}} {
444 $reversedivergentsignal setaspect $aspect
446 if {$aspect eq
"yellow"} {
447 if {[$self cget -nextdivergentblock] ne {}} {
448 [$self cget -nextdivergentblock] propagate green $self -direction [$self cget -direction]
458 package provide C4TSMINI_Switch 1.0