Model Railroad System  2.2.1
Overall User Manaual
Dispatcher Examples

These are four examples created using the Dispatcher program. The code files are included and can be used as references or even modified to suit some part of your layout.

Example 1: Simple siding on single track mainline

This example, shown below, implements a simple passing siding on a single track main line. There are two control points, one at each end of the siding. Both control points are handled with a single SMINI board.

Here is the code:

# Add User code after this line
# This is an example CTC Panel program. It features a section of single
# tracked mainline with a passing siding.
#
# The switches and signals are controled by a SMINI node, which also connects
# occupation detectors and switch point sensors to the host system.
# See example1.iow for the I/O connections for this example.
#
# The signals are two headed, two light signals.
#
package require snit;# Make sure snit is loaded.
# Define types:
# This code uses SNIT to create a set of OO types to encapsulate each of
# the several elements of the system: trackwork sections (blocks),
# switches (turnouts), Switch Plates, Signal Plates, Signals, and control
# points.
#
namespace eval Blocks {
# Block type (general trackwork).
# Encapsulates block occupation detectors.
#
snit::type Block {
# Occupation state values
typevariable OCC 1
typevariable CLR 0
# Occupation state bit
variable occupiedbit
constructor {} {
set occupiedbit $CLR;# Initialize to clear.
}
# Occupation state methods
method occupiedp {} {return [expr {$occupiedbit == $OCC}]}
method setoccupied {value} {
set occupiedbit $value
}
}
Block BK1;# Block 1
Block SD1;# Siding
Block BK3;# Block 3
Block BK4;# Block 4
}
namespace eval Switches {
# Switch type (turnout)
# Encapsulates a switch (turnout), including its OS (delegated to a Block
# object), its switch motor, and its point position sensor (its state).
snit::type Switch {
component block;# OS section
delegate method * to block;# Delegate block methods
variable state unknown;# Sense state (point position)
# Motor bit values
typevariable NOR 1;# 01
typevariable REV 2;# 10
variable motor;# Motor bits -- used to drive switch
# motor.
constructor {} {
# Install OS section
install block using Blocks::Block %AUTO%
# Initialize motor bits
set motor $NOR
}
# State methods
method getstate {} {return $state}
method setstate {statebits} {
if {$statebits == $NOR} {
set state normal
} elseif {$statebits == $REV} {
set state reverse
} else {
set state unknown
}
}
# Motor bit methods
method motorbits {} {return $motor}
method setmotor {mv} {
switch -exact $mv {
normal {set motor $NOR}
reverse {set motor $REV}
}
}
}
Switch S1;# Switch 1
Switch S2;# Switch 2
}
namespace eval SwitchPlates {
# Switch Plate
# Encapsulates a switch plate, implementing its lever position.
snit::type SwitchPlate {
component switch
delegate method * to switch
variable leverpos unknown
constructor {sw} {
set switch $sw
}
method setlever {pos} {set leverpos $pos}
method getlever {} {return $leverpos}
}
SwitchPlate SWP1 Switches::S1
SwitchPlate SWP2 Switches::S2
}
namespace eval Signals {
# Signal types. Encapsulates a signal's aspect.
snit::type OneHead {
# Single head signals have three states: dark, green or red.
typevariable aspects -array {
Dark 0x00
Green 0x01
Red 0x02
}
variable aspectbits
constructor {} {
set aspectbits $aspects(Dark)
}
method setaspect {a} {set aspectbits $aspects($a)}
method getaspect {} {return $aspectbits}
}
snit::type TwoHead {
# Two head signals have four states: dard, green over red, red over green,
# and red over red.
typevariable aspects -array {
Dark 0x00
GreenRed 0x06
RedGreen 0x09
RedRed 0x0A
}
variable aspectbits
constructor {} {
set aspectbits $aspects(Dark)
}
method setaspect {a} {set aspectbits $aspects($a)}
method getaspect {} {return $aspectbits}
}
TwoHead CP1E;# Heading into switch 1 from the west (block 1)
TwoHead CP1WM;# Heading into switch 1 from the east on the main
TwoHead CP1WS;# Heading into switch 1 from the east from the siding
TwoHead CP2EM;# Heading into switch 2 from the west on the main
TwoHead CP2ES;# Heading into switch 2 from the west from the siding
TwoHead CP2W;# Heading into switch 2 from the east (block 4)
}
namespace eval SignalPlates {
# Signal Plate, encapsulating a signal plate with its lever and indicators.
snit::type SignalPlate {
variable leverpos unknown
option -signalplate -default {} -readonly yes
constructor {args} {
$self configurelist $args
}
method setlever {pos} {set leverpos $pos}
method getlever {} {return $leverpos}
method setdot {dir} {
switch $dir {
left {
MainWindow ctcpanel seti $options(-signalplate) L on
MainWindow ctcpanel seti $options(-signalplate) C off
MainWindow ctcpanel seti $options(-signalplate) R off
}
right {
MainWindow ctcpanel seti $options(-signalplate) L off
MainWindow ctcpanel seti $options(-signalplate) C off
MainWindow ctcpanel seti $options(-signalplate) R on
}
none -
default {
MainWindow ctcpanel seti $options(-signalplate) L off
MainWindow ctcpanel seti $options(-signalplate) C on
MainWindow ctcpanel seti $options(-signalplate) R off
}
}
}
}
SignalPlate SGP1 -signalplate SGP1;# Signal plate for control point 1
SignalPlate SGP2 -signalplate SGP2;# Signal plate for control point 2
}
namespace eval ControlPoints {
# Control points. Used to implement code buttons.
# Encapsulates a control point
snit::type ControlPoint {
option -cpname -readonly yes -default {}
constructor {args} {
$self configurelist $args
}
method code {} {
foreach swp [MainWindow ctcpanel objectlist $options(-cpname) SwitchPlates] {
MainWindow ctcpanel invoke $swp
}
foreach sgp [MainWindow ctcpanel objectlist $options(-cpname) SignalPlates] {
MainWindow ctcpanel invoke $sgp
}
}
}
ControlPoint CP1 -cpname CP1;# Control point 1
ControlPoint CP2 -cpname CP2;# Control point 2
}
# Main Loop Start
while {true} {
# Read all ports
set CP1_2_inbits [CP1_2 inputs]
# Occupation Detectors: (Input Port A)
set tempByte [lindex $CP1_2_inbits 0]
Blocks::BK1 setoccupied [expr {$tempByte & 0x01}]
Switches::S1 setoccupied [expr {$tempByte >> 1 & 0x01}]
Blocks::BK3 setoccupied [expr {$tempByte >> 2 & 0x01}]
Blocks::SD1 setoccupied [expr {$tempByte >> 3 & 0x01}]
Switches::S2 setoccupied [expr {$tempByte >> 4 & 0x01}]
Blocks::BK4 setoccupied [expr {$tempByte >> 5 & 0x01}]
# Switch point state switches (Input Port B)
set tempByte [lindex $CP1_2_inbits 1]
Switches::S1 setstate [expr {$tempByte & 0x03}]
Switches::S2 setstate [expr {$tempByte >> 2 & 0x03}]
# Invoke all trackwork and get occupicency
MainWindow ctcpanel invoke Side1
MainWindow ctcpanel invoke BK1
MainWindow ctcpanel invoke BK3
MainWindow ctcpanel invoke BK4
# Initialize all signals to Red
Signals::CP1E setaspect RedRed
Signals::CP1WM setaspect RedRed
Signals::CP1WS setaspect RedRed
Signals::CP2EM setaspect RedRed
Signals::CP2ES setaspect RedRed
Signals::CP2W setaspect RedRed
set dot1 none;# Assume no direction of travel through control point 1
if {![MainWindow ctcpanel invoke S1]} {
# Switch1 OS is clear. We can start to move the points
Switches::S1 setmotor [SwitchPlates::SWP1 getlever]
# get current point state
switch [Switches::S1 getstate] {
normal {;# Aligned for the Main. Set the mainline signals.
if {![Blocks::BK1 occupiedp] &&
[string equal [SignalPlates::SGP1 getlever] "left"]} {
# Clear to left
Signals::CP1WM setaspect GreenRed
set dot1 left
}
if {![Blocks::BK3 occupiedp] &&
[string equal [SignalPlates::SGP1 getlever] "right"]} {
# Clear to right
Signals::CP1E setaspect GreenRed
set dot1 right
}
# Set plate indicators
MainWindow ctcpanel seti SWP1 N on
MainWindow ctcpanel seti SWP1 R off
}
reverse {;# Aligned for the siding. Set siding signals
if {![Blocks::BK1 occupiedp] &&
[string equal [SignalPlates::SGP1 getlever] "left"]} {
# Clear to left
Signals::CP1WS setaspect RedGreen
set dot1 left
}
if {![Blocks::SD1 occupiedp] &&
[string equal [SignalPlates::SGP1 getlever] "right"]} {
# Clear to right
Signals::CP1E setaspect RedGreen
set dot1 right
}
# Set plate indicators
MainWindow ctcpanel seti SWP1 R on
MainWindow ctcpanel seti SWP1 N off
}
default {;# Points still moving.
# Set plate indicators
MainWindow ctcpanel seti SWP1 R off
MainWindow ctcpanel seti SWP1 N off
}
}
}
# Set DOT on switch plate
SignalPlates::SGP1 setdot $dot1
# Switch 2 is much the same.
set dot2 none
if {![MainWindow ctcpanel invoke S2]} {
Switches::S2 setmotor [SwitchPlates::SWP2 getlever]
switch [Switches::S2 getstate] {
normal {
if {![Blocks::BK4 occupiedp] &&
[string equal [SignalPlates::SGP2 getlever] "right"]} {
Signals::CP2EM setaspect GreenRed
set dot2 right
}
if {![Blocks::BK3 occupiedp] &&
[string equal [SignalPlates::SGP2 getlever] "left"]} {
Signals::CP2W setaspect GreenRed
set dot2 left
}
MainWindow ctcpanel seti SWP2 N on
MainWindow ctcpanel seti SWP2 R off
}
reverse {
if {![Blocks::BK4 occupiedp] &&
[string equal [SignalPlates::SGP2 getlever] "right"]} {
Signals::CP2ES setaspect RedGreen
set dot2 right
}
if {![Blocks::SD1 occupiedp] &&
[string equal [SignalPlates::SGP2 getlever] "left"]} {
Signals::CP2W setaspect RedGreen
set dot2 left
}
MainWindow ctcpanel seti SWP2 R on
MainWindow ctcpanel seti SWP2 N off
}
default {
MainWindow ctcpanel seti SWP2 R off
MainWindow ctcpanel seti SWP2 N off
}
}
}
SignalPlates::SGP2 setdot $dot2
# Approach lighting -- darken signals facing empty blocks.
if {![Blocks::BK1 occupiedp]} {
Signals::CP1E setaspect Dark
}
if {![Blocks::BK3 occupiedp]} {
Signals::CP1WM setaspect Dark
Signals::CP2EM setaspect Dark
}
if {![Blocks::SD1 occupiedp]} {
Signals::CP1WS setaspect Dark
Signals::CP2ES setaspect Dark
}
if {![Blocks::BK4 occupiedp]} {
Signals::CP2W setaspect Dark
}
# Pack output bits
# Output Port A Card 0(CP1E and CP1WM)
set CP1_2_outbits [expr {[Signals::CP1E getaspect] | \
[Signals::CP1WM getaspect] << 4}]
# Output Port B Card 0 (CP1WS and S1)
lappend CP1_2_outbits [expr {[Signals::CP1WS getaspect] | \
[Switches::S1 motorbits] << 4}]
# Output Port C Card 0 (CP2EM and CP2ES)
lappend CP1_2_outbits [expr {[Signals::CP2EM getaspect] | \
[Signals::CP2ES getaspect] << 4}]
# Output Port A Card 2 (CP2W and S2)
lappend CP1_2_outbits [expr {[Signals::CP2W getaspect] | \
[Switches::S2 motorbits] << 4}]
lappend CP1_2_outbits 0 0;# Output Ports B and C Card 2are not used.
# puts stderr "*** CP1_2_outbits = $CP1_2_outbits"
# Write all output ports
eval [list CP1_2 outputs] $CP1_2_outbits
update;# Update display
}
# Main Loop End

