Welcome to TrainThing’s Laptop documentation!

Note

Under Active Development.

Todo

TT_4 Add first train.

TT_4

This is the top level software for the Laptop, part (4), of the TrainThing

TT_4.main()

Setup link to trainThing get status of all sensors call set_signals to set all signals based on sensors/tracks/turnouts watch link for changes in sensors :return:

Module client

This module provides: A socket client for the internet (Wi-Fi LAN) connection to the Raspberry Pi (3), running TrainThing software, and on to the Arduino (2), running the DCC++ Base Station. All DCC++ and TrainThing commands strings passed to this module are sent to the Raspberry Pi. All received status messages are converted from bytes to strings bounded by “<” and “>”.

class client.Client(gg)

Establish internet client (Wi-Fi LAN) connection with Raspberry Pi server send ~~ send a message to RPI or SB get ~~ get a response from the RPI que if available -> string

__init__(gg) None

Initialize link to Raspberry Pi. Start thread to receive responses

Constants:

FORMAT

encoding format

_send() None

Send command to Raspberry Pi/Arduino

send(msg: str) None
_receive() None

Thread to receive responses from RPi/Arduino

get() str
stop_client() None

Globals

This module defines the global variables that reflect the status of TrainThing physical assets, blocks of track, turnouts and the Crossover.

sys_state -> the current state of the TrainThing system path_names -> string name of each path paths -> the blocks, in order, for each CW path in path_names order requirements -> assets required to move for one block to the next in a path.

Constants:

globals.B_not: int = 0

(int) Block of track not in use.

globals.B_staging: int = 4

(int) Block 14 is being used as the staging yard.

globals.T_unk: int = 0

(int) Turnout is in an unknown state

globals.T_clear: int = 1

(int) Turnout is in the CLEAR state (mainline path)

globals.T_thrown: int = 2

(int) Turnout is in the THROWN state (divergent path)

globals.X_not: int = 0

(int) the crossover is not in use

globals.X_upLeft: int = 1

(int) the crossover is in use for track blocks 7 & 9

globals.X_upRight: int = 2

(int) the crossover is in use for track blocks 8 & 10

globals.sys_state = 0

(int) The current state of TrainThing

globals.No_train = 0

(int) No loco

globals.RED = 1

(int) Order of the red loco

globals.ORANGE = 2

(int) Order of the orange loco

globals.YELLOW = 3

(int) Order of the yellow loco

globals.GREEN = 4

(int) Order of the green loco

globals.BIGO = 1

index into path_names and paths for the Big Oval

globals.SMALLO = 2

index into path_names and paths for the Small Oval

globals.BIG8 = 3

index into path_names and paths for the Big Figure-8

globals.SMALL8 = 4

index into path_names and paths for the Small Figure-8

globals.BEGINNING = 5

index into path_names and paths for exiting the staging yard

globals.ENDING = 6

index into path_names and paths for returning to the staging yard

globals.GTOUR = 7

for testing a grand tour

