A program consists of a number of declaration sections, in any order and an actions section, containing the required behaviour definition.
This defines only the part of the language relating to monitoring and controlling hardware. It does not include 'glass' throttles, control panels, animated mimic diagrams etc.
This is a formal definition of the language and is intended more as reference material rather than casual reading. The CTI documentation is far better at explaining the language than I can posibly achieve.
This definition includes my extensions to tcl that I have found to be necessary, including arrays and structures. These are explained below, but the rest of the language I leave to the very capable CTI documentation.
This definition uses the following conventions:
something = something-else
Meaning that the 'thing' on the left is the same as the thing on the right.
something = thing1 | thing 2
something is either a thing1 or a thing2
something = ( thing )*
something is any number of things (* means 0 or more, + means 1 or more, ? means 0 or 1 - in otherwords optional)
the parentheses ( and ) show exactly what is repeated
something = 'text'
something consists of exactly the indicated text
This is not a complete definition. If anyone is interested I can show you the formal definition that my application actually uses (and is therefore complete and accurate as of that moment).
A program can therefore be defined as:
comment = '{' text '}' # comments can be inserted anywhere
program = (section)* # meaning any number of sections
section = 'Sensors:' namelist # 'Sensors:' means literal text
| 'Signals:' signallist # | means alternative
| 'Controls:' namelist # the outputs to the hardware
| 'Constant:' constlist # named constants
| 'Variables:' varlist # internal data stores
| 'Actions:' statementlist
namelist = name ( ',' name )* # ie. a comma separated list of sensor names
signallist = signal ( ',' signal )*
signal = name ( '[' number ']' )? # optional bracketed number indicating
# how many wires feed this signal
constlist = const ( ',' const )*
const = name '=' number # to avoid using numbers in a program
varlist = variable ( ',' variable )*
variable = name
| name '[' number ']' # array declaration
| 'struct' '(' namelist ')=(' namelist ')' # see below
statementlist = statement ( statement )*
statement = 'WHEN' conditions 'DO' actions
| 'WHILE' conditions 'DO' actions
| 'FOREVER' 'DO' actions
conditions = andlist ( 'OR' andlist )*
andlist = cond ( 'AND' cond )*
cond = rhs condop rhs
| '$LeftMouse=' xyz
| '$RightMouse=' xyz
| name '=' rhs # name is a QKey
condop = '<' | '<=' | '<>' | '>=' | '>'
xyz = '(' rhs ',' rhs ',' rhs ')' # x,y,z position on CTC panels
actions = action ( ',' action )*
action = lhs assignop rhs
| lhs '=' rhs ( modifyop )?
| 'wait' number
| lhs '=' 'pulse' number
| lhs '=' '&' rhs # take address of rhs
| '$Session=' rhs # set session time
| '$Time=' rhs # set layout time
| 'wait' delay
| 'IF' conditions 'THEN' actions
( 'ELSEIF' conditions 'THEN' actions )*
( 'ELSE' actions )? 'ENDIF'
| drawMessage | eraseMessage
| drawImage | moveImage | eraseImage
| colourText | colourTrack | colourBlock
| switch
assignop = '|=' # arithmetic OR
| '&=' # arithmetic AND
| '+=' # add rhs to lhs
| '-=' # subtract rhs from lhs
| '*=' | '/=' # multiply, divide
| '#=' # remainder on division by rhs
| '%=' # percentage, lhs%=50 is same as lhs/=2
modifyop = '+' | '-' | '*' | '/' | '%' | '&' | '|' | '#'
delay = floatingpoint # delay in seconds such as 3.5
| rhs # Variable delays are in milliseconds
rhs = name # read-only item such as constant or sensor
| lhs
| 'pulse' delay
| '"' string '"'
| drawImage
| cabSettings
lhs = name # read-write item such as control, signal or variable
| name '[' rhs ']' # an element of an array
| name '.' name # an element in a structure
| '*' lhs # dereferencing a variable that is a pointer
cabSettings = speed ( cabModifier )*
cabModifier = 'momentum_' 0..7
| 'brakeon' | 'brakeoff' | 'forward' | 'reverse'There are a few extensions I have made to the tcl used by the CTI software. These are constants, strings, arrays, structures and objects.
Constants
CTI's tcl did not provide for named constants and so I would use code such as:
WHEN status=7 { booked }, sensor=TRUE DO status=8 { arriving }I would insert the english meaning of the numbers I was using, but of course this was a bit of a drag. I also wanted to provide a window showing the values of these variables, but just as a numeric value but in english if appropriate (so status might display 'booked' or 'arriving', rather than just 7 or 8).
I therefore added an extra section titles Constants: and allowed names to be assigned values. Constants are exactly the same as any other variables, except that they cannot be changed.
Strings
String variables are extremely new in Tcc and as such are largely untested. The intention is to allow variables to contain references to string objects and to manipulate such strings as any other language might. Strings can currently only be used in the $draw message action, which draws the string on a CTC panel at the position indicated. I would also like to allow strings to be written to files and interfaces in future.
Arrays
I found a need for a lot of data organised into arrays. For example storing the type of each train. If we have an array holding the type (goods, passenger etc) of each train running on the layout then the following makes sense:
Variables: tt[10] { train type indexed by train number, allowing for }
{ 10 trains to be on the layout }
trainNo # Train number of train on this section of track
Actions:
{ when passeneger train approaches station tell it to stop }
WHEN sens1=TRUE, tt[trainNo]=Passenger DO signal=REDThis was practically impossible with the CTI tcl, though the equivalent of
WHEN ... DO tt[trainNo]=Passenger
was to use:
Variables: tt1, tt2, tt3, tt4, tt5, tt6, tt7, tt8, tt9, tt10, s WHEN ... DO s=&tt1, s=-, s=trainNo+, *s=Passenger
clearly a clumsier approach making arrays extremely desirable.
Structures
6.2:CodeConventions describes how I use a set of variables for each track section.
For example all tracks require a status, a train number and a throttle number.
Each track looks at its predecessor and successors variables to see whats happening. So far so good. It gets a little difficult at turnouts. What a turnout wants to do is present a pointer to the track on the heel of the turnout that references the variables owned by whichever other track that the turnout is currently pointing to. This means that tracks sections can be oblivious to the presence of the turnout, and of what is connected to the other side(s) of the turnout. It is of course possible to maintain a pointer to each individual variable (status, train number, throttle number etc) for each track to reference whichever other track it should 'see', but this proved rather messy.
I therefore tided up my code considerably by adding structures. A structure is simply a group of variables 'inside' another variable. If the variables I wanted were 's', 't' and 'p' (for status, train and power) and I wanted then for track L1 then I would have variables L1.s, L1.t and L1.p. Creating these variables for tracks L1, L2 and L3 would be done using:
Variables: STRUCT (s, t, p)=(L1, L2, L3)
This makes all nine data variables (L1.s through L3.p) as well as the three enclosing structures L1, L2, and L3 (though these aren't strictly variables as they cannot be changed of course).
Taking an example where L1 and L2 feed into a facing turnout that connects into L3 then structures make the following possible:
{ code that drives the turnout }
WHEN ... DO turnout=left, previous=&L1
WHEN ... DO turnout=right, previous=&L2
{ code for L3 (after the turnout) }
WHEN L3.s=Free, *previous.s=Arriving DO L3.s=Booked, L3.t=*previous.tThis translates to:
WHEN a train is authorised to approach from the left DO set the turnout left and let L3 see the variables for L1
ditto for trains from the right
WHEN L3 is not is use and it sees a train approaching from wherever the turnout is set for then become booked for the train that the turnout has been set for.
The variable 'previous' is set by the turnout and read by L3 so that L3 does not need knowledge of the turnout connectivity.
If the simple turnout is relaced by a whole network of trackwork such that L3 could be approached from half a dozen directions then this approach quickly pays significant dividends.
Objects
If a structure is a variable that contains several variables inside it (called elements) then an object is a structure that also provides services (called methods or subroutines in many languages). Objects are pre-compiled into Tcc, so a TCL script cannot define its own type of objects. The following objects are currently defined:
System - allows some control over windows
Qtu - Gives access to advanced features on the QTU boards.
Train - When used with QTU objects this adds control over a train, rather than controlling each section the train might travel through. Also gives access to dead-reckoning data.
Priority - allows a script to easily prioritise use of resources, like allowing an express train to pass in front of a goods train.
String - provides basic string handling facilities.