And the I/O Worksheet:

SMINI @ UA 0:
Card No. 0 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 |Lower Green \ |
| | 1 | 2 |Lower Red | CP1E Signal |
| | 2 | 3 |Upper Green | |
| A | 3 | 4 |Upper Red / |
| | 4 | 5 |Lower Green \ |
| | 5 | 6 |Lower Red | CP1WM Signal |
| | 6 | 7 |Upper Green | |
| | 7 | 8 |Upper Red / |
--------------------------------------------------------------
| | 0 | 9 |Lower Green \ |
| | 1 |10 |Lower Red | CP1WS Signal |
| | 2 |11 |Upper Green | |
| B | 3 |12 |Upper Red / |
| | 4 |13 |Normal \ Switch 1 |
| | 5 |14 |Reverse/ Motor |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 |Lower Green \ |
| | 1 |18 |Lower Red | CP1E Signal |
| | 2 |19 |Upper Green | |
| C | 3 |20 |Upper Red / |
| | 4 |21 |Lower Green \ |
| | 5 |22 |Lower Red | CP1WM Signal |
| | 6 |23 |Upper Green | |
| | 7 |24 |Upper Red / |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 1 Input
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Block 1 Occupation Detector |
| | 1 | 2 | Switch 1 Occupation Detector |
| | 2 | 3 | Block 3 Occupation Detector |
| A | 3 | 4 | Siding Occupation Detector |
| | 4 | 5 | Switch 2 Occupation Detector |
| | 5 | 6 | Block 4 Occupation Detector |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | Switch 1 state (normal) |
| | 1 |10 | Switch 1 state (reverse) |
| | 2 |11 | Switch 2 state (normal) |
| B | 3 |12 | Switch 2 state (reverse) |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 2 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 |Lower Green \ |
| | 1 | 2 |Lower Red | CP2W Signal |
| | 2 | 3 |Upper Green | |
| A | 3 | 4 |Upper Red / |
| | 4 | 5 |Normal \ Switch 2 |
| | 5 | 6 |Reverse/ Motor |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
EOF

Example 2: Mainline with an industrial siding

This example, shown below, implements an industrial siding on a single track main line. There are two control points, one at each end of the siding. This example uses three SMINI boards, one for each control point and one for the siding.

Here is the code:

# Add User code after this line
# This is an example CTC Panel program. It features a section of single
# tracked mainline with an industrial siding.
#
# The switches and signals are controled by a pair of SMINI nodes, which also
# connects occupation detectors and switch point sensors to the host system.
# The switches to the industrial sidings are manually operated (ground throws),
# with point position sensors. There is no occupation detectors on the
# industrial sidings and their switches are not signaled.
# See example2.iow for the I/O connections for this example.
#
# The signals are two headed, three light signals.
#
package require snit;# Make sure snit is loaded.
# Define types:
# This code uses SNIT to create a set of OO types to encapsulate each of
# the several elements of the system: trackwork sections (blocks),
# switches (turnouts), Switch Plates, Signal Plates, Signals, and control
# points.
#
namespace eval Blocks {
# Block type (general trackwork).
# Encapsulates block occupation detectors.
#
snit::type Block {
# Occupation state values
typevariable OCC 1
typevariable CLR 0
# Occupation state bit
variable occupiedbit
constructor {} {
set occupiedbit $CLR;# Initialize to clear.
}
# Occupation state methods
method occupiedp {} {return [expr {$occupiedbit == $OCC}]}
method setoccupied {value} {
set occupiedbit $value
}
}
# Main line trackage
Block BK1;# Block 1
Block BK3;# Block 3
Block BK5;# Block 5
# Industrial siding sections
Block ISide1
Block ISide2
Block ISide3
Block ISide4
}
namespace eval Switches {
# Switch type (turnout)
# Encapsulates a switch (turnout), including its OS (delegated to a Block
# object), its switch motor, and its point position sensor (its state).
snit::type Switch {
component block;# OS section
delegate method * to block;# Delegate block methods
variable state unknown;# Sense state (point position)
# Motor bit values
typevariable NOR 1;# 01
typevariable REV 2;# 10
variable motor;# Motor bits -- used to drive switch
# motor.
constructor {} {
# Install OS section
install block using Blocks::Block %AUTO%
# Initialize motor bits
set motor $NOR
}
# State methods
method getstate {} {return $state}
method setstate {statebits} {
if {$statebits == $NOR} {
set state normal
} elseif {$statebits == $REV} {
set state reverse
} else {
set state unknown
}
}
# Motor bit methods
method motorbits {} {return $motor}
method setmotor {mv} {
switch -exact $mv {
normal {set motor $NOR}
reverse {set motor $REV}
}
}
}
Switch SW1;# Switch at CP1
Switch SW2;# Switch at CP2
Switch INDUS1;# Switch at Sawmill
Switch INDUS2;# Switch at Box Factory
Switch INDUS3;# Switch at Mill
}
namespace eval SwitchPlates {
# Switch Plate
# Encapsulates a switch plate, implementing its lever position.
snit::type SwitchPlate {
component switch
delegate method * to switch
variable leverpos unknown
constructor {sw} {
set switch $sw
}
method setlever {pos} {set leverpos $pos}
method getlever {} {return $leverpos}
}
SwitchPlate CP1SW Switches::SW1
SwitchPlate CP2SW Switches::SW2
}
namespace eval Signals {
# Signal types. Encapsulates a signal's aspect.
snit::type OneHead {
# Single head signals have four states: dark, green, yellow, or red.
typevariable aspects -array {
Dark 0x00
Green 0x01
Yellow 0x02
Red 0x04
}
variable aspectbits
constructor {} {
set aspectbits $aspects(Dark)
}
method setaspect {a} {set aspectbits $aspects($a)}
method getaspect {} {return $aspectbits}
}
snit::type TwoHead {
# Two head signals have five states: dark, green over red,
# yellow over red, red over yellow, and red over red.
typevariable aspects -array {
Dark 0x00
GreenRed 0x11
YellowRed 0x12
RedYellow 0x0c
RedRed 0x14
}
variable aspectbits
constructor {} {
set aspectbits $aspects(Dark)
}
method setaspect {a} {set aspectbits $aspects($a)}
method getaspect {} {return $aspectbits}
}
TwoHead CP1E;# Heading into switch 1 from the west (block 1)
TwoHead CP1WM;# Heading into switch 1 from the east on the main (Block 3)
TwoHead CP1WS;# Heading into switch 1 from the east from the siding
TwoHead CP2EM;# Heading into switch 2 from the west on the main (Block 3)
TwoHead CP2ES;# Heading into switch 2 from the west from the siding
TwoHead CP2W;# Heading into switch 2 from the east (block 5)
}
namespace eval SignalPlates {
# Signal Plate, encapsulating a signal plate with its lever and indicators.
snit::type SignalPlate {
variable leverpos unknown
option -signalplate -default {} -readonly yes
constructor {args} {
$self configurelist $args
}
method setlever {pos} {set leverpos $pos}
method getlever {} {return $leverpos}
method setdot {dir} {
switch $dir {
left {
MainWindow ctcpanel seti $options(-signalplate) L on
MainWindow ctcpanel seti $options(-signalplate) C off
MainWindow ctcpanel seti $options(-signalplate) R off
}
right {
MainWindow ctcpanel seti $options(-signalplate) L off
MainWindow ctcpanel seti $options(-signalplate) C off
MainWindow ctcpanel seti $options(-signalplate) R on
}
none -
default {
MainWindow ctcpanel seti $options(-signalplate) L off
MainWindow ctcpanel seti $options(-signalplate) C on
MainWindow ctcpanel seti $options(-signalplate) R off
}
}
}
}
SignalPlate CP1SG -signalplate CP1SG;#Signal plate for control point 1
SignalPlate CP2SG -signalplate CP2SG;#Signal plate for control point 2
}
namespace eval ControlPoints {
# Control points. Used to implement code buttons.
# Encapsulates a control point
snit::type ControlPoint {
option -cpname -readonly yes -default {}
constructor {args} {
$self configurelist $args
}
method code {} {
foreach swp [MainWindow ctcpanel objectlist $options(-cpname) SwitchPlates] {
MainWindow ctcpanel invoke $swp
}
foreach sgp [MainWindow ctcpanel objectlist $options(-cpname) SignalPlates] {
MainWindow ctcpanel invoke $sgp
}
}
}
ControlPoint CP1 -cpname CP1;# Control point 1
ControlPoint CP2 -cpname CP2;# Control point 2
}
# Main Loop Start
while {true} {
# Read all ports
set CP1_inbits [CP1 inputs]
# Occupation Detectors and point state switches: (Input Port A, Card 1)
set tempByte [lindex $CP1_inbits 0]
Blocks::BK1 setoccupied [expr {$tempByte & 0x01}]
Switches::SW1 setoccupied [expr {$tempByte >> 1 & 0x01}]
Switches::SW1 setstate [expr {$tempByte >> 2 & 0x03}]
set CP2_inbits [CP2 inputs]
# Occupation Detectors and point state switches: (Input Port A, Card 1)
set tempByte [lindex $CP2_inbits 0]
Blocks::BK5 setoccupied [expr {$tempByte & 0x01}]
Switches::SW2 setoccupied [expr {$tempByte >> 1 & 0x01}]
Switches::SW2 setstate [expr {$tempByte >> 2 & 0x03}]
set Siding_inbits [Siding inputs]
# Occupation Detectors: (Input Port A, Card 1)
set tempByte [lindex $Siding_inbits 0]
Blocks::BK3 setoccupied [expr {$tempByte & 0x01}]
Blocks::ISide1 setoccupied [expr {$tempByte >> 1 & 0x01}]
Blocks::ISide2 setoccupied [expr {$tempByte >> 2 & 0x01}]
Blocks::ISide3 setoccupied [expr {$tempByte >> 3 & 0x01}]
Blocks::ISide4 setoccupied [expr {$tempByte >> 4 & 0x01}]
Switches::INDUS1 setoccupied [expr {$tempByte >> 5 & 0x01}]
Switches::INDUS2 setoccupied [expr {$tempByte >> 6 & 0x01}]
Switches::INDUS3 setoccupied [expr {$tempByte >> 7 & 0x01}]
# Point state switches: (Input Port B, Card 1)
set tempByte [lindex $Siding_inbits 1]
Switches::INDUS1 setstate [expr {$tempByte & 0x03}]
Switches::INDUS2 setstate [expr {$tempByte >> 2 & 0x03}]
Switches::INDUS3 setstate [expr {$tempByte >> 4 & 0x03}]
# Invoke all trackwork and get occupicency
MainWindow ctcpanel invoke BK1
MainWindow ctcpanel invoke Bk3
MainWindow ctcpanel invoke BK5
MainWindow ctcpanel invoke ISide1
MainWindow ctcpanel invoke ISide2
MainWindow ctcpanel invoke ISide3
MainWindow ctcpanel invoke ISide4
MainWindow ctcpanel invoke INDUS1
MainWindow ctcpanel invoke INDUS2
MainWindow ctcpanel invoke INDUS3
# Combine all siding ODs
set Siding [expr {[Blocks::ISide1 occupiedp] ||
[Blocks::ISide2 occupiedp] ||
[Blocks::ISide3 occupiedp] ||
[Blocks::ISide4 occupiedp] ||
[Switches::INDUS1 occupiedp] ||
[Switches::INDUS2 occupiedp] ||
[Switches::INDUS3 occupiedp]}]
# Initialize all signals to Red
Signals::CP1E setaspect RedRed
Signals::CP1WM setaspect RedRed
Signals::CP1WS setaspect RedRed
Signals::CP2EM setaspect RedRed
Signals::CP2ES setaspect RedRed
Signals::CP2W setaspect RedRed
set dot1 none;# Assume no direction of travel through control point 1
if {![MainWindow ctcpanel invoke SW1]} {
# Switch1 OS is clear. We can start to move the points
Switches::SW1 setmotor [SwitchPlates::CP1SW getlever]
# get current point state
switch [Switches::SW1 getstate] {
normal {;# Aligned for the Main. Set the mainline signals.
if {![Blocks::BK1 occupiedp] &&
[string equal [SignalPlates::CP1SG getlever] "left"]} {
# Clear to left
Signals::CP1WM setaspect GreenRed
set dot1 left
}
if {![Blocks::BK3 occupiedp] &&
[string equal [SignalPlates::CP1SG getlever] "right"]} {
# Clear to right
Signals::CP1E setaspect GreenRed
set dot1 right
}
# Set plate indicators
MainWindow ctcpanel seti CP1SW N on
MainWindow ctcpanel seti CP1SW R off
}
reverse {;# Aligned for the siding. Set siding signals
if {![Blocks::BK1 occupiedp] &&
[string equal [SignalPlates::CP1SG getlever] "left"]} {
# Clear to left
Signals::CP1WS setaspect RedYellow
set dot1 left
}
if {!$Siding && [string equal [SignalPlates::CP1SG getlever] "right"]} {
# Clear to right
Signals::CP1E setaspect RedYellow
set dot1 right
}
# Set plate indicators
MainWindow ctcpanel seti CP1SW N off
MainWindow ctcpanel seti CP1SW R on
}
default {;# Points still moving.
# Set plate indicators
MainWindow ctcpanel seti CP1SW N off
MainWindow ctcpanel seti CP1SW R off
}
}
}
# Set DOT on signal plate
SignalPlates::CP1SG setdot $dot1
# Switch 2 is much the same.
set dot2 none;# Assume no direction of travel through control point 1
if {![MainWindow ctcpanel invoke SW2]} {
# Switch1 OS is clear. We can start to move the points
Switches::SW2 setmotor [SwitchPlates::CP2SW getlever]
# get current point state
switch [Switches::SW2 getstate] {
normal {;# Aligned for the Main. Set the mainline signals.
if {![Blocks::BK5 occupiedp] &&
[string equal [SignalPlates::CP2SG getlever] "right"]} {
# Clear to right
Signals::CP2EM setaspect GreenRed
set dot2 right
}
if {![Blocks::BK3 occupiedp] &&
[string equal [SignalPlates::CP2SG getlever] "left"]} {
# Clear to left
Signals::CP2W setaspect GreenRed
set dot2 left
}
# Set plate indicators
MainWindow ctcpanel seti CP2SW N on
MainWindow ctcpanel seti CP2SW R off
}
reverse {;# Aligned for the siding. Set siding signals
if {![Blocks::BK5 occupiedp] &&
[string equal [SignalPlates::CP2SG getlever] "right"]} {
# Clear to right
Signals::CP2ES setaspect RedYellow
set dot2 right
}
if {!$Siding && [string equal [SignalPlates::CP2SG getlever] "left"]} {
# Clear to left
Signals::CP2W setaspect RedYellow
set dot2 left
}
# Set plate indicators
MainWindow ctcpanel seti CP2SW N off
MainWindow ctcpanel seti CP2SW R on
}
default {;# Points still moving.
# Set plate indicators
MainWindow ctcpanel seti CP2SW N off
MainWindow ctcpanel seti CP2SW R off
}
}
}
# Set DOT on signal plate
SignalPlates::CP2SG setdot $dot2
# Approach lighting -- darken signals facing empty blocks.
if {![Blocks::BK1 occupiedp]} {
Signals::CP1E setaspect Dark
}
if {![Blocks::BK3 occupiedp]} {
Signals::CP1WM setaspect Dark
Signals::CP2EM setaspect Dark
}
if {!$Siding} {
Signals::CP1WS setaspect Dark
Signals::CP2ES setaspect Dark
}
if {![Blocks::BK5 occupiedp]} {
Signals::CP2W setaspect Dark
}
# Pack output bits
# CP1:
# Output Port A, Card 0 (CP1E and SW1)
set CP1_outbits [expr {[Signals::CP1E getaspect] | \
[Switches::SW1 motorbits] << 5}]
# Output Port B, Card 0 (CP1WM)
lappend CP1_outbits [Signals::CP1WM getaspect]
# Output Port C, Card 0 (CP1WS)
lappend CP1_outbits [Signals::CP1WS getaspect]
# Output Ports A, B, C of Card 2: nothing
lappend CP1_outbits 0 0 0
# CP2:
# Output Port A, Card 0 (CP2W and SW2)
set CP2_outbits [expr {[Signals::CP2W getaspect] | \
[Switches::SW2 motorbits] << 5}]
# Output Port B, Card 0 (CP2EM)
lappend CP2_outbits [Signals::CP2EM getaspect]
# Output Port C, Card 0 (CP2ES)
lappend CP2_outbits [Signals::CP2ES getaspect]
# Output Ports A, B, C of Card 2: nothing
lappend CP2_outbits 0 0 0
# Write all output ports
eval [list CP1 outputs] $CP1_outbits
eval [list CP2 outputs] $CP2_outbits
#eval [list Siding outputs] $Siding_outbits;# No Siding outputs ports used
update;# Update display
}
# Main Loop End

