Welcome to TrainThing’s Laptop documentation!

Todo

This is just the beginning

TT_4

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

//TODO: need to add tkinter and traffic classes

from tkinter import * from tkinter import ttk import client

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 (WiFi 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

Establish internet client (WiFi LAN) connection with Raspberry Pi server

FORMAT

encoding format

__init__() None

Initialize link to Raspberry Pi. Start thread to receive responses

Constants:

_receive() None

Thread to receive responses from RPi/Arduino

get_response() str
send(msg: str) None

Send command to Raspberry Pi/Arduino

GUI

This is the top level module for the Laptop including Tkinter GUI

//TODO: GUI is just a hello world

GUI.main()

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.

globals.restart() None

Think I will need this to restart a run

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

set_14() new_sensor()

__init__() None
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

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.

Signals

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

class signals.Signals

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

T_clear = 1
T_thrown = 2
__init__() 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:

static _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

_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

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

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

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

locked = []
run_control = True
signal_change = [[4, 1, [['B', 13], ['T', 3, 1], ['B', 20]]], [6, 1, [['T', 3, 1], ['B', 20], ['B', 19]]]]
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]]]]
state_change() None
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() None

update all signals by stepping through signal_control

Train

Four instances of this module will represent each of the 4 trains operating as part of TrainThing

class train.Train(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__(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’)

static _available(key: str) bool

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

_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:

static _check_turnouts(key: str) bool

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

_find_key() str

find the key into requirements :return str: key

static _find_target(path: int) tuple

look for the given path in globals,target :param path: :return: tuple of found path, direction

static _release_assets(key: str) None

use key to set assets required to false

_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:

static abort() None

stop the train :return: None

run() None

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

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

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

Turnout

This module sends all commands to change turnouts.

//TODO: testing needed

class turnout.Turnout

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”)

static clear() None

Set all 12 turnouts to CLEAR <T 120 0>

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>'}
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

Indices and tables