globals.requirements = {'10>15': [['T', 11, 2], ['T', 12, 2], ['T', 4, 2], ['B', 15], ['B', 16]], '10>5': [['T', 11, 2], ['T', 12, 1], ['B', 5], ['B', 6]], '10>8': [['X', 2], ['B', 8]], '11>12': [['T', 2, 1], ['B', 12], ['B', 13]], '11>16': [['T', 1, 1], ['B', 16], ['B', 15]], '12>1': [['T', 2, 2], ['T', 8, 2], ['T', 7, 1], ['B', 1]], '12>11': [['T', 2, 1], ['B', 11]], '12>13': [['B', 13]], '12>8': [['T', 2, 2], ['T', 8, 2], ['T', 7, 2], ['B', 8], ['B', 10]], '13>12': [['B', 12]], '13>14': [['T', 3, 1], ['B', 14]], '13>20': [['T', 3, 1], ['B', 20]], '13>4': [['T', 3, 2], ['T', 9, 2], ['T', 10, 1], ['B', 4]], '13>9': [['T', 3, 2], ['T', 9, 2], ['T', 10, 2], ['B', 9], ['B', 7]], '14>13': [['T', 3, 1], ['B', 13], ['B', 12]], '14>15': [['T', 4, 1], ['B', 15], ['B', 16]], '15>10': [['T', 4, 2], ['T', 12, 2], ['T', 11, 2], ['B', 10], ['B', 8]], '15>14': [['T', 4, 1], ['B', 14]], '15>16': [['B', 16]], '15>4': [['T', 4, 2], ['T', 12, 2], ['T', 11, 1], ['B', 4]], '16>1': [['T', 1, 2], ['T', 5, 2], ['T', 6, 1], ['B', 1]], '16>11': [['T', 1, 1], ['B', 11]], '16>15': [['B', 15]], '16>7': [['T', 1, 2], ['T', 5, 2], ['T', 6, 2], ['B', 7], ['B', 9]], '17>15': [['T', 4, 1], ['B', 15]], '18>17': [['B', 17]], '19>18': [['B', 18]], '1>12': [['T', 7, 1], ['T', 8, 2], ['T', 2, 2], ['B', 12], ['B', 13]], '1>16': [['T', 6, 1], ['T', 5, 2], ['T', 1, 2], ['B', 16], ['B', 15]], '1>2': [['T', 7, 1], ['T', 8, 1], ['B', 2], ['B', 3]], '1>6': [['T', 6, 1], ['T', 5, 1], ['B', 6], ['B', 5]], '20>19': [['B', 19]], '2>1': [['T', 8, 1], ['T', 7, 1], ['B', 1]], '2>3': [['B', 3]], '2>8': [['T', 8, 1], ['T', 7, 2], ['B', 8], ['B', 10]], '3>2': [['B', 2]], '3>4': [['T', 9, 1], ['T', 10, 1], ['B', 4]], '3>9': [['T', 9, 1], ['T', 10, 2], ['B', 9], ['B', 7]], '4>13': [['T', 10, 1], ['T', 9, 2], ['T', 3, 2], ['B', 13], ['B', 12]], '4>15': [['T', 11, 1], ['T', 12, 2], ['T', 4, 2], ['B', 15], ['B', 16]], '4>3': [['T', 10, 1], ['T', 9, 1], ['B', 3], ['B', 2]], '4>5': [['T', 11, 1], ['T', 12, 1], ['B', 5], ['B', 6]], '5>10': [['T', 12, 1], ['T', 11, 2], ['B', 10], ['B', 8]], '5>4': [['T', 12, 1], ['T', 11, 1], ['B', 4]], '5>6': [['B', 6]], '6>1': [['T', 5, 1], ['T', 6, 1], ['B', 1]], '6>5': [['B', 5]], '6>7': [['T', 5, 1], ['T', 6, 2], ['B', 7], ['B', 9]], '7>16': [['T', 6, 2], ['T', 5, 2], ['T', 1, 2], ['B', 16], ['B', 15]], '7>6': [['T', 6, 2], ['T', 5, 1], ['B', 6], ['B', 5]], '7>9': [['X', 1], ['B', 9]], '8>10': [['X', 2], ['B', 10]], '8>12': [['T', 7, 2], ['T', 8, 2], ['T', 2, 2], ['B', 12], ['B', 13]], '8>2': [['T', 7, 2], ['T', 8, 1], ['B', 2], ['B', 3]], '9>13': [['T', 10, 2], ['T', 9, 2], ['T', 3, 2], ['B', 13], ['B', 12]], '9>3': [['T', 10, 2], ['T', 9, 1], ['B', 3], ['B', 2]], '9>7': [['X', 1], ['B', 7]]}

Assets required to move from one block to the next requirements[‘a>b’][array of assets]

globals.restart() None

Think I will need this to restart a run

globals.ir_update(status: str) None