And the I/O Worksheet:

SMINI @ UA 0 (CP1)
Card No. 0 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Upper Green \ |
| | 1 | 2 | Upper Yellow | CP1E Signal |
| | 2 | 3 | Upper Red | |
| A | 3 | 4 | Lower Yellow | |
| | 4 | 5 | Lower Red / |
| | 5 | 6 | Normal \ Switch 1 |
| | 6 | 7 | Reverse / motor |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | Upper Green \ |
| | 1 |10 | Upper Yellow | CP1WM Signal |
| | 2 |11 | Upper Red | |
| B | 3 |12 | Lower Yellow | |
| | 4 |13 | Lower Red / |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | Upper Green \ |
| | 1 |18 | Upper Yellow | CP1WS Signal |
| | 2 |19 | Upper Red | |
| C | 3 |20 | Lower Yellow | |
| | 4 |21 | Lower Red / |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 1 Input
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Block 1 Occupation Detector |
| | 1 | 2 | Switch 1 OS Occupation Detector |
| | 2 | 3 | Normal \ Switch 1 |
| A | 3 | 4 | Reverse / state contacts |
| | 4 | 5 | |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 2 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | |
| | 1 | 2 | |
| | 2 | 3 | |
| A | 3 | 4 | |
| | 4 | 5 | |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
SMINI @ UA 1 (Siding)
Card No. 0 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | |
| | 1 | 2 | |
| | 2 | 3 | |
| A | 3 | 4 | |
| | 4 | 5 | |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 1 Input
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Block 3 Occupation Detector |
| | 1 | 2 | Siding, section 1 Occupation Detector |
| | 2 | 3 | Siding, section 2 Occupation Detector |
| A | 3 | 4 | Siding, section 3 Occupation Detector |
| | 4 | 5 | Siding, section 4 Occupation Detector |
| | 5 | 6 | Industry 1 Occupation Detector |
| | 6 | 7 | Industry 2 Occupation Detector |
| | 7 | 8 | Industry 3 Occupation Detector |
--------------------------------------------------------------
| | 0 | 9 | Normal \ Manual switch |
| | 1 |10 | Reverse / Industry 1 state |
| | 2 |11 | Normal \ Manual switch |
| B | 3 |12 | Reverse / Industry 2 state |
| | 4 |13 | Normal \ Manual switch |
| | 5 |14 | Reverse / Industry 3 state |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 2 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | |
| | 1 | 2 | |
| | 2 | 3 | |
| A | 3 | 4 | |
| | 4 | 5 | |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
SMINI @ UA 2 (CP2)
Card No. 0 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Upper Green \ |
| | 1 | 2 | Upper Yellow | CP2W Signal |
| | 2 | 3 | Upper Red | |
| A | 3 | 4 | Lower Yellow | |
| | 4 | 5 | Lower Red / |
| | 5 | 6 | Normal \ Switch 2 |
| | 6 | 7 | Reverse / motor |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | Upper Green \ |
| | 1 |10 | Upper Yellow | CP2EM Signal |
| | 2 |11 | Upper Red | |
| B | 3 |12 | Lower Yellow | |
| | 4 |13 | Lower Red / |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | Upper Green \ |
| | 1 |18 | Upper Yellow | CP2ES Signal |
| | 2 |19 | Upper Red | |
| C | 3 |20 | Lower Yellow | |
| | 4 |21 | Lower Red / |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 1 Input
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Block 5 Occupation Detector |
| | 1 | 2 | Switch 2 OS Occupation Detector |
| | 2 | 3 | Normal \ Switch 2 |
| A | 3 | 4 | Reverse / state contacts |
| | 4 | 5 | |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 2 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | |
| | 1 | 2 | |
| | 2 | 3 | |
| A | 3 | 4 | |
| | 4 | 5 | |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
EOF

Example 3: double track crossover

This example, shown below, implements a double track crossover. Uses two SMINI boards, one for each of the two control points.

Here is the code:

# Add User code after this line
# This is an example CTC Panel program. It features a section of double
# tracked mainline with a pair of crossovers
#
# The switches and signals are controled by a pair of SMINI nodes, which also
# connects occupation detectors and switch point sensors to the host system.
# See example3.iow for I/O connections for this example.
#
# The signals are two headed, three light signals.
#
package require snit;# Make sure snit is loaded.
# Define types:
# This code uses SNIT to create a set of OO types to encapsulate each of
# the several elements of the system: trackwork sections (blocks),
# switches (turnouts), Switch Plates, Signal Plates, Signals, and control
# points.
#
namespace eval Blocks {
# Block type (general trackwork).
# Encapsulates block occupation detectors.
#
snit::type Block {
# Occupation state values
typevariable OCC 1
typevariable CLR 0
# Occupation state bit
variable occupiedbit
constructor {} {
set occupiedbit $CLR;# Initialize to clear.
}
# Occupation state methods
method occupiedp {} {return [expr {$occupiedbit == $OCC}]}
method setoccupied {value} {
set occupiedbit $value
}
}
# Main line trackage
Block BK1T1;# Block 1, East
Block BK1T2;# Block 1, West
Block BK3T1;# Block 3, East
Block BK3T2;# Block 3, West
Block BK5T1;# Block 5, East
Block BK5T2;# Block 5, West
}
namespace eval Switches {
# Switch type (turnout)
# Encapsulates a switch (turnout), including its OS (delegated to a Block
# object), its switch motor, and its point position sensor (its state).
snit::type Switch {
component block;# OS section
delegate method * to block;# Delegate block methods
variable state unknown;# Sense state (point position)
# Motor bit values
typevariable NOR 1;# 01
typevariable REV 2;# 10
variable motor;# Motor bits -- used to drive switch
# motor.
constructor {} {
# Install OS section
install block using Blocks::Block %AUTO%
# Initialize motor bits
set motor $NOR
}
# State methods
method getstate {} {return $state}
method setstate {statebits} {
if {$statebits == $NOR} {
set state normal
} elseif {$statebits == $REV} {
set state reverse
} else {
set state unknown
}
}
# Motor bit methods
method motorbits {} {return $motor}
method setmotor {mv} {
switch -exact $mv {
normal {set motor $NOR}
reverse {set motor $REV}
}
}
}
Switch SW1a;# Switch at CP1
Switch SW1b;# Switch at CP1
Switch SW2a;# Switch at CP2
Switch SW2b;# Switch at CP2
}
namespace eval SwitchPlates {
# Switch Plate
# Encapsulates a switch plate, implementing its lever position.
snit::type SwitchPlate {
delegate method * to switch
variable leverpos unknown
constructor {sw} {
set switch $sw
}
method setlever {pos} {set leverpos $pos}
method getlever {} {return $leverpos}
}
SwitchPlate CP1SW
SwitchPlate CP2SW
}
namespace eval Signals {
# Signal types. Encapsulates a signal's aspect.
snit::type OneHead {
# Single head signals have four states: dark, green, yellow, or red.
typevariable aspects -array {
Dark 0x00
Green 0x01
Yellow 0x02
Red 0x04
}
variable aspectbits
constructor {} {
set aspectbits $aspects(Dark)
}
method setaspect {a} {set aspectbits $aspects($a)}
method getaspect {} {return $aspectbits}
}
snit::type TwoHead {
# Two head signals have five states: dark, green over red,
# yellow over red, red over yellow, and red over red.
typevariable aspects -array {
Dark 0x00
GreenRed 0x11
YellowRed 0x12
RedYellow 0x0c
RedRed 0x14
}
variable aspectbits
constructor {} {
set aspectbits $aspects(Dark)
}
method setaspect {a} {set aspectbits $aspects($a)}
method getaspect {} {return $aspectbits}
}
TwoHead CP1ET1;# Heading into switch 1 from the west (block 1, Track 1)
TwoHead CP1WT1;# Heading into switch 1 from the east (Block 3, Track 1)
TwoHead CP1ET2;# Heading into switch 1 from the east (block 1, Track 2)
TwoHead CP1WT2;# Heading into switch 1 from the west (Block 3, Track 2)
TwoHead CP2ET1;# Heading into switch 1 from the west (block 3, Track 1)
TwoHead CP2WT1;# Heading into switch 1 from the east (Block 5, Track 1)
TwoHead CP2ET2;# Heading into switch 1 from the east (block 3, Track 2)
TwoHead CP2WT2;# Heading into switch 1 from the west (Block 5, Track 2)
}
namespace eval SignalPlates {
# Signal Plate, encapsulating a signal plate with its lever and indicators.
snit::type SignalPlate {
variable leverpos unknown
option -signalplate -default {} -readonly yes
constructor {args} {
$self configurelist $args
}
method setlever {pos} {set leverpos $pos}
method getlever {} {return $leverpos}
method setdot {dir} {
switch $dir {
left {
MainWindow ctcpanel seti $options(-signalplate) L on
MainWindow ctcpanel seti $options(-signalplate) C off
MainWindow ctcpanel seti $options(-signalplate) R off
}
right {
MainWindow ctcpanel seti $options(-signalplate) L off
MainWindow ctcpanel seti $options(-signalplate) C off
MainWindow ctcpanel seti $options(-signalplate) R on
}
none -
default {
MainWindow ctcpanel seti $options(-signalplate) L off
MainWindow ctcpanel seti $options(-signalplate) C on
MainWindow ctcpanel seti $options(-signalplate) R off
}
}
}
}
SignalPlate CP1T1SG -signalplate CP1T1SG;#Signal plate for CP1, track 1
SignalPlate CP1T2SG -signalplate TRACK2;#Signal plate for CP1, track 2
SignalPlate CP2T1SG -signalplate CP1T1SG;#Signal plate for CP2, track 1
SignalPlate CP2T2SG -signalplate TRACK2;#Signal plate for CP2, track 2
}
namespace eval ControlPoints {
# Control points. Used to implement code buttons.
# Encapsulates a control point
snit::type ControlPoint {
option -cpname -readonly yes -default {}
constructor {args} {
$self configurelist $args
}
method code {} {
foreach swp [MainWindow ctcpanel objectlist $options(-cpname) SwitchPlates] {
MainWindow ctcpanel invoke $swp
}
foreach sgp [MainWindow ctcpanel objectlist $options(-cpname) SignalPlates] {
MainWindow ctcpanel invoke $sgp
}
}
}
ControlPoint CP1 -cpname CP1;# Control point 1
ControlPoint CP2 -cpname CP2;# Control point 2
}
# Main Loop Start
while {true} {
# Read all ports
set CP1_inbits [CP1 inputs]
# Occupation Detectors and point state switches: (Input Port A)
set tempByte [lindex $CP1_inbits 0]
Blocks::BK1T1 setoccupied [expr {$tempByte & 0x01}]
Blocks::BK1T2 setoccupied [expr {$tempByte >> 1 & 0x01}]
Blocks::BK3T1 setoccupied [expr {$tempByte >> 2 & 0x01}]
# The both switches are on a common OD for their OSs
Switches::SW1a setoccupied [expr {$tempByte >> 3 & 0x01}]
Switches::SW1b setoccupied [expr {$tempByte >> 3 & 0x01}]
Switches::SW1a setstate [expr {$tempByte >> 4 & 0x03}]
Switches::SW1b setstate [expr {$tempByte >> 6 & 0x03}]
set CP2_inbits [CP2 inputs]
# Occupation Detectors and point state switches: (Input Port A)
set tempByte [lindex $CP2_inbits 0]
Blocks::BK5T1 setoccupied [expr {$tempByte & 0x01}]
Blocks::BK5T2 setoccupied [expr {$tempByte >> 1 & 0x01}]
Blocks::BK3T2 setoccupied [expr {$tempByte >> 2 & 0x01}]
# The both switches are on a common OD for their OSs
Switches::SW2a setoccupied [expr {$tempByte >> 3 & 0x01}]
Switches::SW2b setoccupied [expr {$tempByte >> 3 & 0x01}]
Switches::SW2a setstate [expr {$tempByte >> 4 & 0x03}]
Switches::SW2b setstate [expr {$tempByte >> 6 & 0x03}]
# Invoke all trackwork and get occupicency
MainWindow ctcpanel invoke BK1T1
MainWindow ctcpanel invoke BK1T2
MainWindow ctcpanel invoke BK3T1
MainWindow ctcpanel invoke BK3T2
MainWindow ctcpanel invoke BK5T1
MainWindow ctcpanel invoke BK5T2
MainWindow ctcpanel invoke SW1a
MainWindow ctcpanel invoke SW1b
MainWindow ctcpanel invoke SW2a
MainWindow ctcpanel invoke SW2b
# Initialize all signals to Red
Signals::CP1ET1 setaspect RedRed
Signals::CP1WT1 setaspect RedRed
Signals::CP1ET2 setaspect RedRed
Signals::CP1WT2 setaspect RedRed
Signals::CP2ET1 setaspect RedRed
Signals::CP2WT1 setaspect RedRed
Signals::CP2ET2 setaspect RedRed
Signals::CP2WT2 setaspect RedRed
set dot1t1 none ;# Assume no direction of travel through control point 1, track 1
set dot1t2 none ;# Assume no direction of travel through control point 1, track 2
if {![MainWindow ctcpanel invoke SW1a] &&
![MainWindow ctcpanel invoke SW1b]} {
# Switch1 (a & b) OS is clear. We can start to move the points
Switches::SW1a setmotor [SwitchPlates::CP1SW getlever]
Switches::SW1b setmotor [SwitchPlates::CP1SW getlever]
set s1a [Switches::SW1a getstate]
set s1b [Switches::SW1b getstate]
if {[string equal "$s1a" "$s1b"]} {
switch $s1a {
normal {;# Aligned for the Main. Set the mainline signals.
if {![Blocks::BK1T1 occupiedp] &&
[string equal [SignalPlates::CP1T1SG getlever] "left"]} {
# Clear to left on track 1
Signals::CP1WT1 setaspect GreenRed
set dot1t1 left
}
if {![Blocks::BK1T2 occupiedp] &&
[string equal [SignalPlates::CP1T2SG getlever] "left"]} {
# Clear to left on track 2
Signals::CP1WT2 setaspect GreenRed
set dot1t2 left
}
if {![Blocks::BK3T1 occupiedp] &&
[string equal [SignalPlates::CP1T1SG getlever] "right"]} {
# Clear to right on track 1
Signals::CP1ET1 setaspect GreenRed
set dot1t1 right
}
if {![Blocks::BK3T2 occupiedp] &&
[string equal [SignalPlates::CP1T2SG getlever] "right"]} {
# Clear to right on track 2
Signals::CP1ET2 setaspect GreenRed
set dot1t2 right
}
# Set plate indicators
MainWindow ctcpanel seti CP1SW N on
MainWindow ctcpanel seti CP1SW R off
}
reverse {;# Aligned for crossing over
if {[string equal [SignalPlates::CP1T1SG getlever] [SignalPlates::CP1T2SG getlever]]} {
if {![Blocks::BK1T1 occupiedp] &&
[string equal [SignalPlates::CP1T1SG getlever] "left"]} {
Signals::CP1WT2 setaspect RedYellow
set dot1t1 left
set dot1t2 left
}
if {![Blocks::BK3T2 occupiedp] &&
[string equal [SignalPlates::CP1T2SG getlever] "right"]} {
Signals::CP1ET1 setaspect RedYellow
set dot1t1 right
set dot1t2 right
}
}
# Set plate indicators
MainWindow ctcpanel seti CP1SW N off
MainWindow ctcpanel seti CP1SW R on
}
default {;# Points still moving.
# Set plate indicators
MainWindow ctcpanel seti CP1SW N off
MainWindow ctcpanel seti CP1SW R off
}
}
} else {;# Points not consistently aligned
# Set plate indicators
MainWindow ctcpanel seti CP1SW N off
MainWindow ctcpanel seti CP1SW R off
}
}
# Set DOT on signal plates
SignalPlates::CP1T1SG setdot $dot1t1
SignalPlates::CP1T2SG setdot $dot1t2
set dot2t1 none ;# Assume no direction of travel through control point 2, track 1
set dot2t2 none ;# Assume no direction of travel through control point 2, track 2
if {![MainWindow ctcpanel invoke SW2a] &&
![MainWindow ctcpanel invoke SW2b]} {
# Switch2 (a & b) OS is clear. We can start to move the points
Switches::SW2a setmotor [SwitchPlates::CP2SW getlever]
Switches::SW2b setmotor [SwitchPlates::CP2SW getlever]
set s2a [Switches::SW2a getstate]
set s2b [Switches::SW2b getstate]
if {[string equal "$s2a" "$s2b"]} {
switch $s2a {
normal {;# Aligned for the Main. Set the mainline signals.
if {![Blocks::BK3T1 occupiedp] &&
[string equal [SignalPlates::CP2T1SG getlever] "left"]} {
# Clear to left on track 1
Signals::CP2WT1 setaspect GreenRed
set dot2t1 left
}
if {![Blocks::BK3T2 occupiedp] &&
[string equal [SignalPlates::CP2T2SG getlever] "left"]} {
# Clear to left on track 2
Signals::CP2WT2 setaspect GreenRed
set dot2t2 left
}
if {![Blocks::BK5T1 occupiedp] &&
[string equal [SignalPlates::CP2T1SG getlever] "right"]} {
# Clear to right on track 1
Signals::CP2ET1 setaspect GreenRed
set dot2t1 right
}
if {![Blocks::BK5T2 occupiedp] &&
[string equal [SignalPlates::CP2T2SG getlever] "right"]} {
# Clear to right on track 2
Signals::CP2ET2 setaspect GreenRed
set dot2t2 right
}
# Set plate indicators
MainWindow ctcpanel seti CP2SW N on
MainWindow ctcpanel seti CP2SW R off
}
reverse {;# Aligned for crossing over
if {[string equal [SignalPlates::CP2T1SG getlever] [SignalPlates::CP2T2SG getlever]]} {
if {![Blocks::BK3T2 occupiedp] &&
[string equal [SignalPlates::CP2T2SG getlever] "left"]} {
Signals::CP2WT1 setaspect RedYellow
set dot2t1 left
set dot2t2 left
}
if {![Blocks::BK5T1 occupiedp] &&
[string equal [SignalPlates::CP2T1SG getlever] "right"]} {
Signals::CP2ET2 setaspect RedYellow
set dot2t1 right
set dot2t2 right
}
}
# Set plate indicators
MainWindow ctcpanel seti CP2SW N off
MainWindow ctcpanel seti CP2SW R on
}
default {;# Points still moving.
# Set plate indicators
MainWindow ctcpanel seti CP2SW N off
MainWindow ctcpanel seti CP2SW R off
}
}
} else {;# Points not consistently aligned
# Set plate indicators
MainWindow ctcpanel seti CP2SW N off
MainWindow ctcpanel seti CP2SW R off
}
}
# Set DOT on signal plates
SignalPlates::CP2T1SG setdot $dot2t1
SignalPlates::CP2T2SG setdot $dot2t2
# Approach lighting -- darken signals facing empty blocks.
if {![Blocks::BK1T1 occupiedp]} {
Signals::CP1ET1 setaspect Dark
}
if {![Blocks::BK1T2 occupiedp]} {
Signals::CP1ET2 setaspect Dark
}
if {![Blocks::BK3T1 occupiedp]} {
Signals::CP1WT1 setaspect Dark
Signals::CP2ET1 setaspect Dark
}
if {![Blocks::BK1T2 occupiedp]} {
Signals::CP1WT2 setaspect Dark
Signals::CP2ET2 setaspect Dark
}
if {![Blocks::BK5T1 occupiedp]} {
Signals::CP2WT1 setaspect Dark
}
if {![Blocks::BK5T2 occupiedp]} {
Signals::CP2WT2 setaspect Dark
}
# Pack output bits
# CP1:
set CP1_outbits [Signals::CP1ET1 getaspect];# Output Port A, Card 0 (CP1ET1)
lappend CP1_outbits [Signals::CP1ET2 getaspect];# Output Port B, Card 0 (CP1ET2)
lappend CP1_outbits [Signals::CP1WT1 getaspect];# Output Port C, Card 0 (CP1WT1)
lappend CP1_outbits [Signals::CP1WT2 getaspect];# Output Port A, Card 2 (CP1WT2)
# Output Port B, Card 2 (Switch motor bits SW1a and SW1b)
lappend CP1_outbits [expr {[Switches::SW1a motorbits] | \
[Switches::SW1b motorbits] << 2}]
lappend CP1_outbits 0;# Output Port C, Card 2 (unused)
# CP2:
set CP2_outbits [Signals::CP2ET1 getaspect];# Output Port A, Card 0 (CP2ET1)
lappend CP2_outbits [Signals::CP2ET2 getaspect];# Output Port B, Card 0 (CP2ET2)
lappend CP2_outbits [Signals::CP2WT1 getaspect];# Output Port C, Card 0 (CP2WT1)
lappend CP2_outbits [Signals::CP2WT2 getaspect];# Output Port A, Card 2 (CP2WT2)
# Output Port B, Card 2 (Switch motor bits SW2a and SW2b)
lappend CP2_outbits [expr {[Switches::SW2a motorbits] | \
[Switches::SW2b motorbits] << 2}]
lappend CP2_outbits 0;# Output Port C, Card 2 (unused)
# Write all output ports
eval [list CP1 outputs] $CP1_outbits
eval [list CP2 outputs] $CP2_outbits
update;# Update display
}
# Main Loop End