Receives sensor status message from traffic/client and changes the state in IR_status, block_status and turnout_status :param status: string <Qnn> | <qnn> :return: None

Layout

This module defines the graphic functions and procedures to draw the TrainThing layout using the current status of blocks of track, turnouts, the crossover, and trains location as reflected in module globals.

class layout.Layout(gg)
__init__(gg)

Defines: self.xover, self.turnout[], self.block[], self.signal[t/f,[x,y],[x,y]] and self.train[]

start_graph()
update()

Do forever loop polling displaying the current status of blocks of track, turnouts, the crossover and the trains.

set_signal(pol: int, top: bool, aspect: int) None
stop()
layout.random() x in the interval [0, 1).

Sensors

This module receives all changes in sensor status for TrainThing and sets the status of assets for global access //TODO: Needs testing

class sensors.Sensors(gg, cc, lout)

set_14() new_sensor()

__init__(gg, cc, lout) None
set_14(inuse: bool) None

when the staging yard is in use, sys_state 0,1,2 & 6 track block 14 is blocked. This controls signals 4,6,9,11 top - ‘blocking’ access to track block 14, protecting the staging yard.

new_sensor(msg: str) None

When the client module receives a <Q ID> or <q ID> status response from TrainThing the message is passed here. The status of the corresponding block of track or turnout is updated. parse of Sensors ID 1 - 32 relate to track blocks 1 - 16 Sensors ID 33 - 36 relate to staging yard parking places (blocks) 17 - 20 parse of sensors ID 51 - 74 relate to turnouts 1 - 12 :param msg: <Q sensor #> went high or <q sensor #> went low :return: None

Signals

This module sets one or all signals based on status of blocks, turnouts and crossover. //TODO: testing needed

class signals.Signals(gg, cc, lout)