And the I/O Worksheet:

SMINI @ UA 0 (CP1)
Card No. 0 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Upper Green \ |
| | 1 | 2 | Upper Yellow | CP1ET1 Signal |
| | 2 | 3 | Upper Red | |
| A | 3 | 4 | Lower Yellow | |
| | 4 | 5 | Lower Red / |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | Upper Green \ |
| | 1 |10 | Upper Yellow | CP1ET2 Signal |
| | 2 |11 | Upper Red | |
| B | 3 |12 | Lower Yellow | |
| | 4 |13 | Lower Red / |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | Upper Green \ |
| | 1 |18 | Upper Yellow | CP1WT1 Signal |
| | 2 |19 | Upper Red | |
| C | 3 |20 | Lower Yellow | |
| | 4 |21 | Lower Red / |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 1 Input
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Block 1, Track 1 Occupation Detector |
| | 1 | 2 | Block 1, Track 2 Occupation Detector |
| | 2 | 3 | Block 3, Track 1 Occupation Detector |
| A | 3 | 4 | OS 1 Occupation Detector |
| | 4 | 5 | Normal \ Switch 1a |
| | 5 | 6 | Reverse / state contacts |
| | 6 | 7 | Normal \ Switch 1a |
| | 7 | 8 | Reverse / state contacts |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 2 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Upper Green \ |
| | 1 | 2 | Upper Yellow | CP1WT2 Signal |
| | 2 | 3 | Upper Red | |
| A | 3 | 4 | Lower Yellow | |
| | 4 | 5 | Lower Red / |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | Normal \ Switch 1a |
| | 1 |10 | Reverse / motor |
| | 2 |11 | Normal \ Switch 1b |
| B | 3 |12 | Reverse / motor |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
SMINI @ UA 1 (CP2)
Card No. 0 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Upper Green \ |
| | 1 | 2 | Upper Yellow | CP2ET1 Signal |
| | 2 | 3 | Upper Red | |
| A | 3 | 4 | Lower Yellow | |
| | 4 | 5 | Lower Red / |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | Upper Green \ |
| | 1 |10 | Upper Yellow | CP2ET2 Signal |
| | 2 |11 | Upper Red | |
| B | 3 |12 | Lower Yellow | |
| | 4 |13 | Lower Red / |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | Upper Green \ |
| | 1 |18 | Upper Yellow | CP2WT1 Signal |
| | 2 |19 | Upper Red | |
| C | 3 |20 | Lower Yellow | |
| | 4 |21 | Lower Red / |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 1 Input
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Block 5, Track 1 Occupation Detector |
| | 1 | 2 | Block 5, Track 2 Occupation Detector |
| | 2 | 3 | Block 3, Track 2 Occupation Detector |
| A | 3 | 4 | OS 2 Occupation Detector |
| | 4 | 5 | Normal \ Switch 2a |
| | 5 | 6 | Reverse / state contacts |
| | 6 | 7 | Normal \ Switch 2b |
| | 7 | 8 | Reverse / state contacts |
--------------------------------------------------------------
| | 0 | 9 | |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 2 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Upper Green \ |
| | 1 | 2 | Upper Yellow | CP2WT2 Signal |
| | 2 | 3 | Upper Red | |
| A | 3 | 4 | Lower Yellow | |
| | 4 | 5 | Lower Red / |
| | 5 | 6 | |
| | 6 | 7 | |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | Normal \ Switch 2a |
| | 1 |10 | Reverse / motor |
| | 2 |11 | Normal \ Switch 2b |
| B | 3 |12 | Reverse / motor |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
EOF

Example 4: From Chapter 9 of C/MRI User's Manual V3.0

This example, shown below, implements the yard example from Chapter 9 of C/MRI User's Manual V3.0.

This example uses a single SMINI board. The physical push buttons are replaced by "virtual" push buttons on the computer screen. Otherwise, this code is a drop-in replacement, in Tcl under Linux, for the Quick BASIC code under MS-Windows included in Bruce Chubb's manual.

Here is the code:

# Add User code after this line
# From chapter 9 of Computer/Model Railroad Interface User's Manual V3.0.
# See example4.iow for I/O connections for this example.
namespace eval Groups {
# Radio groups (from push buttons)
snit::type Group {
option -buttonmap -readonly yes -default {}
variable value
constructor {args} {
$self configurelist $args
$self setvalue {}
}
method getvalue {} {return $value}
method setvalue {newvalue} {
set value $newvalue
foreach {b v} $options(-buttonmap) {
if {[string equal "$v" "$value"]} {
MainWindow ctcpanel seti $b I on
} else {
MainWindow ctcpanel seti $b I off
}
}
}
}
Group TrackSelect -buttonmap {PB1 Track1 PB2 Track2 PB3 Track3 PB4 Track4
PB5 Track5 PB6 Track6}
}
namespace eval Signals {
# Signal types. Encapsulates a signal's aspect.
# Descrete Led types, three aspect (Red, Yellow, Green)
# See: Fig 3-5 of The Computer / Model Railroad Interface User's Manual V3.0
snit::type OneHead {
# Single head signals have four states: dark, green, yellow, or red.
typevariable aspects -array {
Dark 0x00
Green 0x01
Yellow 0x02
Red 0x04
}
option -signal -default {}
variable aspectbits
constructor {args} {
$self configurelist $args
set aspectbits $aspects(Dark)
if {[string length $options(-signal)] > 0} {
MainWindow ctcpanel setv $options(-signal) dark
}
}
method setaspect {a} {
set aspectbits $aspects($a)
if {[string length $options(-signal)] > 0} {
switch $a {
Dark {MainWindow ctcpanel setv $options(-signal) dark}
Green {MainWindow ctcpanel setv $options(-signal) green}
Yellow {MainWindow ctcpanel setv $options(-signal) yellow}
Red {MainWindow ctcpanel setv $options(-signal) red}
}
}
}
method getaspect {} {return $aspectbits}
}
snit::type TwoHead {
# Two head signals have five states: dark, green over red,
# yellow over red, red over yellow, and red over red.
typevariable aspects -array {
Dark 0x00
GreenRed 0x11
YellowRed 0x12
RedYellow 0x0c
RedRed 0x14
}
option -signal -default {}
variable aspectbits
constructor {args} {
$self configurelist $args
set aspectbits $aspects(Dark)
if {[string length $options(-signal)] > 0} {
MainWindow ctcpanel setv $options(-signal) dark
}
}
method setaspect {a} {
set aspectbits $aspects($a)
if {[string length $options(-signal)] > 0} {
switch $a {
Dark {MainWindow ctcpanel setv $options(-signal) dark}
GreenRed {MainWindow ctcpanel setv $options(-signal) {green red}}
YellowRed {MainWindow ctcpanel setv $options(-signal) {yellow red}}
RedYellow {MainWindow ctcpanel setv $options(-signal) {red yellow}}
RedRed {MainWindow ctcpanel setv $options(-signal) {red red}}
}
}
}
method getaspect {} {return $aspectbits}
}
snit::type ThreeHead {
# Three head signals have six states: dark, green over red over red,
# yellow over red over red, red over yellow over red, red over red
# over yellow, and red over red over red.
typevariable aspects -array {
Dark 0x00
GreenRedRed 0x51
YellowRedRed 0x52
RedYellowRed 0x4c
RedRedYellow 0x34
RedRedRed 0x54
}
option -signal -default {}
variable aspectbits
constructor {args} {
$self configurelist $args
set aspectbits $aspects(Dark)
if {[string length $options(-signal)] > 0} {
MainWindow ctcpanel setv $options(-signal) dark
}
}
method setaspect {a} {
set aspectbits $aspects($a)
if {[string length $options(-signal)] > 0} {
switch $a {
Dark {MainWindow ctcpanel setv $options(-signal) dark}
GreenRedRed {MainWindow ctcpanel setv $options(-signal) {green red red}}
YellowRedRed {MainWindow ctcpanel setv $options(-signal) {yellow red red}}
RedYellowRed {MainWindow ctcpanel setv $options(-signal) {red yellow red}}
RedRedYellow {MainWindow ctcpanel setv $options(-signal) {red red yellow}}
RedRedRed {MainWindow ctcpanel setv $options(-signal) {red red red}}
}
}
}
method getaspect {} {return $aspectbits}
}
OneHead SIG2RF -signal SIG2RF
OneHead SIG2RE -signal SIG2RE
OneHead SIG2RD -signal SIG2RD
OneHead SIG2RC -signal SIG2RC
OneHead SIG2RB -signal SIG2RB
OneHead SIG2RA -signal SIG2RA
TwoHead SIG2LAB -signal SIG2LAB
OneHead SIG4LA -signal SIG4LA
OneHead SIG4RA -signal SIG4RA
}
namespace eval Blocks {
# Block type (general trackwork).
# Encapsulates block occupation detectors.
#
snit::type Block {
# Occupation state values
typevariable OCC 1
typevariable CLR 0
# Occupation state bit
variable occupiedbit
constructor {} {
set occupiedbit $CLR;# Initialize to clear.
}
# Occupation state methods
method occupiedp {} {return [expr {$occupiedbit == $OCC}]}
method setoccupied {value} {
set occupiedbit $value
}
}
Block OS1;# Shared by all switches
Block BK1
Block BK2
Block BK3
Block BK4
Block BK5
Block BK6
Block BK7
Block BK8
}
namespace eval Switches {
# Switch type (turnout)
# Encapsulates a switch (turnout), including its OS (delegated to a Block
# object), its switch motor, and its point position sensor (its state).
snit::type Switch {
component block;# OS section
delegate method * to block;# Delegate block methods
variable state unknown;# Sense state (point position)
# Motor bit values
typevariable NOR 1;# 01
typevariable REV 2;# 10
variable motor;# Motor bits -- used to drive switch
# motor.
constructor {} {
# Install OS section
install block using Blocks::Block %AUTO%
# Initialize motor bits
set motor $NOR
}
# State methods
method getstate {} {return $state}
method setstate {statebits} {
if {$statebits == $NOR} {
set state normal
} elseif {$statebits == $REV} {
set state reverse
} else {
set state unknown
}
}
# Motor bit methods
method motorbits {} {return $motor}
method setmotor {mv} {
switch -exact $mv {
normal {set motor $NOR}
reverse {set motor $REV}
}
}
}
Switch SM1
Switch SM2
Switch SM3
Switch SM4
Switch SM5
}
# Main Loop Start
while {true} {
# Read all ports
set Butte_inbits [Butte inputs]
# Unpack input bits
set tempbyte [lindex $Butte_inbits 0];# Port A: Blocks 1-7, OS1
Blocks::BK1 setoccupied [expr {$tempbyte & 0x01}]
Blocks::BK2 setoccupied [expr {$tempbyte >> 1 & 0x01}]
Blocks::BK3 setoccupied [expr {$tempbyte >> 2 & 0x01}]
Blocks::BK4 setoccupied [expr {$tempbyte >> 3 & 0x01}]
Blocks::BK5 setoccupied [expr {$tempbyte >> 4 & 0x01}]
Blocks::BK6 setoccupied [expr {$tempbyte >> 5 & 0x01}]
Blocks::OS1 setoccupied [expr {$tempbyte >> 6 & 0x01}]
Blocks::BK7 setoccupied [expr {$tempbyte >> 7 & 0x01}]
set tempbyte [lindex $Butte_inbits 1];# Port B: Block 8
Blocks::BK8 setoccupied [expr {$tempbyte & 0x01}]
# Invoke all trackwork and get occupicency
MainWindow ctcpanel invoke OS1a
MainWindow ctcpanel invoke OS1b
MainWindow ctcpanel invoke OS1c
MainWindow ctcpanel invoke OS1d
MainWindow ctcpanel invoke OS1e
MainWindow ctcpanel invoke OS1f
MainWindow ctcpanel invoke OS1g
MainWindow ctcpanel invoke OS1h
MainWindow ctcpanel invoke OS1i
MainWindow ctcpanel invoke OS1j
MainWindow ctcpanel invoke OS1k
MainWindow ctcpanel invoke OS1l
MainWindow ctcpanel invoke OS1m
MainWindow ctcpanel invoke OS1n
MainWindow ctcpanel invoke BK1
MainWindow ctcpanel invoke BK2
MainWindow ctcpanel invoke BK3
MainWindow ctcpanel invoke BK4
MainWindow ctcpanel invoke BK5
MainWindow ctcpanel invoke BK6
MainWindow ctcpanel invoke BK7
MainWindow ctcpanel invoke BK8
# Process switch machines
if {![Blocks::OS1 occupiedp]} {
switch [::Groups::TrackSelect getvalue] {
Track1 {Switches::SM4 setmotor normal;Switches::SM5 setmotor normal}
Track2 {Switches::SM4 setmotor reverse;Switches::SM5 setmotor normal}
Track3 {Switches::SM5 setmotor reverse;Switches::SM3 setmotor reverse}
Track4 {Switches::SM5 setmotor reverse;Switches::SM3 setmotor normal;
Switches::SM2 setmotor normal}
Track5 {Switches::SM5 setmotor reverse;Switches::SM3 setmotor normal;
Switches::SM2 setmotor reverse;Switches::SM1 setmotor reverse}
Track6 {Switches::SM5 setmotor reverse;Switches::SM3 setmotor normal;
Switches::SM2 setmotor reverse;Switches::SM1 setmotor normal}
}
}
MainWindow ctcpanel invoke SM1
MainWindow ctcpanel invoke SM2
MainWindow ctcpanel invoke SM3
MainWindow ctcpanel invoke SM4
MainWindow ctcpanel invoke SM5
# Compute direction of traffic
if {[Block::OS1 occupiedp]} {
set dot1 EastBound
} else {
set dot1 Westbound
}
# Calculate SIG4RA
Signals::SIG4RA setaspect Red
if {$dot1 ne WestBound && ![Block::BK8 occupiedp]} {
Signals::SIG4RA setaspect Green
}
# Calculate Butte exit signals
Signals::SIG2RA setaspect Red
Signals::SIG2RB setaspect Red
Signals::SIG2RC setaspect Red
Signals::SIG2RD setaspect Red
Signals::SIG2RE setaspect Red
Signals::SIG2RF setaspect Red
set ExitSig Red
if {$dot1 ne WestBound && ![Blocks::OS1 occupiedp] && ![Blocks::BK7 occupiedp]} {
set ExitSig Yellow
if {[MainWindow ctcpanel getv SIG4RA] ne red} {set ExitSig Green}
switch [::Groups::TrackSelect getvalue] {
Track1 {Signals::SIG2RA setaspect $ExitSig}
Track2 {Signals::SIG2RB setaspect $ExitSig}
Track3 {Signals::SIG2RC setaspect $ExitSig}
Track4 {Signals::SIG2RD setaspect $ExitSig}
Track5 {Signals::SIG2RE setaspect $ExitSig}
Track6 {Signals::SIG2RF setaspect $ExitSig}
}
}
Signals::SIG2LAB setaspect RedRed
if {![Blocks::OS1 occupiedp]} {
switch [::Groups::TrackSelect getvalue] {
Track1 {if {![Blocks::BK1 occupiedp]} {Signals::SIG2LAB setaspect YellowRed}}
Track2 {if {![Blocks::BK2 occupiedp]} {Signals::SIG2LAB setaspect RedYellow}}
Track3 {if {![Blocks::BK3 occupiedp]} {Signals::SIG2LAB setaspect RedYellow}}
Track4 {if {![Blocks::BK4 occupiedp]} {Signals::SIG2LAB setaspect RedYellow}}
Track5 {if {![Blocks::BK5 occupiedp]} {Signals::SIG2LAB setaspect RedYellow}}
Track6 {if {![Blocks::BK5 occupiedp]} {Signals::SIG2LAB setaspect RedYellow}}
}
}
Signals::SIG4LA setaspect Red
if {$dot1 ne EastBound && ![Blocks::BK7 occupiedp]} {
Signals::SIG4LA setaspect Yellow
if {[MainWindow ctcpanel getv SIG2LAB] ne {red red}} {
Signals::SIG4LA setaspect Green
}
}
# Pack output bits
# Port A, Card 0:
set Butte_outbits [expr {[Signals::SIG2LAB getaspect] | \
[Signals::SIG2RA getaspect] << 5}]
# Port B, Card 0:
lappend Butte_outbits [expr {[Signals::SIG2RB getaspect] | \
[Signals::SIG2RC getaspect] << 3 | \
[Switches::SM1 motorbits] << 6}]
# Port C, Card 0:
lappend Butte_outbits [expr {[Signals::SIG2RD getaspect] | \
[Signals::SIG2RE getaspect] << 3 | \
[Switches::SM2 motorbits] << 6}]
# Port A, Card 2:
lappend Butte_outbits [expr {[Signals::SIG2RF getaspect] | \
[Switches::SM3 motorbits] << 3 | \
[Switches::SM4 motorbits] << 5}]
# Port B, Card 2:
lappend Butte_outbits [expr {[Signals::SIG4LA getaspect] | \
[Signals::SIG4RA getaspect] << 3 | \
[Switches::SM5 motorbits] << 6}]
# Port C, Card 2:
lappend Butte_outbits 0
# Write all output ports
eval [list Butte outputs] $Butte_outbits
update;# Update display
}
# Main Loop End