lock/unlock used in sys_state 0-1 to hold signals 8 and 33 red lock(pole, top, aspect) force a signal to the given aspect Unlock(pole, top) Release signal update(pole, top) update a signal update_all() update all signals changed(‘TBX’, #) asset changed. update all related signals state_change() system state changed. change signal requirements _valid(pole, top, aspect) check for valid input parameters _one(pole, top) internal evaluate aspect of a signal _set(pole, top, aspect) internal Send command to change aspect of a signal

signal_control = [[], [False, [['T', 1, 1], ['B', 16], ['B', 15]]], [False, [['T', 2, 1], ['B', 12], ['B', 13]]], [True, [['T', 2, 1], ['B', 11], ['T', 1, 1], ['B', 16]], [['T', 2, 2], ['T', 8, 2], ['T', 7, 2], ['B', 8], ['X', 2], ['B', 10]]], [True, [['B', 13], ['T', 3, 1], ['B', 14]], [['B', 13], ['T', 3, 2], ['T', 9, 2], ['T', 10, 2], ['B', 9]]], [True, [['B', 12], ['T', 2, 1], ['B', 11]], [['B', 12], ['T', 2, 2], ['T', 8, 2], ['T', 7, 2], ['B', 8]]], [True, [['T', 3, 1], ['B', 14], ['T', 4, 1], ['B', 15]], [['T', 3, 2], ['T', 9, 2], ['T', 10, 2], ['B', 9], ['X', 1], ['B', 7]]], [False, [['T', 3, 1], ['B', 13], ['B', 12]]], [False, [['T', 4, 1], ['B', 15], ['B', 16]]], [True, [['T', 4, 1], ['B', 14], ['T', 3, 1], ['B', 13]], [['T', 4, 2], ['T', 12, 2], ['T', 11, 2], ['B', 10], ['X', 2], ['B', 8]]], [True, [['B', 16], ['T', 1, 1], ['B', 11]], [['B', 16], ['T', 1, 2], ['T', 5, 2], ['T', 6, 2], ['B', 7]]], [True, [['B', 15], ['T', 4, 1], ['B', 14]], [['B', 15], ['T', 4, 2], ['T', 12, 2], ['T', 11, 2], ['B', 10]]], [True, [['T', 1, 1], ['B', 11], ['T', 2, 1], ['B', 12]], [['T', 1, 2], ['T', 5, 2], ['T', 6, 2], ['B', 7], ['X', 1], ['B', 9]]], [True, [['T', 6, 1], ['T', 5, 1], ['B', 6], ['B', 5]], [['T', 6, 1], ['T', 5, 2], ['T', 1, 2], ['B', 16], ['B', 15]]], [True, [['T', 7, 1], ['T', 8, 1], ['B', 2], ['B', 3]], [['T', 7, 1], ['T', 8, 2], ['T', 2, 2], ['B', 12], ['B', 13]]], [True, [['T', 8, 1], ['T', 7, 1], ['B', 1], ['T', 6, 1], ['T', 5, 1], ['B', 6]], [['T', 8, 1], ['T', 7, 2], ['B', 8], ['X', 2], ['B', 10]]], [True, [['B', 3], ['T', 9, 1], ['T', 10, 1], ['B', 4]], [['B', 3], ['T', 9, 1], ['T', 10, 2], ['B', 9]]], [True, [['B', 2], ['T', 8, 1], ['T', 7, 1], ['B', 1]], [['B', 2], ['T', 8, 1], ['T', 7, 2], ['B', 8]]], [True, [['T', 9, 1], ['T', 10, 1], ['B', 4], ['T', 11, 1], ['T', 12, 1], ['B', 5]], [['T', 9, 1], ['T', 10, 2], ['B', 9], ['X', 1], ['B', 7]]], [True, [['T', 10, 1], ['T', 9, 1], ['B', 3], ['B', 2]], [['T', 10, 1], ['T', 9, 2], ['T', 3, 2], ['B', 13], ['B', 12]]], [True, [['T', 11, 1], ['T', 12, 1], ['B', 5], ['B', 6]], [['T', 11, 1], ['T', 12, 2], ['T', 4, 2], ['B', 15], ['B', 16]]], [True, [['T', 12, 1], ['T', 11, 1], ['B', 4], ['T', 10, 1], ['T', 9, 1], ['B', 3]], [['T', 12, 1], ['T', 11, 2], ['B', 10], ['X', 2], ['B', 8]]], [True, [['B', 6], ['T', 5, 1], ['T', 6, 1], ['B', 1]], [['B', 6], ['T', 5, 1], ['T', 6, 2], ['B', 7]]], [True, [['B', 5], ['T', 12, 1], ['T', 11, 1], ['B', 4]], [['B', 5], ['T', 12, 1], ['T', 11, 2], ['B', 10]]], [True, [['T', 5, 1], ['T', 6, 1], ['B', 1], ['T', 7, 1], ['T', 8, 1], ['B', 2]], [['T', 5, 1], ['T', 6, 2], ['B', 7], ['X', 1], ['B', 9]]], [True, [['T', 6, 2], ['T', 5, 1], ['B', 6], ['B', 5]], [['T', 6, 2], ['T', 5, 2], ['T', 1, 2], ['B', 16], ['B', 15]]], [True, [['X', 1], ['B', 9], ['T', 10, 2], ['T', 9, 1], ['B', 3]], [['X', 1], ['B', 9], ['T', 10, 2], ['T', 9, 2], ['T', 3, 2], ['B', 13]]], [True, [['T', 7, 2], ['T', 8, 1], ['B', 2], ['B', 3]], [['T', 7, 2], ['T', 8, 2], ['T', 2, 2], ['B', 12], ['B', 13]]], [True, [['X', 2], ['B', 10], ['T', 11, 2], ['T', 12, 1], ['B', 5]], [['X', 2], ['B', 10], ['T', 11, 2], ['T', 12, 2], ['T', 4, 2], ['B', 15]]], [True, [['T', 10, 2], ['T', 9, 1], ['B', 3], ['B', 2]], [['T', 10, 2], ['T', 9, 2], ['T', 3, 2], ['B', 13], ['B', 12]]], [True, [['X', 1], ['B', 7], ['T', 6, 2], ['T', 5, 1], ['B', 6]], [['X', 1], ['B', 7], ['T', 6, 2], ['T', 5, 2], ['T', 1, 2], ['B', 16]]], [True, [['T', 11, 2], ['T', 12, 1], ['B', 5], ['B', 6]], [['T', 11, 2], ['T', 12, 2], ['T', 4, 2], ['B', 15], ['B', 16]]], [True, [['X', 2], ['B', 8], ['T', 7, 2], ['T', 8, 1], ['B', 2]], [['X', 2], ['B', 8], ['T', 7, 2], ['T', 8, 2], ['T', 2, 2], ['B', 12]]], [False, [['T', 4, 1], ['B', 15], ['B', 16]]], [False, [['B', 17], ['T', 4, 1], ['B', 15]]], [False, [['B', 18], ['B', 17]]], [False, [['B', 19], ['B', 18]]]]
signal_default = [[4, 1, [['B', 13], ['T', 3, 1], ['B', 14]]], [6, 1, [['T', 3, 1], ['B', 14], ['T', 4, 1], ['B', 15]]]]
signal_change = [[4, 1, [['B', 13], ['T', 3, 1], ['B', 20]]], [6, 1, [['T', 3, 1], ['B', 20], ['B', 19]]]]
counter = 0
run_control = True
OFF = 0
RED = 1
YELLOW = 2
GREEN = 3
__init__(gg, cc, lout) None
locked = []
state_change(new: int) None
_set(pole: int, top: bool, aspect: int) None

send command to Raspberry Pi to set a signal <K pole top aspect :param pole: :param top: :param aspect: :return: None

_one(pole: int, top: bool) None

check the assets identified for pole/top in signal_control, to define the aspect of the signal then call _set() :param pole: :param top:

_valid(pole: int, top: bool, aspect: int = 1) bool

check for valid parameters :param pole: Signal pole/tower number 1-36 :param top: True if top signal, False otherwise :param aspect: 1-red, 2-yellow, 3-green, 0-off (33-36 only) :return: True if parameters pole, top and aspect are valid, False otherwise

lock(pole: int, top: bool, aspect: int) bool

if parameters are valid . Lock a signal to aspect until unlocked :param pole: Signal pole/tower number 1-36 :param top: True if top signal, False if lower semaphore :param aspect: 1-RED, 2-YELLOW, 3-GREEN, 0-OFF (33-36 only) :return bool: True of locked, False if failed (already locked | not valid

unlock(pole: int, top: bool) bool

if pole/top is locked . removed from list, and set signal :param pole: :param top: :return bool: True if parameters have been locked, False otherwise

update(pole: int, top: bool) bool

Force the update of a given signal by calling _one() :param pole: :param top: :return: True if parameters are valid

update_all() bool

update all signals by stepping through signal_control

changed(btx: str, nnum: int = 0) None

The Block of track | Turnout | Crossover “num” has changed. Update affected signals listed in tables :param btx: :param nnum:

Turnout

This module sends all commands to change turnouts.

//TODO: testing needed

class turnout.Turnout(gg)

clear() Set all 12 turnouts to CLEAR set(key: str) key is a pointer into globals requirements, the key is used here as a pointer into rout to identify the Wabbit Smart Rout that will set all turnouts needed to move from block N to M (“N>M”)

__init__(gg)
rout = {'10>15': '<T 128 1>', '10>5': '<T 127 1>', '11>12': '<T 102 0>', '11>16': '<T 101 0>', '12>1': '<T 124 0>', '12>11': '<T 102 0>', '12>8': '<T 124 1>', '13>14': '<T 103 0>', '13>20': '<T 103 0>', '13>4': '<T 126 0>', '13>9': '<T 126 1>', '14>13': '<T 103 0>', '14>15': '<T 104 0>', '15>10': '<T 128 1>', '15>14': '<T 104 0>', '15>4': '<T 128 0>', '16>1': '<T 122 0>', '16>11': '<T 101 0>', '16>7': '<T 122 1>', '17>15': '<T 104 0>', '1>12': '<T 124 0>', '1>16': '<T 122 0>', '1>2': '<T 123 0>', '1>6': '<T 121 0>', '2>1': '<T 123 0>', '2>8': '<T 123 1>', '3>4': '<T 125 0>', '3>9': '<T 125 1>', '4>13': '<T 126 0>', '4>15': '<T 128 0>', '4>3': '<T 125 0>', '4>5': '<T 127 0>', '5>10': '<T 127 1>', '5>4': '<T 127 0>', '6>1': '<T 121 0>', '6>7': '<T 121 1>', '7>16': '<T 122 1>', '7>6': '<T 121 1>', '8>12': '<T 124 1>', '8>2': '<T 123 1>', '9>13': '<T 126 1>', '9>3': '<T 125 1>'}
static clear() None

Set all 12 turnouts to CLEAR <T 120 0>

set(key: str) bool

<T ID 0|1> 0-CLEAR 1-THROWN :param key: str index into globals.requirements and self.rout :return: bool True if turnout command sent, else False

Train

Each instance of this module will represent one of the 4 trains operating as part of TrainThing

class train.Train(gg, path: str, direction: str, color: str)

Each instance of this class manages the movement of one of the four trains.

__init__: init all self variables, and call setup. setup(path, direction, color): initialize all self variables for this train based on passed values _find_target(path) -> path, direction: _check_change() -> bool: changed _find_key() -> str: on > next _available() -> bool: _set_assets() -> bool: Need to wait _release_assets() -> None: clear required _check_turnouts() -> bool: when all set True _steps() -> None: run() -> None: moves a train based on the state of the train and the state of the system. Abort() -> None: ends this instance of the train clearing all variables.

_add() -> None: _new_path() -> None:

__init__(gg, path: str, direction: str, color: str) None

Initialize an instance of a train given the path to follow and the color (therefore the location) of the train :param path: Assigned target path for this train :param direction: Assigned direction for the target path (‘CW’, ‘CCW’) :param color: Assigned color for this train (‘red’, ‘orange’, ‘yellow’, ‘green’)

setup(path: str, direction: str, color: str) None

this is a comment :param path: :param direction: Assigned direction for the target path :param color: :return: None

_find_target(path: int) tuple

look for the given path in self.gg,target :param path: :return: tuple of found path, direction

_check_change() bool

if ‘on’ in paths[pointer+1] . if ‘on+1’ in pointer == paths[pointer+1] . switch . return true . else return false else return false :return bool True switched paths:

_find_key() str

find the key into requirements :return str: key

_available(key: str) bool

check if all assets are available if all available set all required :return bool: True if all assets are available

_release_assets(key: str) None

use key to set assets required to false

_check_turnouts(key: str) bool

check turnout status when all set as required return True else False :return bool:

_steps() None

0: Check/require assets if all available set required 1: Set turnouts and Xover as required 2: wait for turnouts to change 3: move to next block 4: check for train on next block then release assets, reset to 0 :return:

run() None

take next step based of train_path & sys_state train_path: meaning depends on which sys_state

static abort() None

stop the train :return: None

new_path

This module/function is included in module train only. Based on sys_state, train_path, and the target path and direction (CW) this function adds “the next” path to where the train needs to follow moving from the staging area, to its target path, and finally back to the staging area.

new_path._add(self, path: int, direction: bool) None

add a path & direction for this train

new_path._new_path(self) None

if the current path/direction = t_path/t_CW done else: add new next path to get to the target

if sys_state: 0,1 nothing 2 - if target is small oval or SMALL8 add SMALL8 CW 3 - head to target path/CW 4 - nothing 5 - head to BIG8 CCW 6 - in order ENDING

Indices and tables