And the I/O Worksheet:

SMINI @ UA 0 (Butte)
Card No. 0 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Upper Green \ |
| | 1 | 2 | Upper Yellow | SIG2LAB Signal |
| | 2 | 3 | Upper Red | |
| A | 3 | 4 | Lower Yellow | |
| | 4 | 5 | Lower Red / |
| | 5 | 6 | Green \ |
| | 6 | 7 | Yellow | SIG2RA |
| | 7 | 8 | Red / |
--------------------------------------------------------------
| | 0 | 9 | Green \ |
| | 1 |10 | Yellow | SIG2RB Signal |
| | 2 |11 | Red / |
| B | 3 |12 | Green \ |
| | 4 |13 | Yellow | SIG2RC Signal |
| | 5 |14 | Red / |
| | 6 |15 | Normal \ SM1 |
| | 7 |16 | Reverse / |
--------------------------------------------------------------
| | 0 |17 | Green \ |
| | 1 |18 | Yellow | SIG2RD Signal |
| | 2 |19 | Red / |
| C | 3 |20 | Green \ |
| | 4 |21 | Yellow | SIG2RE Signal |
| | 5 |22 | Red / |
| | 6 |23 | Normal \ SM2 |
| | 7 |24 | Reverse / |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 1 Input
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | BK1 Occupation Detector |
| | 1 | 2 | BK2 Occupation Detector |
| | 2 | 3 | BK3 Occupation Detector |
| A | 3 | 4 | BK4 Occupation Detector |
| | 4 | 5 | BK5 Occupation Detector |
| | 5 | 6 | BK6 Occupation Detector |
| | 6 | 7 | OS1 Occupation Detector |
| | 7 | 8 | BK7 Occupation Detector |
--------------------------------------------------------------
| | 0 | 9 | BK8 Occupation Detector |
| | 1 |10 | |
| | 2 |11 | |
| B | 3 |12 | |
| | 4 |13 | |
| | 5 |14 | |
| | 6 |15 | |
| | 7 |16 | |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------
Card No. 2 Output
--------------------------------------------------------------
|PORT|BIT|PIN|DESCRIPTION OF FUNCTION PERFORMED |
--------------------------------------------------------------
| | 0 | 1 | Green \ |
| | 1 | 2 | Yellow | SIG2RF Signal |
| | 2 | 3 | Red / |
| A | 3 | 4 | Normal \ SM3 |
| | 4 | 5 | Reverse / |
| | 5 | 6 | Normal \ SM4 |
| | 6 | 7 | Reverse / |
| | 7 | 8 | |
--------------------------------------------------------------
| | 0 | 9 | Green \ |
| | 1 |10 | Yellow | SIG4LA Signal |
| | 2 |11 | Red / |
| B | 3 |12 | Green \ |
| | 4 |13 | Yellow | SIG4RA Signal |
| | 5 |14 | Red / |
| | 6 |15 | Normal \ SM5 |
| | 7 |16 | Reverse / |
--------------------------------------------------------------
| | 0 |17 | |
| | 1 |18 | |
| | 2 |19 | |
| C | 3 |20 | |
| | 4 |21 | |
| | 5 |22 | |
| | 6 |23 | |
| | 7 |24 | |
--------------------------------------------------------------
| | 0 |25 | |
| | 1 |26 | |
| | 2 |27 | |
| D | 3 |28 | |
| | 4 |29 | |
| | 5 |30 | |
| | 6 |31 | |
| | 7 |32 | |
--------------------------------------------------------------