BIB-VERSION:: CS-TR-v2.0
ID:: CORNELLCS//TR93-1329
ENTRY:: 1993-10-14
ORGANIZATION:: Cornell University, Computer Science Department
LANGUAGE:: English
TITLE:: The Trainset Railroad Simulation
AUTHOR:: Brown, Richard A.(Editor)
AUTHOR:: Schneider, Fred B.(Editor) 
AUTHOR:: Aizikowitz, Jacob I.
AUTHOR:: Bressoud, Thomas C. 
AUTHOR:: Lekas, Tony 
DATE:: February 1993
PAGES:: 120
COPYRIGHT:: 1993 Richard A. Brown and Fred B. Schneider - All rights reserved 
            Permission to copy in whole or in part without payment of fee is 
            granted for nonprofit educational and research purposes provided 
            that all such whole or partial copies include the following: a 
            notice that such copying is by permission of the copyright owners;
            an acknowledgement of the authors and individual contributors to 
            the work; and all applicable portions of the copyright notice. 
            Copying, reproducing or republishing in whole or in part for any 
            commercial purpose is prohibited without written permission of 
            the copyright owners.   
ABSTRACT::
A prototype real-time process control application is described. A simulator 
for this application is available--its interface is specified. 
END:: CORNELLCS//TR93-1329
BODY::
The Trainset Railroad Simulation
Richard A. Brown (Editor)*
Fred B. Schneider (Editor)
Jacob Aizikowitz**
Thomas C. Bressoud
Tony Lekas***
TR 93-1329
February 1993
Department of Computer Science
Cornell University
Ithaca, NY 14853-7501
rDepartment of Mathematics, St. Olaf College, Northfield, MN 55057
**Electronics for Imaging, 950 Elm Avenue, San Bruno, CA 94066
***Digital Equipment Corporation, 9 Northeastern Boulevard, Salem,
NH 03079
The Trainset R?ailrnad Simulation
Richard A. Brown1 and Fred 13. Schneider, Editors
Jacob Aizikowitz2			Thomas C. Bressoud			Tony Lekas3
The PR Project
Department of Computer Science
Cornell University
Ithaca, NY 14850
February 13,1993
`Department of Mathematics, St. Olaf College, Northfield, MN 55057
2Electronics for Imaging, 950 Elm Avenue, San Bruno, CA 94066
3Digital Equipment Corporation. 9 Northeastern Boulevard, Salem, NH 03079
Copyright Oc 1993 Rjchard A. Brown and Fred B. Schneider.
All rights reserved.
Permission to copy in whole or in part without payment of fee is granted for
nonprofit educational and research purposes provided that all such whole or
partial copies include the following: a notice that such copying is by permission
of the copyright owners; an acknowledgement of the authors and individual
contributors to the work; and all applicable portions of the copyright notice.
Copying, reproducing or republishing in whole or in part for any commercial
purpose or for other purpose is prohibited without written permission of the
copyright owners.
Contents
Preface
o Introduction and Overview
Obtaining a Copy
2
3
Trainset User's Guide
1.1 Getting Started . . .
1.2 How to Build a Layout of Blocks and Trains
1.3 How to Run a Layout
1.4 Control Programs
Trainset R?lroads
2.1 Introduction
2.2 Blocks
2.3 Trains
Automatic Control Interface (ACI)
3.1 Introduction
3.2 Initial-State Download
3.3 Commands
3.4 Queries
3.5 Voting . .
3.6 Timer Facilities
4 Low-Level Interface (LLI)
4.1 Introduction
4.2 Messages
4.3 Initiating Communication
4.4 Initial-State Download
4.5 Commands and Queries .
4.6 Quit Message From the Simulator
A Constants Used by Trainset
ii
iv
2
3
3
10
21
21
30
30
30
35
38
38
39
40
43
45
47
50
50
50
52
53
53
54
55
B
c
Code Listings: ACI
B.1 aci,h, Interface Header File
B.2 acitest c, Example Using the ACI Interface . .
Code Listings: LLI
C.1 cpi .h, Interface Header File
C.2 aci c, Example Using the LLI Interface
111
56
56
60
75
75
83
D Reference Pages
97
Preface
I became intrigued with real-time systems in the spring of 1986. Here was an
application domain where using formal methods is justified, because the cost
in life and property of programmer errors could be so great. Here also was an
application domain where making assumptions about hardware failure modes
is inappropriate, and so a system must be able to tolerate so-called Byzantine
failures. My research efforts had been concerned with these two subjects, thus
putting me in the enviable position of having discovered a problem" for my
various solutions".
Attacking a real process-control problem seemed like a good way to get a
better understanding of the area. But, which problem? The problem had to
be simple enough so that Computer Science issues dominated any application-
dependent details. Yet, the problem could not be too simple or else some key
aspect of real-time systems might be overlooked. I was aware that various
research groups (e.g., at University of Newcastle upon Tyne and at University
of Waterloo) had used electric toy trains as a vehicle [sic? for such research,
and so that was an obvious place to start. After studying a bit of rallroading,
however, it became clear that toy trains are not accurate models of reality --H
they change the problem too much. For example, real trains cannot accelerate
or decelerate as rapidly as toy trains do. Therefore, a control program for
a real train must anticipate changes to train speed; a control program for a
toy train need not. Some of the inaccuracies of toy trains can be corrected
by modifying the electronics used to control the trains, but dealing with this
and the other custom hardware necessary for integrating a toy train set with
a computer system seemed like a black hole (as only custom hardware can be)
that I should avoid. Thus, I decided to build a railroad simulator along with
support software for constructing railroad layouts controlling those layouts from
networks of computers, and monitoring such control experiments.
After the first version of the railroad simulator was built, it became clear
that my circumstances were not unique. Other scientists were also becoming
interested in studying real-time programming issues and they, too, felt that a
prototype application could be a useful research tool. Courses on real-time sys-
tems would benefit from using this software in laboratory exercises, particularly
because specialized hardware and software were not required. So, we cleaned-
iv
up the code, wrote some user documentation and put together this software
distribution.
The Trainset system. as our railroad simulation software is now known
is the work of many people over the last 5 years. Jacob Aizikowitz defined
our model of railroads and wrote the first railroad simulator in the spring of
1987; it ran under SUNOS Unix. Jacob also supervised MEng. students Ellen
Blood, Anthony Pellegrini, and Jane Smidesang in producing an XlO graph-
ics interface to the system. Michael Abbott, an undergraduate. then defined
and implemented a high-level interface to the simulator for use by control pro-
grams. This software was then rewritten and ported to VMS, ULTRIX. and
Xli/DECWindows by Tony Lekas, a DEC engineer working with us as part of
a DEC-funded research project at Cornell. Dick Brown joined the project in
Fall 1989, spending that year and the following spring term on a major rewrite
of the system and writing documentation for what we had. Dick was assisted
by Thomas Bressoud, who rewrote and documented the layout editor and part
of the graphics monitor software. Most recently, Donald Wihardja has helped
us debug the installation procedure.
This software development effort would not have been possible without fi-
nancial support from a number of sources. My research in concurrent and
distributed systems has been funded by grants from the National Science Foun-
dation since 1978 and from the Office of Naval Research since 1985. Dr. Andre
van Tilborg, now the division director for Computer Science at ONR, was es-
pecially supportive as my ONR program manager during the initial stages of
this project, and Gary Koob, his successor, has continued that tradition. Fund-
ing from Digital Equipment Corporation was also critical to the success of this
project. Ed Balkovich of DEC encouraged me to apply for funding under Digi-
tal's External Research Program (ERP) and then served as our corporate liaison,
helping to transfer our research results to engineers at DEC who could benefit
from them. The DEC ERP funds allowed us to procure hardware for the lab?
ratory used to develop this software and to run real-time systems experiments.
John Gannon, the Software Engineering Program manager at NSF for 1988-89,
alerted me to the NSF Software Capitalization Initiative and encouraged me to
apply. Funding from that program supported Dick Brown's stay at Cornell and
is largely responsible for transforming our research prototype into a system that
could be widely distributed.
v
Fred B. Schneider
Ithaca, New York
Chapter 0
Introduction and Overview
Trainset is a real-time simulation of a railroad. The software consists of a sim-
ulator, an interactive graphics editor for defining railroad layouts and graphics
monitor programs for displaying the state of the railroad and manually control-
ling it. Two communications interfaces to the simulator are provided.
o+ The control program interface (CPI) is used by computer programs that
control Trainset railroads. The CPI consists of library routines and data
structures, collectively called the ACI (Automatic Control Interface), and
low-level message formats and related facilities, called the LLI (Low-Level
Interface).
o+ The monitor interface is used only by the graphics monitor programs.
These interfaces can be in use simultaneously.
Trainset has been implemented in C language on Digital Equipment Corp.
Ultrix, using DECwindows/X-11 and TCP/IP or DECnet.
This manual introduces Trainset and gives specifications for the railroads it
implements. The document also discusses the mechanics of using Trainset and
provides other information that a researcher or student should know in order to
write control programs that interact with Trainset railroad layouts.
The manual is organized as follows.
Chapter 1 is a tutorial on using the software. It includes instructions for
running a demonstration, creating and simulating a railroad layout and writing
programs to control a Trainset railroad. A simple programming example is
discussed to illustrate the use of the ACi.
Chapter 2 specifies the attributes of simulated railroads that Trainset sup-
ports.
Chapter 3 discusses the ACI. A high-level mechanism is presented for estab-
lishing a connection with a running Trainset railroad simulation and receiving
an initial-state download of that railroad. Commands and queries are described
for interacting with a Trainset railroad. Utility routines that provide timer
facilities are introduced, and a service is discussed that supports the writing of
fault-tolerant control programs.
Chapter 4 documents the LLI. This information will be of interest to pro-
grammers who wish to bypass the ACI layer or reimplement it for other envi-
ronments.
The appendices include reference pages for the programs that comprise
Trainset and selected code listings.
A separate installation guide accompanies the software distribution.
Obtaining a Copy
You may obtain a copy of the Trainset software, installation instructions and
the text of this manual from either of the Internet sites listed below.
o+ ftp.cs cornell.edu(Cornell University)
o+ ftp.stolaf.edu (St. Olaf College)
In either case, use the file-transfer program ftp to open a connection to the
desired host. Use the login name anonymous, and provide your own Internet
address as the password.
After logging in, issue the ftp command cd pub/trainset to access the
Trainset distribution directory. Among the files in that directory are:
o+ READNE, which briefly describes Trainset and the contents of the distri-
bution directory,
o+ install.dvi, installation manual for Trainset (in ?X output format),
o+ ts dvi, the text of this manual,' and
o+ trainset tar.Z, the source code package for Trainset.
See the manual page for ftp( 1) for file transfer instructions. Use ftpfile type
binary for dvi and tar.Z ffles. The source code package trainset tar.Z
may be unpacked on most Unix systems by issuing the following command:
7. zcat trainset.tar.Z I tar xvf -
See the manual pages for compress(1) and tar(l) for more information about
this unpacking procedure.
1To print this manual complete with figures, install Tra?nset at your local site and follow
the printing instructions provided in the installation manual.
2
Chapter 1
Trainset User's Guide
1.1 Getting Started
Trainset consists of programs to support interaction with a simulated railroad.
Users can control the railroad manually and watch it in action by using graphics
monitor programs. In addition, computer programs, called control programs,
may be written to control a Trainset railroad.
The rest of this section gives you a chance to get acquainted with Trainset.
In subsequent sections we explain how to create and run your own railroad
layout and how to use the ACI library.
1.1.1 Invoking the Programs
To start the Trainset software enter the following command.
Y. ts
(The symbol 7. is assumed to be your shell prompt.) Three windows will open
on your display, as shown in Figure 1.1. One of these, the Siniulator Window
is a terminal window that displays messages from the simulation. The other two
windows make up the graphics monitor. The Viewer window shows the current
positions of the railroad tracks and trains in a simulation. The Control Panel
window has controls and indicators, including pushbuttons and slide bars; it is
a graphical interface for controlling trains and switch blocks manually.1
The sample railroad layout displayed in the Viewer window of Figure 1.1
has two trains and 24 blocks.2 Each train has two ends; one is called the head
1The names of the programs that comprise Traineetare teil for the simulator, tiviesfor
the Vicier, tepanel for the Control Panel and teed for the layout editor. teedis discussed
in Section 1.2 below.
2A careful reader may observe that the 24 block identihers in Figure 1.1 range from 1 to
5 and 7 to 25. Identifler 6 is associated with a subblock of the cross block cr 5, as explained
in Section 2.2.3.
3
o+ Vicuer
I Control Panel
_			c?l
T?.tn I			?itd-
??????			?.ttone1			sj4
f-
[?Th			????
n??tl.			--H
Thai 2
?????			oa.,.ti'o+I
--H			oi?s',,
I Si?ulator?W1ndow			?t4?
hii ii			.j'i'.??lator
ded li?t, cha?n.l 0, d.icrip ? time 2720, p?'t'c'l 0, `ljent?d 0
?P?1/P.?.112.o+			0
dd.d client, ch.?,'ml I, dmic'p 6, time 5460, p?ctccci 0, ciient?d I
ident			dit.'??o+?/'io+aer1i'
n?'tt1m			RMne
Figure 1.1: Sample railroad layout
4
(indicated by an angle bracket) and the other is called the tail (indicated by a
square bracket). A block's type (see Section 2,2) is defined by its label:
rg for regnlar blocks,
st for station blocks,
in for join blocks,
cr for cross blocks and
s'W for switch blocks.
Niost blocks in the layout of Figure 1.1 are represented by straight lines or arcs,
e.g., those labelled rg I, rg 2 and St ii. Five such blocks are thickened to
indicate that they are occupied by a train. Three blocks (cr sin 8 and sw 14)
have other block types that are represented by circled symbols in the layout.
The control panel comprises one subwindow for each train and a pushbutton
for each switch block. Each switch block's pushbutton can toggle that switch
block's setting between the straight and turned settings. Each train's subwindow
includes the following controls and indicators.
o+ Two slide bars labelled Speed and Goal that display the train's current
speed and the goal speed desired for that train.
o+ A slide bar labelled Throttle for setting a new goal speed. An operational
train accelerates or decelerates when its goal speed differs from its current
speed.
o+ Two labels showing the name of the train (e.g., Train 1) and its state
(Operational., Derailed or Collided).
o+ Two indicator lights, EmerStop and Stastop. The ElnerStop light is illu-
minated while the train is performing an emergency stop. The StaStop
light is illuminated when the train is capable of performing a station stop,
described later.
o+ Three pushbuttons, Emerstop, StaStop and Reverse. The Elerstop
pushbutton can be used to begin an emergency stop of the train. The
StaStop pushbutton can be used to enable the station-stop feature for
that train. The Reverse pushbutton changes the direction of the train's
motion if the train is stopped.
1.1.2 Using the Control Panel
The best way to familiarize yourself with the features of the Control Panel is to
try them, observing their effects as displayed in the Viewer window. Below are
5
some suggested steps for getting acquainted with the system using the sample
layout of Figure l.l.?
When describing graphics manipulations, we will use the following terms for
mouse-oriented input operations. The mouse button to press and release is the
left mouse button (for standard workstation window managers).
o+ To click on a pushbutton or icon, move the mouse pointer into the push-
button or icon area, then press the mouse button down and release it
immediately.
To drag from one point to another in a window, position the mouse cursor
at the starting point, then press the mouse button and hold it down
while moving the mouse icon to the finish point. Finally, release the
mouse button.
o+ To drag a slide bar to a value v, first position the mouse pointer over the
inner region that contains the indicator arrow for that slide bar graphic.
Then, press the mouse button and hold it down while moving the mouse
cursor right or left until the desired value v is displayed on the numerical
display. Finally, release the mouse button. Due to variation in graphics
display resolution, it may not be possible to enter arbitrary desired values
using a slide bar.
Before starting, identify the controls and indicators for each train and switch
blockinthe Control Panel window. The Speed, Goal andThrottle slide bars
for each train display the initial value zero. Each traln is operational, and
neither of the StaStop and EmerStop indicator lights is illuminated.
[Click(once)onthepushbuttonfortheswitchblocksw14.
When you click on the pushbutton, the switch block toggles between the stralght
and turned settings. Observe that there is a delay after pressing the pushbutton
before the switch block setting actually changes on the screen. This delay has
two causes: communication time and the time that it actually takes for a switch
block, which is a large mechanical device, to change setting.
Click on the switch block pushbutton again to toggle the sw
block back to the straight setting.
Click on the StaStop and EmerStop pushbuttons for Train 2.
Do not click on the Reverse pushbutton for Train 2 nor any
pushbuttons for Train I at this time.
6
3Suggested actions are enclosed in boxes.
When the StaStop indicator light is on, we say that train is in station
stop mode; likewise, the Emerstop indicator light shows whether the train is
in emergenc?stop mode. Note that the StaStop indicator light can be illumi-
nated, but the EmerStop indicator light cannot be illuminated yet. An opera-
tional train's station-stop mode can be enabled or disabled whether that train
is moving or not. Emergency-stop mode cannot be enabled for a train unless
that train is moving.
We are now ready to set a train in motion.
Accelerate Train I by dragging the Throttle slide bar for that
train to a value near 40 (mIsec).
Notice that several things happen when you do this.
o+ The goai speed indicator, labelled Goal, now shows the throttle value.
o+ The current speed indicator, labelled Speed, begins changing from the
previous speed (zero in this case) towards the goal speed.
o+ The train at the top of the Viewerwindow begins moving (forward towards
the left, in this case).
The goal speed is not reached instantaneously. A train ordinarily changes speed
at a fixed rate of acceleration, as explained in Section 2.3.2, and it takes time
to accelerate from 0 to 40 mIsec.
Observe that a block is highlighted in the Viewer window if any part of that
block is occupied by a train.
Drag the Throttle slide bar to about 55 m/sec, then drag it to
about 45 m/sec before the train has completed accelerating to
55 m/sec.
This exercise shows that a new goai speed value overrides a previous one when
the throttle is changed, even if a previous goal speed has not yet been reached.
There is a maximum speed limit of 60 m/sec for each block in the sample
layout of Figure 1.1. If a train exceeds this limit,that train derails. All minimum
speed limits in the sample layout are zero. Using the editor tied(see Section 1.2
below) it is possible to create new layouts having different shapes and block
speed limits or to modify the features of an existing layout. However, there is
no provision for changing the attributes of a layout while it is being simulated.
Next, request an emergency stop.
Click on the EmerStoppushbutton for Train I.
7
Observe that the Emerstop indicator light is illuminated while an emergency
stop is in progress and goes off as soon as the stop has completed. Another way
to stop a train is by simply setting that train's throttle to zero. An emergency
stop, like a throttle change, overrides any prior motion plan. Thus, during an
emergency stop a train's Goal speed is zero and its current speed decreases
toward zero. There are two important differences between using the throttle to
decelerate to zero and using emergency stop.
o+ Emergency stops use a larger deceleration rate.
o+ Nothing can override an emergency stop, other than derailment or collision
of the train.
In particular, dragging the Throttleslide bar has no effect during an emergency
stop. Nor does an emergency stop cause the throttle bar to move. The throttle
is simply ignored until the emergency stop has completed and the throttle is
dragged again.
Station blocks differ from regular blocks in that they have a station.stop
feature. To test this feature, perform the following steps.
Click on the stastoppushbutton of Train 1 so that the StaStop
indicatorlightbecomesilluminated(station-stopmode).
Dragthattrain'sThrottleslidebarto30m/sec or less.
The next time that Train I enters a station block (either St II or St 16), it
will immediately begin slowing down so that it comes to a halt exactly at the
opposite terminator of that block. Three conditions are required for a train to
begin performing a station stop.
o+ That train must have station-stop mode enabled.
o+ That train must have speed at most the station-stop speed.
o+ That train must be entering a station block.
The station-stop speed (30 m/sec in the sample layout of Figure 1.1) is specified
for each station block when a layout is created.
Observe that the station-stop indicator automatically goes off as soon as a
station stop begins.
Allow Train 1 to complete a station stop.
Unlike emergency stops, it is possible to abort a station stop by changing
the throttle value for the train involved. If a station stop is aborted, then no
station stop is performed until the three station-stop conditions are again met
on entry into a station block.
A train's direction can only be changed when that train is stopped.
8
Click on Train l's Reverse pushbutton (once) while that train is
stopped.
Then drag that train's throttle slide bar to a positive value, e.g.
30 (m/sec).
Train I will begin to move backw&ds; that is the head-end retreats and the
tail-end advances.
Up to now, both trains have remained in the Operational state. in order
to become acquainted with another train state, try the following.
Let Train 1 travel (backwards) around the layout until it derails
at block jn 8.
When a train enters a join block (such as jn 8) from the tail terminator
(the one attached to rg 9 in the sample layout), then the train will derail. if a
train derails, the state label for that train changes to Derailed and the train
stops moving.
Trains that are not Operational do not respond to commands. Thus, once a
train derails, there is no way to set that train in motion again, short of starting
another simulation. Page 35 lists all the conditions under which a train will
derail.
Click on the pushbutton for switch block 8W 14 once so that the
switch block changes to the turned setting.
Drag the Throttle slide bar for Train 2 to a positive vaiue, e.g.,
30 m/sec.
Observe that attempting to enter a disconnected terminator of a switch block
would cause a train to derail.
Finally, observe what happens when trains collide.
Allow Train 2 to continue until it collides with Train I
The state labels of both trains involved in a collision change to Collided. Hence-
forth, neither train will respond to any commands, so the demonstration has
ended--Hin disaster! Fortunately, simulated trains are inexpensive to replace.
Page 36 lists all possible collision conditions.
1.1.3 Shutting the Programs Down
The programs that comprise Trainset may be shut down by selecting q?it
All in the Command menu of the Control Panel or Viewer.
9
Exercises
1.
2.
Start a simulation of the default layout by issuing the ts command, Set
both trains in motion around the track simultaneously, with Train 1 trav-
elling forward and Train 2 travelling backward.
Perform Exercise 1. Then, begin toggling the setting of the switch block
SI 1450 that Train 1 always passes through sw 14when sw 14is straight
and Train 2 always passes through that switch block when it is turned.
Note: This is not as easy as it may sound! Keeping both trains travelling
along different paths in this layout is a challenge, particularly if both are
moving as fast as possible. Train speed adjustments and switch block
setting changes must be coordinated and must be issued far enough in
advance so that the trains neither collide nor occupy a switch block while
it changes setting.
1.2 How to Build a Layout of Blocks and Trains
tsedis an interactive graphics editor for defining and modifying railroad layouts.
1.2.1 Invoking tsed
To start tsed, enter the following command.
7. tsed [filename]
The editor may also be invoked by entering:
7. ts -edit [filename?
When tsed is started, two windows appear on your display, as shown in
Figure 1.2. The larger window that is overlaid with a grid pattern is the canvas
window (Figure 1.2a). A railroad layout can be created in the canvas window.
The distance between two adjacent parallel dotted lines in the grid is called a
grid division.
The smaller window is called the toots window (Figure i.2b). It consists of
twelve icons representing operations called the toots that are available in tsed
for creating and modifying blocks and trains. The tools are applied using the
mouse operations described on Page 6. Figure 1.3 shows the tools window
together with the names of its tools.
The tools window contains one or more tools for each of the five types of
blocks in Trainset. Note that the tools window includes two tools for creating
switch blocks. The Switch Block 1 and Switch Block 2 tools differ in the
orientation of the switch block; each is a mirror image of the other. Also, there
are three tools for creating regular blocks: Straight Block, Arc Block 1 and
Arc Block 2. A straight block is a regular block consisting of a line segment,
and an arc block is a regular block consisting of a circular segment. Arc Block 1
10
FIe Qistomize
Message Wridow
(a) Canvas Window
Figure 1.2; tsed Windows
Select
Erase
(b) Tools Window
Cross Block
Join Block
Straight Block			Switch Block 1
Station Block
Arc Block 1
ArcBlock2
0
Switch Block 2
Train
Rotate
Figure 1.3: Annotated Tools Window
11
and Arc Block 2 differ only in the radius of the arcs they create. The Arc
Block 1 tool creates arcs with radius two grid divisions and the Arc Block 2
tool creates arcs with radius four grid divisions.
The remainder of this section is a two-part tutorial on using tsed for creating
and editing railroad layouts. Section 1.2.2 introduces you to the various tsed
tools. A series of exercises demonstrates how to construct a layout and how to
make simple editing changes. Section 1.2.3 explains how to set attributes such
as block speed limits, how to save a layout in a file, and how to exit from the
editor.
1.2.2 Using the tsed tools
The Current Tool
In tsed, one tool is enabled at any given time; it is called the current tooL The
current tool is indicated in the tools window by being highlighted. The message
area at the bottom of the canvas window also displays the name of the current
tool. On startup, Select is the current tool.
Click on a tool icon other than Select to choose a different current
tool. Repeat one or more times.
Regular and Station Blocks
Straight regular blocks and station blocks are created in a layout by dragging
with the mouse from one point to another in the canvas window when the
corresponding straight block or station block is the current tool. This procedure
creates a line segment between the starting and stopping points of the drag
operation. The starting point is called the head terminator of the block, and
the stopping point is cailed the tail terminator.
Lick ontheStraightBlocktoolicon.J
Create a horizontai regular block by dragging from right to left
starting near the middle of the canvas window, as shown in Fig-
ure 1.4a.
All linear blocks (Straight and Station) created by tsed are constrained
to be vertical horizontal or at a ?45O diagonal. To aid in alignment, tsed
enforces additional constraints on the placement and length of a block relative
to the dimensions of the canvas window grid.
Click on the Station Block tool icon
12
r?i
(a)
st2 . . : wj5t2
Figure 1.4: Tutorial Steps
Now create a station block in the canvas window by dragging from
the right endpoint of the existing stralght block to a point further
to the right, as shown in Figure I.4b.
When a new block is created, tsed establishes a connection with an existing
block if the new head terminator is within a few pixels of an existing terminator.
tsed also constralns the slope of the new block to match the slope of the existing
block at that terminator.
Station blocks and straight regular blocks appear graphically to be identical
except for their labels. Regular blocks (whether stralght or arc) are labelled rg,
and station blocks are labelled St.
The Erase and Select tools are used for modifying objects already on the
canvas window. Erase enables you to remove a block or traln that you have
created. Select enables you to reposition the label for a block or to designate
a block whose attributes you wish to change.
?lick on the Erase tool icon.
Now,erasethestraightblockby clicking on it.
Click on the Select tool icon.
J
J
13
Now drag the label for the station block to a new position above
the block.
The result of these changes are shown in Figure l.4c.
Arc blocks are created by dragging when the current tool is Arc Block 1 or
Arc Block 2. The starting point ofthe drag operation determines the location
ofthe arc block's head terminator. The extent of the arc is determined by the
ending point of the drag operation and depends on the location of the head
terminator and, ifthe arc block is attached to another block, the slope of that
block.
IClick on the Arc Block 1 tool icon.
Create an arc block connected to the station block by dragging
from the left endpoint of the station block toward a point that
producesa900arc pointing downward, as shown in Figure 1.4d.
When using the Straight Block, Station Block, Arc Block 1 or Arc
Block 2 tools, you can cancel creation of a block after a drag operation has
already been initiated by finishing that drag operation within afew pixels ofits
starting point.
Start dragging to create another arc block connected to the last
arc block, then cancel the creation ofanew block byfinishing that
drag operation at its starting point
Iconic Blocks: Crosses, Joins and Switches
The Cross Block, Join Block, Switch Block 1 and Switch Block 2 tools
always create blocks that have a fixed size and shape. In tsed, these blocks are
referred to as iconic blocks. They are created by clicking with the mouse rather
than by dragging.
When an iconic-block tool is current and the mouse cursor is in the canvas
window, the cursor takes the form of the icon for the current tool. Along
the outer circle of such a cursor are enlarged points called hot spots at which
connections with other blocks can be made.
Clickonthe Join Block tool icon. Observe that the mouse cursor
changes to the form of ajoin block whenever the cursor is in the
canvas window.
Position the mouse cursor so that the hot spot at its right head
is over the unattached terminator of the arc block. This requires
slightoverlappingbetween the arc block and thejoin-block cursor;
see Figure 1.5a.
14
st 2 :
________________________I
(a) Before Clicking
.. .
(b) Mter Clicking
Figure 1.5: Join Block Creation
Now click with the mouse and create the join block of Figure l.5b.
Note that the newly created join block is constrained so that the slope of the
join block and the slope of the arc block agree at their common terminator.
tsed does not make more than one connection when an iconic block is cre-
ated. Thus, during an editing session, it is probably best to create iconic blocks
before creating the regular and station blocks they are attached to.
The Rotate tool enables you to rotate an iconic block clockwise by one hot
spot.
Click on the Rotate tool icon.
Now click on the join block created in step 15. Observe that the
join block labelled jn 4 rotates so that its tail terminator becomes
the terminator attached to the arc block.
Clickonthejoinblockasecondtimetorotateagain.
If an iconic block is not connected to any other block, the Rotate tool
rotates it by one-eighth turns. If a switch block is rotated through a full circle
using the Rotate tool, it changes orientation.
Trains
A train may be created in a layout by dragging when Train is the current
tool. The drag operation begins at the position desired for the head end of a
train, continues aiong the blocks to be occupied by that train, and stops at the
position desired for that train's tail end. The head end of a train is indicated
in the layout by an angle bracket, and the tail end is indicated by a square
bracket. All blocks occupied by a train are highlighted. The head end of a train
is constrained by tsed to start in a regular or station block.
15
A			B
-			r? ? : :			- : : rs 7			-
(a) Before Train Creation
rc i			-
r? ?			r% 7
(b) After Train Creation
Figure 1.6: Oval Layout
16
Erase all blocks currently on the layout. Then, create an oval
layout as in Figure l.6a made of four straight blocks and four arc
blocks,usingtheStraightBlockandArcBlock1tools.
It is not necessary that the block identifiers i.e., the numbers in the labels,
match those in Figure l.6a.
Click on the Train tool icon.______________________________
Create a train on the oval by dragging clockwise from the point
marked A to that marked B in Figure l.6a, resulting in Fig-
ure 1.6b.
If, while creating a train, you drag across the starting point, the brackets
that indicate train ends reverse their directions. Also, observe that a whole
block is highlighted if any part of that block is occupied by a train.
You should now feel comfortable with the basic drag and click operations
for creating blocks and trains in a layout. In the next part of this teed tutorial,
you will learn how to store a layout in a ffle, how to specify attributes such as
speed limits for individual blocks, and how to exit teed.
If you would prefer to return to this tutorial at a later time, select quit4
from the File menu now, and invoke tsed again when you are ready for the
remainder of the tutorial. There is no need to save your present work, since it
will not be used in the second part of the tutorial.
1.2.3 Creating a figure-eight layout file
Our goal is to create the figure-eight layout illustrated in Figure 1.7 and then
save that layout in a file iylayout .1. All blocks in the layout should have
minimum speed of 0 (m/sec) and maximum speed of 70, except that the station
blocks at the top and bottom of the layout are to have minimum speed of 0 and
maximum speed of 50.
Select Jew from the File menu. If you are continuing from the first
part of the tutorial, teed will ask if you wish to discard changes
in the canvas window; click on the Yee button. In response to the
prompt, enter iylayout as the name of the file to be created.
By invoking the Jew command above, mylayout .1 becomes the current ffle
name. This name is indicated in the title for the canvas window. Prior to the
Jew command there was no current file name. When naming a file, the extension
.1 is automatically appended by teed if you do not include it. Pathnames that
`Quit willbe discussedin detail on Page 21.
17
tsed: mylayout.1
Select
Figure 1.7: A figure-eight layout
18
Owige straight block attriiiutas:
0
iv?.?IMII V?iocity
60
t
Maximum VeiocI?
(I
Stop Velocity
owige
Figure 1.8: Change Default Block Attributes subwindow
do not begin with a slash / are interpreted relative to the current working
directory.
The default minimum and maximum speeds for blocks can be changed by
choosing the Change Default Block Attributes entry from the Customize
menu. A changed default value applies to blocks created after the change; it
does not affect blocks already created. Change Default Block Attributes is
itself a menu whose subentries are the various block types.
Change the default attributes for straight blocks by c?oosing
Change Default Block Attributes from the Customize menu
and moving the cursor to the right until the subentries appear.
Select the Straight subentry.
After this step, a subwindow will appear that contains three slide bars as
shown in Figure 1.8. The slide bar on the top determines the minimum speed
for straight blocks and is normally 0. The slide bar in the middle determines
the maximum speed, which is 60 at present. The slide bar on the bottom is
disabled for straight blocks since it defines the station stop speed, an attribute
that is only applicable to station blocks.
Set a new maximum speed by dragging the middle slide bar
Leave the minimum speed at 0.
Click on the Change pushbutton to make the new default speed
limits effective.
The steps above change the default maximum speed for straight blocks only.
We also need arc blocks and cross blocks that have a maximum speed of 70.
19
Change the default maximum speed to 70 for arc blocks, using the
Arc subentry in the Change Default Block Attributes menu
under Customize and proceeding as above.
Also change tii% default maximum speed to 70 for cross blocks,
You are now ready to create the blocks of your layout. Refer to Figure 1.7
for illustration of the steps below.
Create a cross block near the center of the layout at an intersection
pointoftwoperpendiculardottedlinesinthegrid.
Rotate the new cross block once using the Rotate tool. The
subblocks of the cross should form an as opposed to a
Create two straight blocks of exactly the same length, each form-
ing the diagonal of a square with sides about 4 grid divisions long,
so that the head terminators of the straight blocks are attached
to the upper hot spots of the cross block.
Note that the grid assists you in determining when blocks have exactly the
same length.
Create two arc blocks with l35? extent and head terminators at-
tachedtothestraightblocksthatyoujustcreated.
Create a station block that connects to both unattached termina-
tors of the arc blocks.
The attributes of an individual block may be customized by selecting that
block with the Select tool and choosing Change Block Attributes from the
Customize menu.
I Click on the Select tool icon.
Click on station block St 7 in the canvas window to select it for
customization.
ChooseChangeBlockAttributesfromtheCustomizemenu.
In the subwindow that appears, set a new maximum speed by
dragging the slide bar in the middle to 50.
The station stop speed is another attribute that may be adjusted for station
blocks using Change Block Attributes. (See Section 2.2.5 for a discussion of
the station stop speed.) We will leave the default value at 30 for this layout.
20
Create the lower portion of a figure-eight layout using a procedure
similar to the previous seven steps.
Place a train on the figure-eight layout.
You have now created the desired figure-eight layout. It is time to save your
work and exit from the editor.
Save the figure-eight layout by choosing Save from the File menu.
Either the Save entry or the Save as ... entry in the File menu may be
used to save your work in a file. The difference is that Save as ... always
prompts you for a file name, while Save uses the current file name if there is
one.
Finally, end the editing session:
[Exittsedbychoosing quit from the File menu.
The quit command checks for any unsaved changes before exiting. If any
are found, quit gives you the option of writing them to a file first. The Close
command is similar to quit, except tsed does not exit after checking for unsaved
changes. Instead, a Close causes tsed to enter a state with an empty canvas
window and no current filename.
1.3 How to Run a Layout
A railroad layout Iylayout created using tsed can be simulated by issuing the
following command.
Y. ts -layout mylayout
A Trainset simulation will start and search for a file called mylayout .1, first
searching in the current working directory, then in the standard location for
layouts, as explained in the manual page for ts in Appendix D.
1.4 Control Programs
The Automatic Control Interface (ACI) is a library of procedures and data
structure definitions for writing programs that control a Trainset railroad.
The principal data type associated with the ACI, LayoutData, represents
various attributes of the blocks and trains in a railroad layout. Layoutflata is
described in Section 3.2 and defined in Appendix B.l.
The ACI routines may be classified into five categories.
21
1. The ACI GetDownloadprocedure establishes a network connection with a
running railroad simulator and receives a report of the state of the railroad
being simulated. GetDoinloadreturns a pointer to an internally allocated
data structure of type LayoutDataholding the state information that has
been received. See Section 3.2 for further details.
2. ACI commands enable a program to change certain attributes of blocks
and trains. Seven command types are available.
SetSiitchinitiates a setting change for a switch block.
Accelerate and Decelerate initiate changing the speed of a train
at a constant rate for a specified time period.
SetSpeedinitiates changing the speed of a train towards a specified
goal speed.
Setflirectionchanges the direction of a train that is stopped.
E?ergencyStophalts a train using a deceleration rate that is quicker
than the rate for Decelerate.
o+ Stationstopenables a train to come to a complete stop at a known
location in a station block, provided that the train enters that station
block slowly enough.
See Section 3.3 for more details about ACI commands.
3. ACI queries enable a control program to obtain state information about a
railroad after a download has been received. Four query types are avail-
able.
0
See Section 3.4 for more information.
Getfliockoccupancyindicates whether a specified block is occupied.
GetSiitchposit returns the setting of a switch block.
GetTrainStatus indicates whether a train is operational.
GetTrainMotionindicates whether a train is moving.
4. ACI voting service procedures, SetSeqlumberand JeiSeqiumber,interact
with a command arbitration facility in the simulator. This service is useful
when implementing fault-tolerant control programs. See Section 3.5.
5.
ACI utility procedures, InitTiner, GetTiier, AwaitTinier, CancelTimer
and Sleep, provide high-level general purpose timing facilities. See Sec-
tion 3.6.
22
/* demo.c -- simple demonstration of the ACI */
#include "aci.h" /* ACI definitions and declarations */
*
*
*
*
*
*
[z2
This program shows the mechanics of the ACI layer of the control
program interface, by initiating a connection with the simulator,
receiving the state of the railroad, moving a train, and making some
queries
23
char *progname; /* name of this program */
char *hostname = "`?; /* host that is running simulator */
int silnum = 0; /* distinguishes between simulators running on same host */
Seconds timeout = 20.0; /* maximum second to connect to simulator */
Layoutflata *datap; /* pointer to entire received railroad state info */
Figure 1.9: demo. c, an example using the ACI (beginning).
main(argc, argv)
int argc;
char *argvO;
L]3
#define MAX?H0STNANE 100
#define POLLIJG?INTERVAL
#define POLLING?TIME0UT
#define NULL (void *) 0
0.100 /* Seconds */
200.0 /* Seconds */
/* generic null pointer */
int poll?timeout?flag = 0; /* flag for terminating polling loops */
* set?poll?timeout?flag is executed by a timer that is used to prevent
* infinite polling loops.
void
set?poll?timeout?flag()
poll?timeout?flag++;
E]
E]
TrainData *tp; /* pointers to parts of received layout data */
Blockflata *bp;
int T = I, 13 = 1; /* identifiers of a train and a block */
enum Occupancy occ; /* return values from queries */
enum TrainNotion tin;
int n; /* loop counter */
* Collect coininand line args
*/
progname = *argv++;
if (argc)
hostname = *argv++;
if (argc)
simnuni = atoi(*argv++);
if (argc)
timeout : atof(*argv++);
/*
* Connect and get the state of the railroad.
* Then, use received values to check the identifiers T and 13.
datap Getflovnload(hostname, simnuin, progname, timeout);
if (datap =: (Layoutflata *) 0) ?
printf('couldn't get initial dovnloadI\n");
exit (?);
if (T <= t ii T >= datap->train?ct) <
printf(sample train ID Y.d out of range (1. .0Ad]!\n', T, datap->train?ct);
exit (I);
if (13 <= I II 13 >= datap->block?ct) ?
printf('sample block ID Y.d out of range Ei. .Y.d)!\n", 13, datap->block?ct);
exit (I);
Figure 1,9: demo .c an example using the ACI (continued).
24
/*
* Print some sample values
L]7
10
11
printf('Number of trains: Y.d. Number of blocks: Y.d\n
datap->train?ct, datap->block?ct);
: adatap->trains(T];
printf(Train Y.d has length Y..2f, gith front at block Y.d offset Y?.2f\n'?,
tp--H>length, tp--H>front block, tp->front offset);
bp = adatap->blocksEB];
printf("Block Y1d has length Y?.2f, max speed Y..2f and min speed Y0.2f\n"
bp->length, bp->max?speed, bp->min?speed);
if (bp->type :: BT?REGULAR)
printf(fl?Thi5 is a regular block, connected to blocks Y.d and Y.d\n\n'?,
bp->t.rg.tail, bp->t.rg.head);
* Perform some accelerations and decelerations of train T.
printf('beginning acceleration\n'?);
AccelerateCT, 20.0);
printf(?'pausing.. .\n");
Sleep(25.0);
printf(?beginning deceleration\n");
Decelerate(T, S.0);
* Travel until block B is occupied (or timeout occurs)
printf('polli:ng until block Y.d is reached.. .\n'?, B);
poll?timecut?flag 0;
InitTiier(P0LLIJG?TIME0UT, 0, set?poll?timeout?flag);
while ((occ = GetHlock0ccupancy(B)) == 0C?FREE aa !poll?timeout?flag)
Sleep(P0LLIIG?INTERVAL);
Figure 1.9: demo.c, an example using the ACI (continued).
25
12,13
14
if (occ			OC?ERROR) ?
printf("error getting block occupancy!\n');
exit (1);
if (poll?timeout?flag) <
printf('polling timed out after Y.f seconds\n?, POLLING?TINEOUT);
exit (I);
CancelTimer();
/* assertion:
[m7			occ :: OC?OCCUPIED */
18
19
* Perform an emergency stop, then poll until train stops
printf(?'performing emergency stop\n'?);
Elergencystop(T);
printf("polling uiLtil train stops..
poll?timeout?flag 0;
InitTimer(POLLIJG?TINECUT, 0, set?poll?timeout?flag);
while ((tm = GetTrainMotion(T)) TN?NOVIIG aa !poll?timeout?flag)
Sleep(PCLLIIG?IJTERVAL);
if (tm == TM?ERRoR) ?
printf(error when querying about train motion!\n);
exit (I);
if (poll?timeout?flag) ?
printf(polling timed out after 7.f seconds\n", POLLIJG?TINEOUT);
exit (I);
CancelTimer();
/* assertion:
TN?sToPPEn */
printf(train has stopped.\n");
exit (0);
Figure 1.9: demo. c, an example using the ACI (concluded).
26
The example program demo.c (see Figure 1.9) illustrates the use of the ACI.
This program connects to a simulation and receives the state of a railroad, then
attempts to move a train in that railroad to a certain block and perform an
emergency stop. Some key points about the code are indicated by numbers
etc.
[Ji The include ffle aci .h contains declarations and definitions required for
compiling a source file that uses the ACI. In order to create an executable,
one must link with the ACI library libaci a.
Time values passed to the ACI timer procedures are aiways floating-point
quantities representing seconds. An ACI type Seconds is defined for such
quantities.
[?] set?polling?tirneout is the procedure that will be invoked if a tsuser
timer expires.5 Such a procedure cannot be invoked with arguments, so it
changes a global variable polling?timeout in order to inform the main
program about timer expiration.
[?] The ffle aci.h defines a number of data types besides Layoutflata. The
type Trainflatais used to represent download information received for a
train; likewise, HlockData represents download information for a block.
Both Trainflata and BlockData are component types used in the defini-
tion of Laycutnata. Enumerated types, including Occupancy and Train-
Not ion, are used for command arguments and query return values. Trains
and blocks have unique positive integer identifiers.
Getflownloadtakes four arguments, returns a null pointer on failure, etc.
Chapter 3 is the reference for this and the other ACI procedures.
6,7,8 Several examples of references to a LayoiitDatastructure follow the inv?
cation of GetDoinload. More direct references such as
datap->trains(T-I] length
datap->blocks(H-I] .t.rg tail
could be used in place of those that involve tp and bpin deic.c. Observe
that the index of a train in datap->trains is always one less than that
train's identifier. A similar remark holds for blocks.
The function Printflowuloadin Appendix B.2 includes examples of refer-
ences to every component in a LayoutDatastructure.
Checking the values of T and H at ? is not strictly necessary in this
program, since their values are known to be valid in this case. It is a
good defensive programming practice to include such checks anyway, as
protection against future changes.
?Note: As explained in Section 3.6, a cail to InitTilor overrides any prior calls. Thus, we
speak of "the ACI timer," because only one such timer can be in effect at any time.
27
ACI commands such as Accelerate are non-blocking and return no values.
They print no warnings about unreasonable arguments. The command
Accelerate(T, 20.0);
requests that train T accelerate for a duration of 20 seconds, increasing its
speed during that period at the predefined fixed acceleration.
10,11 The instruction
Sleep(28.O);
causes demo.c (not the Trainset simulator'.) to suspend execution for 25
sec. This is long enough that the subsequent Decelerate command 11
is unlikely to interfere with the prior Accelerate command [?.
The train would begin to reduce its speed as soon as the Decelerate
command is received, even if the prior acceleration had not been in effect
for the acceleration's entire duration. For example, if the Sleep had been
for 10 seconds instead of 25, then train T would accelerate for about 10
seconds, then decelerate for 5 seconds.
12 The query GetHlockOccupancy is used so that train T may reach block
H. That query is issued frequently until block H is found to be occupied
or until an error or timeout occurs. This technique of frequent querying
is called poiling. The ACI timer is set up before the while statement to
provide timeout mechanism for leaving that loop.
13 The loop guard condition shows that there are two ways to leave the loop.
If occ differs from OC?FREE, i.e., ccc has value OC?occUPIED or
OC?EUOR, then the loop will be exited. (Query errors can be caused
by invalid arguments, loss of communication with the railroad, etc.)
If polling?timeout has a non-zero value, then the loop will be ex-
ited. polling?timeout changes from zero to a non-zero value if the
timer expires (see [?j and the invocation of InitTimer).
14,15,16 Before concluding that occ = OC?OCCUP1ED, which would indicate occu-
pancy of block B, it is necessary to eliminate the other events that can
cause the loop to exit.
17 A period of time elapses between the moment that block B becomes oc-
cupied and the time when the control program demo. c can act on that
information. This time period arises from the following causes.
Delay from polling. Some time necessarily elapses between the
moment that block H becomes occupied and the time when that sen-
sor is checked. The sensor is checked by each successful call to the
28
query GetBiockoccupancy. Thus, if GetHiockoc cupancy succeeds
this period is bounded by the execution time of one iteration of the
polling loop, except possibly when block B is found to be occupied
on the first GetBiockOccupancy query.
Network delay. This is the time required for a sensor value to be
communicated to the control program.
Local processing delay. Once the process that is running deio. c
receives a communication that block B was occupied, several further
steps occur: the query function returns; the polling loop exits; checks
are performed in order to deduce that block occupancy was in fact
the reason for loop exit; and the timer is cancelled. Each of these
steps requires local processing time.
18
Such delays can affect the correctness of control programs. For example,
the occupancy of a block might change by the time that a control program
could take action on that occupancy information. In particular, it is not
correct to conclude that block B is curr?ntI? occupied based solely on the
fact that occ has the value OC?DccUPIED at 17.
The command ElergencyStop causes a train to reduce its speed to zero
quickly. Unlike Accelerate and Decelerate, the effects of Eiergencystop
cannot be interrupted by another command.
19 Polling with the query GetTrainMot ion is used in deino. c to determine
when the train has come to a complete stop.
Exercises
1.
2.
Write a program that uses the ACI to cause both trains in the sample
layout (see Figure 1.1) to move around the track for five minutes, then
stop. (Compare to Exercise 1 of Section 1.1.2.) The trains need not travel
on different loops.
As in the loop ??, a timer is set up just before the while statement
in order to guard against an infinite loop. Is this timer necessary, or is
that loop certain to exit in a reasonable amount of time without a timer?
(Hint: Consider the specifications of GetTrainNotion in Section 3.4.4.)
29
Chapter 2
Trainset Railroads
2.1 Introduction
Trainset railroads are idealized versions of real railroads. This chapter dis-
cusses the attribute? and operation of Trainsot railroads. You will see that
while Trainset railroads are simpler than their real-life counterparts, the sim-
plifications are ones that do not make it appreciably easier to write programs
to control the railroad.
2.2 Blocks
In a Trainset railroad, a la?ottt consists of an assembly of blocks together with
movable trains that occupy some of those blocks. See Figure 1.1 for an example.
Every block is assigned a unique block ID B, a length L?, a ma:imttm speed
limit MXa and a minim?m speed limit MNs. We expect1
o < MNs < MXE < MAXFLOAT?
Each block has a set of terminators that delimit the track implementing that
block.
As in real railroads, each block has an associated sensor called a track circuit
that indicates whether that block is occupied by a train. Polling a track circuit
only returns one bit of information signifying whether the associated track block
is occupied. Note that it is not possible to learn the exact location of a moving
train from the information returned by a track circuit.
A block may have between two and four terminators, depending on its type.
There are five types of train blocks: regular, join, switch, cross and station
blocks. These are illustrated in Figures 2.1--H2.5.
-MA XFL OAT is the largest floating point number on the computer system. The stand&d
mks units of measure are used throughout this document.
30
r? 2
rg i
Figure 2.1: Regular Blocks
Two blocks are attachedto each other if each of the blocks has a terminator
that is associated with the other block. Trainset block layouts are subject to
the following restrictions.
o+ A terminator may be associated with at most one block.
o+ A block may not be attached to itself.
Blocks may not be attached to each other more than once, e.g., circular
track configurations that consist only of two half-circle blocks are prohib-
ited.
These restrictions make it simpler to describe attachments between blocks but
do not limit the topology of Trainset layouts. For example. we could easily
construct a circular track configuration using two quarter circles and a semicir-
cle.
It is not necess&y that all terminators of a block be attached to other blocks.
Of course, a traln will derall if it attempts to exit from a block at an unattached
terminator.
2.2.1 Regular Blocks
Each regular block has two terminators. Such a block can have the shape of a
line segment or a circular &c. See Figure 2.1.
2.2.2 Join Blocks
A join block has three terminators and allows two train routes to merge. Two
of the terminators, called the heads, are each connected to the third terminator,
called the taji.
A traln can occupy the track between either of the heads and the tail. Tralns
can enter a join block at either head and exit through the tall. A train deralls
when it either enters a join block at the tall or leaves from one of the heads.
31
Tail
v
Headi			Head2
Subblock
rg 25
r? 7
Figure 2.2: Join Block
Head
Tail
Subblock
Head?? Tail
rg 4
r? 2i
Figure 2.3: Cross Block
Join blocks are assumed to be LENjoin meters long measured from the tail
to either head terminator.2 At most one train may occupy a join block at any
time. If a second train enters a join block that is already occupied, then a
collision occurs.
2.2.3 Cross Blocks
A cross block (see Figure 2.3) allows a track to intersect itself, so that "figure
eights" and related designs may be built. A cross has four terminators, and a
train may only travel between any opposing pair--Hturns are not allowed. Thus,
cross blocks behave like two short regular blocks, cailed subblocks, with the
2Constants such as LENj0in are determined at the time of installation. See the installation
manual for detalls.
32
Tail
Turnhead
Straighthead
Straight Position
Tail
??Turnhead
A
Straighthead
Turn Position
Figure 2.4: Switch Block
same length and speed limits, that have been overlaid at their midpoints. A
cross block and both of its subblocks are occupied whenever either of those
subblocks is occupied. Cross subblocks are LENcross meters long. A collision
occurs when a cross block becomes occupied by two or more trains.
In order to distinguish between the subblocks of a cross block, each has its
own block ID. One of these aiso serves as the cross block's ID.
2.2.4 Switch Blocks
A switch block (see Figure 2.4) has three terminators, labelled tail, straight head
and turn head, and consists of a movable track segment that has two settings
and a mechanism to move this track between those settings. Depending on the
setting of the movable track, either the train may travel between the tail and
straight head (in either direction) or between the tail and turn head. Thus, a
switch block can be part of two different train routes, according to that switch
block's setting.
A switching time Tswi?? must elapse for a switch block to change from one
setting to the other. The switch setting is undefined during this time period.
If a switch is commanded to change to one setting (e.g., straight) while it is
already in that setting or in the process of changing to that setting, then that
setting-change command has no effect. If the setting-change command was to
switch to the other setting, then the switch begins changing to the new setting.
It takes a full Tswit? seconds to change to a new setting, even if the switch
33
Figure 2.5: Station Block
setting was undefined at the time of the setting change command.
A train attempting to traverse a switch block will derail under the following
circumstances.
o+ The switch block has undefined setting.
o+ A switch change is attempted on the switch block.
o+ The train enters the turn head terminator of a straight switch, or the
straight head terminator of a turned switch.
Switch blocks are LENsWitch meters long measured from the tail to either of
the other terminators. At most one train may occupy a switch block at a time
or a collision will occur.
A switch block that is occupied by a derailed or collided train will not respond
to setting change commands.
2.2.5 Station Blocks
Station blocks are like regular blocks (see Figure 2.1) except that they allow a
train to stop at a fixed specified location. Using a station block is the only way
to be sure of the exact position of a train after it has begun moving, because
track circuits only reveal whether a block was occupied sometime in the past.
Each station block B has a station-stop-speed VSSs. A station stop begins
whenever a train that is in station-stop mode (see Section 2.3) enters a station
block while travelling with speed at most VSSB. Under these conditions, the
train will automatically begin to slow down so that it will come to a complete
stop exactly at the opposite terminator of the station block.
A station stop by a train can be aborted by commanding that train to change
velocity or acceleration.
34
rg 9
Figure 2.6: Block occupancy.
2.3 Trains
Every train is assigned a unique train ID T and a length and has the following
attributes that may change during a simulation.
o+ A speed VT and an acceleration ??
o+ Two ends, labelled head and tail, that are points within blocks.
o+ A direction dir?.
o+ An emergency?stop mode es? and a station?stop mode 55T which may be
enabled or disabled.
We expect 0 < VT< MAXFLOAT. The distance along the track between a
train's ends is always that train's length LT.
Each train in a layout always occupies a sequence of attached blocks. A
train occipies a block if the interiors of that train and that block intersect. For
example, suppose that the train in Figure 2.6 has just completed a station stop,
so that one end of that train is at a terminator. That train occupies blocks
rg 10and St 11, but does not occupy block rg 12. If that train moves slightly
to the left (i.e., forward), it will then occupy block rg 12.
A train's direction dir determines which of the two ends advances if that
train moves forward. The train end that advances is called the front, and the
other end is called the rear. For example, suppose that the head of a train is
the front. If that train's direction is changed, then the train's tail will become
the front andthe head will become the rear. A train's direction can only change
when that train is not moving, that is, when VT = aT = 0
2.3.1 Train States
A train can be in one of three states: operational, derailed or collided. Figure 2.7
shows possible transitions between these states.
A train's state changes from operational to derailed under any of the follow-
ing conditions:
35
derailed			collided
Figure 2,7: Train state transitions,
o+ That train occupies a block and the train's speed VT violates one of that
block's speed limits, VT < MNs or MXs < VT.
o+ The front of that train exits a block at an unattached terminator.
o+ The front of that train enters a join block at the tail terminator or exits
a join block from a head terminator.
o+ That train occupies a switch block that has undefined setting.
o+ A switch change is attempted on a switch block occupied by that train.
o+ That train enters the turn head terminator of a switch in the straight
setting or the straight head terminator of a switch in the turned setting.
A train's state changes to collided if there is another train in the layout such
that either of the following conditions holds.
o+ Both trains occupy the same switch, join or cross block.
o+ Both trains occupy the same block at the same point.
2.3.2 Laws of Motion
In Trainset, the acceleration of a train is one of the constant values ACC, 0,
--HACC and Aemer, except during a station stop. We expect
0 < ACC< Aemer < MAXFLOAT.
If the acceleration a? of a train is constant during a time interval [t0, tii and
v(ti) is the speed VT of that train at time tj, then
v(ti) = v(to) + ?? (t1 --H t0).
36
For such a train, if x(ti) represents the position of an end of that train at time
tj, then
x(t1) =x(to)+v(to) (t1 --Hto) + (t1 ?to)2,
2
A train's station-stop mode turns on the station-stop feature for that train.
A station stop begins if the station-stop mode of a train is enabled and that
train enters a station block with speed VT satisfying
0< v_ < VSSs,
where VSSB is the station-stop speed of that block. The station-stop mode of
that train is disabled as soon as a station stop begins.
A station stop by a train can be aborted by setting that train's speed or
acceleration during that station stop. If a train performs a station stop in a
station block and that station stop is not aborted, then all of the following will
hold upon its completion.
o+ The train's speed VT and acceleration a? will be 0.
o+ The train's front will be at the opposite terminator of that station block.
o+ The train will occupy that station block.
A station stop's completion may be observed using the query GetTrai?otion
discussed in Section 3.4.
The emergency-stop feature allows a train to stop at a faster rate than
usual. A train performs an emergency stop when its emergency-stop mode
is enabled. During an emergency stop by a train T, its acceleration satisfies
= --HAemer. The emergency stop finishes as soon as VT becomes 0. At that
time, the emergency-stop mode for T is disabled and its acceleration a? becomes
0.
A train does not respond to commands during an emergency stop. Thus,
emergency stops cannot be aborted.
37
Chapter 3
Automatic Control
Interface
3.1 Introduction
The Control Program Interface (CPI) is used for writing computer programs
that interact with Trainset railroads. It consists of two layers, the Automated
Control Interface (ACI) and the Low-Level Interface (LLI), as illustrated in
Figure 3.1.
The ACI layer consists of library routines that can be used in a control
program to establish communication with a simulation of a Trainset railroad
obtain the state of that railroad, interact with the railroad's layout using com-
mands and queries and use the Trainset voting service.? The ACI also provides
some local timer utilities. This layer is intended to meet the needs of most con-
trol program writers,
1The voting service facilitates writing fault-tolerant control programs. See Section 3.5.
control
program
ACI
LLI
Trainset
CPI			railroad
simulation
network
Figure 3.1: Communication interface layers.
38
The LLI layer has most of the same capabilities as the ACI, but at a more
primitive level that requires a programmer to manage communication and mes-
sage buffers directly.
This chapter describes the ACI layer. Usage details are described in a ref-
erence format using C language syntax. Chapter 4 presents the LLI layer.
For programming examples that use the ACI layer, see Section 1.4 and Ap-
pendix B.2.
A Trainset railroad can accept three kinds of messages from CPI clients.
A state downioad request asks for a railroad's state. A command can affect a
railroad layout, e.g.. by changing a switch block's setting or setting a train's
acceleration. A query requests information about a block or train.
Trainset railroads silently ignore messages that contain syntax errors.
3.2 Initial-State Download
Layoutflata *
GetDownload(hostname, simnum, progname, timeout)
char *hostname; /* machine running a simulation */
int simnum; /* identifies a running simulation */
char *progname; /* name of invoking program */
double timeout; /* in seconds */
Executing GetDownload causes a network connection to be established with
a railroad simulation and information about the state of the railroad to be
received from that simulation. The download is stored in an internal static data
structure of type LayoutData defined in Appendix B. 1.
hostname and simnum2 must identify an executing Trainset railroad simu-
lation, and timeout must be positive, or GetDovnload will fail. If hostname is
an empty string, then the local host is used.
The address of the LayoutData structure is returned if execution succeeds.
A message is printed and a NULL pointer is returned on failure. Possible failure
conditions are:
o+ timeout is not a positive floating-point value.
o+ A network connection could not be established before timeout seconds
elapsed,
o+ There is no simulationon the host hostname with identifler simnum.
2sisnuidetermines whichwell-known ports &e to be used when initiaiizingcommunication
with a railroad simulation. Multiple railroad simulations n'ay be ran simultaneously on the
same host if they use different ijinun values.
39
o+ An error was detected while parsing the download.
o+ GetDownload has already been called successfully by this client at a prior
time.
The download consists of the following.
General parameters:
o+ The number of blocks NbIocks and of trains Ntrains in the layout. Block
IDs range from 1 to NbIocks and train IDs range from 1 to Ntrains.
o+ The standard acceleration rate ACCand emergency-stop acceleration rate
Aemer for trains,
o+ The switching time Tswitch for switch blocks.
o+ The voting window Wvoting and voting quorum Qvoting for commands to
be accepted. See Section 3.5.
For each block:
o+ The ID B, type and length of the block.
o+ The maximum speed limit MXs and minimum speed limit MNs.
o+ The attachments for that block. A block ID is specified for each attached
terminator and 0 for each unattached terminator.
o+ For station blocks, the station-stop speed VSSB.
o+ For subblocks of cross blocks, the block ID of the cross block.
For each train:
o+ The ID T and length of that train.
o+ The locations of that train's head and tail ends. The location of an end
of a train is specified by giving the ID of a block containing that end and
the offset of that end within the block. Offsets are measured from the tail
terminator of a block.
3.3 Commands
A CPI client can cause a change in a Trainset railroad by issuing a command.
Such a change is called a layout action. Only trains and switch blocks can be
affected by commands.
40
Each command gives rise to at most one layout action. A command fails
to cause a layout action if the preconditions of that command are not satisfied.
For example, a SetDirection command causes a new direction to be assigned
to a train only if that train is operational and not moving. Also, multiple com-
mands may optionally be required to cause a single layout action, as described
in Section 3.5.
There are seven types of command that a CP! client can send to a Trainset
railroad: SetSwitch Accelerate, Decelerate, Setspeed, SetDirect ion,
Emergencystop and Stationstop. These are specified as follows.
3.3.1 Set Switch
enum NewPosit fNP?sTRAIGHT? NP?TURNED>;
void
SetSvitch(block?id, new?setting)
int block?id;
enum NewPosit ne??setting;
SetSwitchcauses the switch block identified by block?id to begin changing
setting to new?setting. A setting change requires %wjt? seconds to complete.
block?id must identify an unoccupied switch block that is neither in the
setting new?setting nor in the process of changing to that setting; otherwise
no action is taken.
3.3.2 Accelerate and Decelerate
void
AccelerateCtrain?id, duration)
int train?id;
double duration;
void
flecelerate(train?id, duration)
int train?id;
double duration;
Accelerate and Decelerate cause the acceleration atrain?id of the train
identified by train?id to be assigned the following value.
+ACC for Accelerate
Utrain?id = --HACC or Decelerate
41
atrain?id is assigned the value 0 after either duration seconds elapse or the
train's speed reaches 0. whichever comes first, unless ?train?id is changed again
or the train collides or derails.
duration must be non-negative, and train?id must identify an operational
train that is not performing an emergency stop; otherwise no action is taken.
3.3.3 Set Speed
void
SetSpeed(train?id, goa1?speed)
int train?id;
double goal?speed;
Setspeedcauses the acceleration itraifl?id of the train identified by train?id
to be assigned the following value,
atrain?id =
if goal?speed> Vtrain?id
if goal?speed = Vtrain?id
if goal?speed < Vtrain?id
The acceleration ?rain?id is assigned the value 0 when that train's speed
reaches goal?speed. unless ?train?id is changed again or the train derails or
collides before the goal speed can be reached.
goal?speed must be non-negative, and train?id must identify an opera-
tional train that is not performing an emergency stop; otherwise no action is
taken.
3.3.4 Set Direction
void
SetDirection(train?id, new?dir)
int train?id;
int new?dir;
Setflirectioncauses the direction dirtrain?id train identified by train?id
to be assigned the following value.
tail-to-head			if new?dir> 0
dirtrain?id =			head-to-tail			if new?dir < 0
opposite of dir?rain?i?			if new?dir = 0
Here, tail-to-head means that the head end of that train is the front end, and
head-to-tail means that the tail end is the front end.
42
The direction dirtrain?id of traizi?id must identify an operational train
that satisfies
atrain?id = Vtrain?id = 0
or no action is taken.
3.3.5 Emergency Stop
void
EmergencyStop(train?id)
int train?id;
Elergencystop causes the emergency-stop mode ?Strain?id of the train
identified by train?id to be enabled. This causes an emergency stop to begin
immediately. That train's acceleration ?rain?id is assigned the value --HAemer
untilthe train's speed Vtrain?idbecomes 0' at which time ?train?jdis assigned
the value 0 and C?rain?id is disabled.
train?id must identify an operational train, or no action is taken.
3.3.6 Station Stop
enwi StationstopMode ?SS?DISAHLED, SS?EJAHLED?;
void
StationStop(train?id, nev?va1)
int train?id;
enui StationstopMode neI?va1;
stationstopcauses the station-stop mode Sstrain?idofthe train identified
by train?id to be assigned new?va1. A station stop (see Section 2.3.2) begins
if an operational train enters a station block with station-stop mode enabled
and with speed ?train?id that does not exceed the block's station-stop speed
VSSs.
train?idmustidentify an operational train that is not performing an emer-
gency stop; otherwise no action is taken.
3.4 Queries
Queries are the only way that a CPI client can obtain information about a
layout after the initial-state download has been received. There are four query
types: GetHiockoccupancy, GetSwitchPos it ion, GetTrainStatus and
GetTrainMotion. These queries return very limited information. Thus, facts
43
about the layout's state such as a train's speed and emergency-stop mode must
be computed by a CP! client program from a knowledge of past values and of the
properties of Trainset railroads. This makes writing process control programs
difficult but realistic.
3.4.1 Block Occupancy
enul Occupancy <Oc?FREE, OC?OCCUPIED, OC?ERROR = -I>;
enuin Occupancy
GetHiockoccupancy(b1ock?id)
int b1ock?id;
The occupancy status of the indicated block is returned. If b1ock?id is not
the ID of a block or if a timeout occurs awaiting a reply from the simulation,
OC?ERROR is returned.
3.4.2 Switch Position
enum Switchposit ?sP?sTRAIGHT, SP?TURIED, SP?UJDEFIJEfl,
SP?ERROR -1>;
enum Switchposit
GetSwitchPosition(b1ock?id)
int b1ock?id;
The setting of the indicated switch block is returned. If b1ock?id is not the
ID of a switch block or if a timeout occurs awaiting a reply from the simulation,
SP?ERROR is returned.
3.4.3 ?ain Status
enum Trainstatus ?Ts?cRAsHEn, TS?RUJMIHG, TS?EM1OR -1>;
enum Trainstatus
GetTrainStatus(train?id)
int train?id;
If the indicated train has state operational, TS?RUINING is returned. If that
train's state is derailed or collided, TS?CRASHEfl is returned. If train?id is not
the ID of a train or if a timeout occurs awaiting a reply from the simulation,
TS?ERROR is returned.
44
3.4.4 Train Motion
enum TrainNotion ?TN?sTOPPED, TN?NOVING, TN?ERRoR = -1);
enum TrainNotion
GetTrainNotion(train?id)
int train?id;
If the indicated train has state operational and at least one of the train's
speed ?rain?id or acceleration ?train?id is non-zero, TN?NOVING is returned.
Otherwise, TN?STCPPED is returned, except that TN?ERRoR is returned if train?id
is not the ID of a train or if a timeout occurs awaiting a reply from the simula-
tion.
3.5 Voting
The voting service supports the writing of fault-tolerant control prograrns for a
Trainset railroad. By default, there is a one-toone relationship between CPI
client commands (satisfying various preconditions, as described in Section 3.3)
and layout actions. The voting service makes it possible to require that a simu-
lation receive a specified number Qvoting of commands from different CPI clients
within a certain time period Wvoting before a layout action can occur.
The behavior of the voting service is governed by the following parameters.
o+ A voting quorum Qvoting (integer). When the voting service is being
employed, a layout action can be generated only if Qvoting commands
satisfying the conditions below are received from different CPl clients.
o+ A voting window Wvo?ng (floating point, seconds). This is a time limit af-
ter which CPI client commands expire. If a command has not contributed
to a layout action within Wvoting seconds after receipt by a simulation,
then that command will have no effect.
o+ A sequence number seq (integer) that is associated with each CPI com-
mand.
We expect Qvoting> 0, Wvoting > 0 and seq> 0.
The voting service is enabled only for commands with positive
numbers. Figuratively speaking, commands with different positive
numbers participate in different "elections."
The following paragraphs provide a specification of the voting service.
sequence
sequence
Sequence number 0. If a command has sequence number 0, then that com-
mand alone generates a layout action immediately, as described in Section 3.3.
The values of Wvoting and Qvoting have no effect on such commands.
45
Command A
B
C
D
E
F
command			sequence
time			type			client number
7:22:06			Accel			2			61
7:22:13			Accel			1			60
7:22:17			Accel			3			61
7:22:19			Accel			1			60
7:22:25			Setspeed			2			61
7:22:30			Setspeed			3			61
Figure 3.2: Voting example (positive sequence numbers).
Positive sequence numbers. If a simulation receives Qvoting or more com-
mands with the same positive sequence number seq from different CPI clients
during any time period of Wvotiiig seconds or less, then a single layout action is
generated. By default, the layout action generated is the action caused (subject
to the preconditions listed in Section 3.3) by the last of those commands,
No commands with positive sequence numbers can otherwise contribute to
layout actions, Commands with the same sequence number need not have the
same arguments nor even the same command type. A command can contribute
to the generation of at most one layout action.
For example, suppose that Wvoting = 10 sec, Qvoting = 2 and that the
commands in Figure 3.2 are received by a simulation. Then only one layout
action will be performed by that simulation as of the time 7:22:30. That layout
action will be E, occurring at time 7:22:25. The quorum of commands consists
ofC and E.
The values of Qvoting and Wvoting are determined when a Trainset railroad
simulation is invoked, and cannot be changed during a simulation. The default
value for Q?ting is 2, and the default value for Wvoting is 5 seconds.
Sequence numbers can be modified dynamically by a control program. The
ACI provides an internal sequence-number variable seq??J whose value is auto
matically sent with each command. The value of the variable Seq??? remains
constant unless explicitly modified, and the initial value is 0.
In order to employ the voting service, it is necessary to start a simulation
with Qvoting> 1 and to send commands that have positive sequence numbers.
The ACI provides two functions for modifying the value of a sequence number.
46
3.5.1 New Sequence Number
int
NewSeqJumber()
The sequence number is incremented and the new value is returned.
3.5.2 Set Sequence Number
int
SetSeqlinnber(new?val)
int new?val;
Ifnew?val is non-negative, the sequence number s?q is assigned new?val,
and that value is returned. Otherwise, seqis left unchanged and -i is returned.
3.6 Timer Facilities
The ACI layer provides procedures that implement a simple timer service in
which time quantities are represented as seconds in floating point. These rou-
tines affect a program that calls them; they do not themselves interact with
a Trainset railroad. Alternative time-management routines or direct system
calls may be used instead of these timer procedures when writing programs that
use the ACI. Thus, use of the ACI timer facilities is optional.
The five timer procedures are lnitTiiner, GetTimer, AwaitTimer, Cancel-
Tiier and Sleep.
3.6.1 Initialize Timer
enum TimerReturn <TNR?SUCCESS, TNR?ERROR : -I>;
typedef double Seconds;
enul TiierReturn
lnitTiier(first, period, f)
Seconds first; /* delay to first tizneout */
Seconds period; /* wait between successive tiieouts */
void (*f)(); /* function executed upon each timeout */
InitTimerinitializes the timer mechanism toexecute the function f() after
first seconds and every periodseconds thereafter. Iffirst is zero, the timer
isimmediatelydisabled. Ifperiodis zero, the timerexecutes f() at mostonce.
47
If the value zero is specified instead of a function f, then nothing is done on
timeouts except to reset the timer as appropriate. TNR?SUCCESS is returned
if the operation succeeds. If a time argument is negative or a low-level error
occurs, TNR?ERROR is returned.
A call to InitTimer overrides any prior timer setting. Thus, there is no
provision for more than one timer being in effect at any time. The timers used
in other ACI functions, including GetDownload, the ACI queries and Sleep, do
not interfere with InitTimer.
3.6.2 Get Timer Values
enus TimerStatus fTMS?DISABLED, TNS?oIcE, TNS?INDEFINITE,
TNS?ERROR = -I>;
typedef double Seconds;
enus TimerStatus
GetTinter(nextp, periodp)
Seconds *nextp; /* next scheduled timeout */
Seconds *periodp; /* delay between successive timeouts */
GetTimer stores the seconds remalning until the next timer expiration in
*nextp and stores the current period between timer expirations in *periodp.
The return value is TNS?flISABLED ifthe timer is currently disabled, TMS?OICE
if the timer is scheduled to expire once only, TNS?IJDEFII1TE if the timer is
scheduled to continue indefinitely and TMS?ERR0Rifalow-level error occurs.
3.6.3 Await for Timer
void
AwaitTimer()
AwaitTimer suspends the calling process until an ACI timer expiration or
another operating system signal occurs.
3.6.4 Cancel Timer
enui TimerReturn ?TNR?SUCCESS, TMR?ERRaR
enum TimerReturn
CancelTimer()
48
CancelTimer cancels an ACI timer. TMR?SUCCESS is returned if the oper-
ation succeeds. CancelTiiner fails if a low-level error occurs, in which case
TNR?ERRORis returned.
3.6.5 Sleep
enum TimerReturn ?TNR_SUCCESS TNR?ERROR = -1>;
typedef double Seconds;
enus TiierReturn
Sleep(sec)
Seconds sec;
max nusber of seconds to suspend */
Sleep suspends the calling process for indicated number of seconds. The
value TMR?SUCCESS is returned if the suspension was not interrupted before the
time limit expired. Otherwise, TNR?ERROR is returned.
Sleep does not interfere with the operation of InitTimer. Thus, these
functions may be used together, e.g., to set up a polling loop with a timeout.
49
Chapter 4
Low-Level Interface
4.1 Introduction
The Low-Level Interface (LLI) consists of predefined message formats and re-
lated facilities for communicating with a Trainset railroad simulation. This
layer is used to implement the ACI layer in the Control Program Interface (see
Figure 3.1).
This chapter gives a sketch of the LLI layer. A reading knowledge of the
C programming language is assumed. The rest of this chapter includes the
following sections.
Section 4.2 is external documentation for the source file cpi .h (see Ap-
pendix C.1). The internal structure of that file is described, and the source
lines that pertaln to the acceleration command are discussed as examples.
o+ Section 4.3 describes the steps necessary for a client to connect and identify
itself to a Trainset rallroad simulation.
Sections 4.4 and 4.5 briefly list the message formats that are used for
initial-state downloads, commands and queries. A knowledge of the ACI
layer (Chapter 3) is assumed.
For a programming example that uses the LLI programming layer, see the
implementation of the ACI layer in the source file aci. c (Appendix C.2).
4.2 Messages
In the LLI layer, a control program interacts with a Trainset railroad simula-
tion by sending and receiving messages. An LLI message is an ASClI character
string that is organized according to one of the message formats defined in
50
[zi
Li
#define NSG?CPI?ACCEL "XaY.d,7.d:Y.d:Y.lf#"
typedef struct ?
int client?id; /* unique identifier for this client */
int seq?num; /* label for this conintand */
int train?id; /* simulator train number */
double duration; /* time to accelerate in seconds */
> NsgcPlAccel;
#define NSG?CPI?ACCEL?ARGC 4
#define MsgcPlAccelArgs(buff, datap) (buff, NSG?CPI?ACCEL, \
datap->client?id , datap->seq?num , datap->train_id datap->duration )
[z3
#define CreateNsgcPlAccel(buff, datap) sprintf NsgcPlAccelArgs(buff, (datap))
#define ParsessgcplAccel(buff, datap) sscanf NsgcPlAccelArgsCbuff, a(datap))
Figure 4.1: Excerpt from cpi h relating to acceleration command.
cpi h. Each such message format consists offi?ids for representing data, to-
gether with an identifying header and delimiter characters.
For example, consider Figure 4.1, an excerpt from cpi h. Line ? defines a
message format called MSG?CPI?ACCEL. That message format has the following
syntax.
Xa (fflteger? , (integer?			(integer? : (fioat? #
There are four fields. The notations (integer) and (float) indicate ASCIl repre-
sentations ofinteger and floatingpoint numbers. The total length ofa message
depends on the lengths ofthe data in its fields.
In general, for each message format in cpi h there is a type definition of
an associated data s&ttcture whose components correspond to the fields of that
message format, in the same order.1 For MSG?CPI?AccEL, the associated data
structure is called MsgcplAccel. See the lines near [J2 in Figure 4.1.
A comparison of the message format MSG?CPI?ACCEL and the documented
data structure NsgcPlAccel shows that the first (integer) field in that message
format holds the client ID,2 the second (integer) field holds the voting sequence
number of an Accelerate command, etc.
For each message format, a macro has been defined that produces a string
in that message format, using values stored in a variable of the associated data
1Four OPI message formats are associated with basic data types that are not explic-
itly defined as structures in cpi.h. These are MSG?CPI?IIT (string), MSG?CK (string),
MS0?PIJ)0WJLD?0IE (integer) and MS0flUITJLLL (string).
2A `mique client LD is assigned to each OPI client when that client connects to a simulation,
as described below.
51
type for the fields. Another macro has been defined for parsing such a mes-
sage into a variable the associated data type. These macros are provided as
a convenience. In the case of NSG?cPI?AccEL, the creation macro is called
CreateNsgcPlAccel and is defined by line ? in Figure 4.1. The message-
parsing macro is ParseNsgcplAccel defined on line
In general, for any such macro associated with a message format:
There are two arguments, a character string buffer holding the message
and a pointer to a variable of the associated data structure type holding
the values.
The run-time replacement value of such a macro is the length of the re-
sulting message, not counting a null byte that is placed at the end of that
message string.3
Thus, the macro CreateNsgcPlAccel behaves like a function with the following
heading.
int
CreateNsgcplAccelCbuff datap)
char buffO; /* resulting message is placed here */
NsgCPIAccel *datap; /* points to data values */
4.3 Initiating Communication
Establishing communication between a client and a Trainset railroad simula-
tion requires two steps.
1.
2.
Connection. Create a network connection between that client and that
simulation. A simulation provides well-known ports for supported commu-
nication protocol families (e.g., TCP/IP, DECnet) and is capable of socket
or stream transmissions in each case. The function GetSiiport defined in
the source file lib/syslocal. c returns the well-known port, given a base
port (provided in lib/syslocal.h), a host name and an integer simn?m
that distinguishes between multiple simulations running on the same host.
Use standard techniques to open one bidirectional communication line.
?gistraticn. Send a registration message to that simulation to declare
a client type (CPI or monitor) and receive a client ID. A registration
message for a CPI client has the message format MSG?cPI?IIIT
CPI (one space? (string? Y.
3Sending a terminating null byte to a simulation as p&t of a message is optionai.
52
where (string? is any sequence of characters that are not the percent sign
Y. and is conventionally the file name of that client's executable. The sim-
ulation replies with an acknowledgement message in the format NSG?ACK,
as follows.
ack (one space? (integer? # 7.
The (integer? field in that message represents the unique client ID number
to be used in all commands and queries from that client to the simulation.
The macros CreateNsgcpllnit and ParseMsgAck may be used if desired.
We assume that each client creates only one connection with a simulation.
For the remainder of this chapter, message formats will be cited by name
only, omitting mention of syntax, related macros. etc. See Appendix C.1 for
syntax detalls.
4.4 Initial-State Download
After initiating communication with a simulation, a CPI client can receive a
report of the current global state of the railroad being simulated. Such a report
is called a download.
To request a download, a control program must send a message to a simu-
lation using the message format NsG?cPI?DowILn?REqUEsT. The acknowledge-
ment from the simulation is the download itself. The download consists of
messages in the following DOWILD formats.
o+ NsG?cPI?DoWMLD?GEJERAL transmits constants that describe a railroad in
general, eg., the number of blocks.
o+ MSG?CPI?DOWJLD?TRAIJ describes attributes of trains. One message in
this format is sent per train.
o+ NSG?CPI?DOWJLD?BLocK describes a block. NSG?CPI?DOWILD?LIJK gives
block attachments for a block as described in Section 3.2. One message in
each of these formats is sent per block.
o+ NSG?CPI?DCWJLD?DoME marks the end of a download.
Only one download request is honored by a simulation per client connection.
4.5 Commands and Queries
o+ MSG?CPI?SET?SWITCH, MSG?cpI?AccEL, NSG?CPI?DEcEL,
NSG?CPI?sET?spEED, NSG?CPI?sET?DIR, NSG?CPI?EMER?sToP and
NSG?CPI?STA?sToP request the commands described in Section 3.3. No
acknowledgement message is sent in response to such a command.
53
o+ NSG?CPI?BLDCK?OCC requests a block's occupancy status. A simulation
responds with a message4 0" if that block is occupied or "f" (for "free")
if it is not occupied.
o+ NSG?CPI?SWITCH?POSIT asks for the current setting of a switch block. The
response is "s" for straight, "t" for turned, or "u" for undefined.
o+ NSG?CPI?TRAIN?STATUS inquires about a train's status. A simulation re-
sponds with "r" for running (operational state) or "c" for crashed (col-
lided or derailed states).
o+ NSG?CPI?TRAIJ?MOTION queries whether a train is moving. The response
is "s" for stopped or "ii" for moving. A train is moving if it is operational
and at least one of its speed and acceleration is non-zero. A train is
stopped if it is not moving.
4.6 Quit Message ?om the Simulator
Just before a Trainset simulation exits normally, it sends a message in
the format MsG?qUIT?ALL to all of its clients. Receipt of this message is a
certain indicator that a simulation has finished.
4Note: One-character return strings such as "0" are sent as two-byte messages from the
simulator, including the terminating null byte.
54
Appendix A
Constants Used by Trainset
Constant			Typical			Page
name			Description			value			references'
ACC			Standard acceleration rate for trains			1.5 mIsec2 36, 40, 41, 42
Aemer Emergency-stop acceleration rate for trains 2.5 m/sec2 36, 37, 40, 43
LENci?ss Length of a cross block			35 m			33
LEA??01n Length of a join block			35 m			32
LENswit? Length of a switch block			35 m			34
LB			Length of block 3			35 to 300 m 30
LT			Length of train T			150 to 500 m 35
MNB			Minimum speed limit on block B			0 mIsec			30, 36, 40
MXs			Maximum speed limit on block B			60 m/sec			30, 36, 40
Nblocks			Number of blocks in a layout			5 to 50			40
Ntrains			Number of trains in a layout			1 to 3			40
Qvoting			Voting quorum			2			40, 45, 46
Tswitctt			Time for a switch block to diange settings			2 sec			33, 40, 41
VSSs			Station stop speed for a station block			30 m/sec			34, 37, 40, 43
Voting window			5 sec			40, 45, 46
Wvoting
1A bo1d?ce number indicates the primary reference for a symbol.
55
Appendix B
Code Listings: ACI
B.1 aci.h, Interface Header File
/*
aci,h -- header file for ACI.
This file defines types and constants and declares functions
found in the ACI library libaci.a
Created by R. Brown, 4/91 */
/* all units are inks unless other?ise indicated */
* constant definitions
#ifndef
#define
#endif
#ifndef
#define
#endif
NAX?TRAIIS
NAX?TRAIJs 10 /* iaxim'ii number of trains in a layout */
MA1?BLOCKS
MA1?BLOCKs loO /* maximum number of blocks in a layout */
* type definitions for storing a state download
56
/* HlockType specifies the type of a block */
enuin BlockType ?BT?REGULAR, BT?sTATION, BT?SWITcH, BT?JOIN, BT?CROSS?;
/* BlockTypeData represents data that is specific to a type of block */
union BlockTypeflata ?
struct ? /* BT?REGULAR blocks */
int tail, head; /* terminators */
? rg;
struct ? /* BT?STATIDI blocks */
int tail, head; /* terminators */
double sta?stop?speed; /* station stop speed */
? st;
struct ? /* BT?SWITCH blocks */
int tail, straight, turn; /* ternLinators */
> sv;
struct < /* BT?JOIN blocks */
int tail, headi, head2; /* terminators */
> jn;
struct ? /* HT?cRoss (sub)blocks */
int tail, head; /* terminators */
int cross?id; /* block ID of this
> cr;
subblock's cross block */
/* Hlockflata represents the doinload inforination for a specific block */
typedef struct Blockflata <
int block?id; /t identifies the block */
enus HlockType type;
double length;
double niax?speed, min?speed; /* trains derail that violate these liiits */
union BlockTypeData t; /* additional data that depends on block type */
> BlockData;
57
/* Location specifies a position within a block */
struct Location ?
int block; /* id of a block containing the location */
double offset; /* distance from tail of that block to the location */
> Location;
/* Trainflata represents the download information for a specific train */
typedef struct TrainData ?
int train?id; /* identifies the train */
double length;
struct Location head, tail; /* init positions of train's ends */
> Trainflata;
/* Layoutnata represents all information received in a doinload */
typedef struct LayoutData <
int block?ct; /* number of blocks */
int train?ct; /* number of trains */
double acc, emer?acc; /* standard and emergency stop acceleration rates*/
double switch?time; /* time required for switch block to change posit */
int voting?quorum; /* number of agreeing commands required for an action */
double voting?window; /* time limit after which commands expire */
Hlockflata blocks(MA1?BLOCKS]; /* block-specific data */
Trainflata trains(NAX?TRAIIS]; /* train-specific data */
> LayoutData;
/*
* ACI function declarations, with associated type definitions
/* GetDownload, for connecting to a simulated railroad and receiving
a report of that railroad's state. */
Layoutflata *Getflownload();
58
/* ACI commands */
enuni Neiposit ?NP?STRAIGHT, NP?TURNED>;
void Setswitch();
void Accelerate();
void Decelerate();
void Setspeed();
void Setflirection();
void Einergencystop();
enini StationstopMode ?SS?DISABLED, SS?ENABLEfl>;
void Stationstop();
/* ACI queries */
enum Occupancy <OC?FREE, OC?OCCUPIED, OC?ERROR = -1>;
enun Occupancy GetBlockoccupancy();
enwi SwitchPosit ?SP?STRAIGHT, sP?TURIED, SP?UJDEFIIED, SP?ERROR : -1>;
enunt SwitchPosit Getsvitchposition();
enuin TrainStatus <TS?CRASHED, TS?RUJJIIG, TS?ERROR =
enuin TrainStatus GetTrainStatus();
enun TrainMotion ?Th?STOPPED, TN?NOVIJG, TN?ERROR = -1>;
enun TrainMotion GetTrainMotion();
/* changes to voting service sequence nunber */
int Setseqlunber();
int JewSeqlunber();
59
/* tinLer facility */
typedef double Seconds;
enum TimerReturn ?TNR?SUCCESS, TMR?ERROR			-1>;
enum TimerReturn InitTimer();
enum TimerStatus ?TNS?DISABLED, TNS?OJCE, TMS?INDEFIIITE? TNS?ERROR			-1>;
enum TimerStatus GetTimer();
void AwaitTimer();
enum TimerReturn CancelTimer();
enum TimerReturn Sleep();
B.2 acitest . c, Example Using the ACI Inter-
face
/* $RCSfile: acitest.c;v $ $Revision: 1.8 $ $flate: 92/07/17 10:58:43 $ */
/* acitest.c -- manual demonstration of the ACI interface. */
#include <stdio .
#include `?aci.h'
#define TIMEOUT 60.0 /* seconds to contact the simulator */
#define NAILIJE loo /* maximum input length */
#define lORNAL 0 /* exit code for normal exit */
#define ERROR 1 /* exit code for error exit */
#define USAGE `Usage: Y.s (-d) (simhost Esimnum))\n" /* diagnostic msg */
#define OUIT?COMMAJn o+`q" /* user entry for quit cofland */
enum cofland?type ?
/* coiniands */
SET?SWITCH : 1, ACCELERATE? DECELERATE, SET?SPEED,
SET?DIRECTIOJ, ENERGEJCY?STOp, STATIOJ?STOP,
/* queries *1
GET?BLOCK?OCCUPAJCy, GET?SWITCH?POSITIOJ, GET?TRAIM?STATUS, GET?TRAIJ?MOTIOM,
/* other */
SET?SED?JUMBER, JEw?SEq?JUNBER,
qUIT, HELP
60
struct command <
enum
char
char
char
command?type type; /* type of this command or query */
*code; /* entry code for this command or query */
*name; /* full name of this ACI command or query */
*args; /* names of arguments required for this command or query */
1;
/* NULL-terminated table of command types, codes and names
struct command command?tableO =
/* commands */
SET?SWITCH, "5w", "SetSwitch", "<block?id> <new?posit>"
ACCELERATE, "ac", "Accelerate", "<train?id> <duration>",
DECELERATE, "dc", "Decelerate", "<train?id> <duration>"
SET?SPEED, "sp", "SetSpeed", "<train?id> <goal?speed>",
SET?DIRECTION, "sd", "SetDirection", "<train?id> <new?dir>"
ENERGEICY?sToP, "es", "Emergencystop", "<train?id>",
STATION?sTDP, "st", "Stationstop", "<train?id> <new?val>",
/* queries */
GET?BLOCK?CCCUPANCY, "0", "GetHiockoccupancy", "<block?id>",
GET?SWITCK?POSITIOJ, "p", "GetSwitchPosition", "<block?id>",
GET?TRAIJ?STATUS, "s", "GetTrainStatus", "<train?id>",
GET?TRAIJ?NOTIOJ, "1", "GetTrainNotion", "<train?id>",
/* other */
SET?SEq?NUMBER, o+`sn", "SetSeqluinber", "<new?val>",
NEw?SEq?NUNHER, "nn", "Newseqlumber , -
qUIT, qUIT?CONMAJD, "quit'
HELP, "?", "Kelp", " h help",
(enum command?type) 0, (char *) 0, (char *) 0, (char *) 0
/* Whitespace takes a char argument c. Evaluates to non-zero if
c is ihitespace, zero otherwise */
#define Whitespace(c) Cc =:			I c			`\t'			c :=
/* CommandLookup determines the conand corresponding to a code
entered to identify a command.
Any whitespace characters at the beginning of entry are ignored.
61
Returns the index in the table of the indicated conand, or
-1 if the code was empty or the given entry code was not found. */
int
CommandLookup(entry)
char *entry; /* entry code to be interpreted as a command */
struct command *p; /* pointer into command table */
while (*entry aa Whitespace(*entry))
entry++;
if (!*entry)
return (-I);
/* entry is non-empty string beginning with a non-whitespace char *1
if (*entry ==
entry =
/* if request is for Help then entry has value `?" */
for (p : command?table; p->type;
if (?strcmp(entry, p->code)) <
return (p - command?table);
/* entry does not match a code in the table */
printf(" no match found\n");
return (-1);
/* PrintDownload displays every field received in a download */
void
Printflownload(dp, simhost, simnum)
Layoutnata *dp; /t data received from doinload */
char *simhost; /* name of the host systel running the simulator */
int simnul; /* identifier for a running simulation on that host */
pointer to data for a specific train */
pointer to data for a specific block */
62
int i; /* loop control */
Trainflata			*tp;			/* utility
BlockData			*bp;			/* utility
printf(Contents of download from simhost Y.s, simnum Y.d:\n",
simhost, simnum);
printf("\nGeneral parameters:\n');
printf(" There are Y.d blocks and 7.d trains\n",
dp->block?ct, dp->train?ct);
printf(" Train acceleration rates (m/sec): standard Y.f, emergency stop 7.f\n
dp->acc, dp->emer?acc);
printf(' Switches take Xf seconds to change position\n"
dp->switch?time);
printf(' The voting quorum is Y.d and the voting window is Y.f seconds\n"
dp->voting?quorum, dp->voting?window);
printf(`?\nBlocks
for (i=O; i < dp->block?ct;
bp = &dp->blocks(i];
printf(" Block 7.d has type
switch (bp->type) <
case BT?REGULAR:
printf(?'REGULAR'?);
break;
case BT?STATIOI:
printf("STATIOl");
break;
case BT?SWITCH:
printf("SWITCH");
break;
case BT?J()IJ:
printf("JDIJ");
break;
case HT?CROSS:
printf("CROSS");
break;
i++) <
bp->block?id);
printf(" and length titf meters\n", bp->length);
printf(" Its speed limits are Y,f (minimum) and Y,f (maximum)\n",
bp->iin?speed, bp->max?speed);
printf(" Its terminators are connected to blocks ");
switch (bp->type) ?
case BT?REGULAR:
printf("Y?d (head) and Y?d (tail)\n",
bp->t.rg.head, bp->t rg.tail);
break;
63
case HT?STATION:
printf("Y,d (head) and Y.d (tail)\n",
bp->t St.head, bp->t.st tail);
printf(" Its station stop speed is Y.f m/sec\n"
bp->t.st sta?stop?speed);
break;
case BT?SWITCH:
printf("7,d (straight head), Y.d (turn head) and Y,d (tail)\n",
bp->t.5w.straight, bp->t.5w.turn, bp->t.sw.tail);
break;
case BT?JOI1:
printf("Y,d (headi), Y,d (head2) and Y,d (tail)\n"
bp->t.jn.headi, bp->t.jn.head2, bp->t.jn.tail);
break;
case BT?CROSS:
printf("Y,d (head) and Y,d (tail)\n"
bp->t.cr.head, bp->t.cr.tail);
printf(" This is a subblock of the cross block with ID Y,d\n',
bp--H>t.cr.cross?id);
break;
printf("\nTrains:\n");
for (i=O; i < dp->train?ct; i++) ?
tp = adp->trainsEi];
printf(" Train 7.d has length Xf meters\n',,
tp->train?id, tp->length);
printf(" Its head end is in block Y,d, Y,f meters from tail terminator\n"
tp->head.block, tp->head.offset);
printf(' Its tail end is in block Y,d, Xf meters from tail terminator\n",
tp->tail.block, tp->tail.offset);
printf("\nEnd of configuration download report\n\n");
return;
/* GetValue attempts to locate a value in the string buff.
If buff contains only whitespace, prompt is printed and standard input
is read until some non-whitespace characters are entered.
64
If a value matching the printf format fit is found in buff or stdin,
then its value is placed in *valp and a pointer is returned that points
to the first whitespace character after the value.
If the next non-whitespace characters do not scan according to fit,
an error message requesting user to try again is printed and
MULL is returned. */
char *
Getvalue(buff, fit, valp, prompt)
char *buff; /* buffer that may contain a value */
char *fmt; /* printf-style format for reading the value */
char *valp; /* to receive value if one is found */
char *prompt; /* prompt, used to obtain standard input */
char *p; /* utility pointer */
/* invar: buff is unexamined */
do ?
for (p = buff; *p aa whitespace(*p); p++)
/* *p is null-byte or first non-whitespace */
if (!*p) <
printf(" 7.s: o+`, proipt);
gets(buff);
p = buff;
/* *p is first non-whitespace char, or p points to beginning of
an unexamined (yet possibly empty) buff */
> while (!*p II Whitespace(*p));
/* *p is non-whitespace */
if (sscanf(p, fit, valp) I: 1) <
printf("Error reading value -- try again\n");
return (lULL);
/* value was successfully read into *valp */
while (*p aa !Whitespace(*p))
return (p);
65
/* Getint and GetFiQat are macros for getting values of certain types
from buffE] or standard input, via GetValue above */
#define Getlnt(buff, vaip, proipt) GetValue(buff, "Y.d", vaip, prompt)
#define GetFloat(buff, vaip, prompt) Getvalue(buff, "Y.lf", vaip, prompt)
main(argc, argv)
int argc; /* number of command line arguments remaining */
char *argvE]; /* list of command line arguments remaining */
char *progname; /* name of this program */
char simhost(NAXLIJE+1); /* name of host system running simulator */
int simnum = 0; /* number of this simulator */
int print?download = 0; /* boolean; set non-2ero to print doinload */
char buff(NAXLIJE+1]; /* buffer for interactive input */
Layoutflata *datap; /* data received from doinload *1
int index; /* index of a command table entry */
char *p, **tablep; /* utility pointers */
struct command *commandp; /* pointer into command table */
char prompt()(AXLIIE); /* holds any prompts for user input */
p = progname = *argv++; argc--;
1* invar: there are no slashes in the string progname before p */
while (*p)
if (*p++ ==
progname :
/* progname is null-terminated program name after stripping directories */
if (argc > 0 ?& **argv == ?-`) <
a flag was encountered */
if (Istrcmp(*argv, o+-d")) ?
print?download++;
argc--; argv++;
1 else <
fprintf(stderr, USAGE, progname);
exit (ERROR);
/* all flags have been processed */
66
if (argc > 0) <
a next argument exists, presumed to be simhost */
if (strlen(*argv) > MAXLIJE) <
fprintf(stderr, `simulator host name too long, max Y.d\n", MAXLINE);
exit (ERROR);
/* first arg gill fit in simhost */
strcpy(simnost *argv++);
argc--;
else <
no more conand-line arguments remain */
printf("Enter name of host running simulator ELOCAL HOST): ");
if (!gets(simnost)) <
fprintf(stderr, "error getting host name\n");
exit (ERROR);
printf("Enter simulator number on that host (0]:
if (`.gets(buff)) <
fprintf(stderr, "error getting simulator number\n");
exit (ERROR);
simnum = (*buff) ? atoi(buff) : 0;
/* simnost contains (possibly empty) null-terminated string AID
if no argument remains then simnum holds specified or default value */
if (argc > 0) <
/* another argument second parameter exists, presumed to be simnum */
sscanf(*argv++, "7.d", asimnum);
argc--;
/* simnum holds specified or default value *1
printf("Connecting to simulator using %&.If second timeout.. .\n", TINEOUT);
if (!(datap Getflownload(simhost, simnum, progname, TINEOUT))) ?
fprintf(stderr, "Getflownload failed\n");
exit (ERROR);
/* download was successfully received */
if (print?download)
67
PrintDownload(datap, simhost, simnum);
printf("Enter ? for a list of commands\n);
for (;;) <
do <
printf('Y.s> ", progname); /* prompt */
buff(O] :
if (!gets(buff))
/* end of input encountered */
sprintf(buff, qUIT?CONNAJD);
for (p:buff; *p aa wbitespace(*p);
/* p points to first non-whitespace char of buff, or null byte */
> while (I*p);
/* buffE] contains a new non-empty null-terminated comnand */
while (*p aa !Whitespace(*p))
/* *p is null--Hbyte or first whitespace after comnand code */
if (*p)
/* buff is null-terminated string holding a non-empty conand code
consisting of non-whitespace chars, and holding QUIT?CONNAID on EoF,
AND string p is any remainder of the (null-terminated) input line */
if ((index : CommandLookup(buff)) -i) ?
printf(Unrecognized conand code C?,?? (enter `?` for help)\n?,
buff);
continue;
1* a recognized conand code was entered */
/* gather arguments for the appropriate ACI routine and call it */
switch (conand?table(index] type) <
case SET?SWITCH:
int block?id;
enwi JewPosit
/* identifier of the switch block */
new?posit; /* desired position */
p = Getlnt(p, ?block?id, o+Setswitch. ID of switch block'?);
68
if (!p) continue;
sprintf(prompt, "New position (Y.d = straight, Y.d = turned)",
NP?STRAIGHT, NP?TURJEn);
p = Getlnt(p, Anew?posit, prompt);
if (!p) continue;
/* all arguments successfully obtained */
SetSwitch(block?id, new?posit);
break;
case ACCELERATE:
case DECELERATE:
int train?id; /* identifier of the train */
double duration; /* number of seconds to accelerate */
sprintf(prompt, "7.5. ID of train", conandtable(index].name);
p = Getlnt(p, ?train?id, prompt);
if (!p) continue;
sprintf(prompt, "Seconds to Y,s", command?table(index].name);
p = GetFloat(p, &duration, prompt);
if (Ip) continue;
/* all arguments successfully obtained */
if (coaand?tableEi::dez] ?yp?			ACCELERATE)
Accelerate(train?id, duration);
else
Decelerate(train?id, duration);
break;
case SET?SPEED:
int train?id; /* identifier of the train */
double goal?speed; /* speed to accelerate or decelerate to *1
p : Getlnt(p, etrain?id, "SetSpeed. ID of train");
if (!p) continue;
p : GetFloat(p, agoal?speed, "Jew goal speed");
if (!p) continue;
69
/* all argulnents successfully obtained */
SetSpeed(train?id, goal?speed);
break;
case SET?DIRCTIOJ:
int train?id; /* identifier of the train */
int new?dir; /* indicator of new direction */
p Getlnt(p, ?train?id, "SetDirection. ID of train");
if (!p) continue;
p = Getlnt(p, knew?dir,
"Jew direction (+1 for front=head, -1 for front:tail, 0 to toggle)");
if (!p) continue;
/* all arguments successfully obtained */
SetDirection(train?id, new?dir);
break;
case EMERGEJCY?STOP:
int train?id; /* identifier of the train */
p : Getlnt(p, ktrain?id, "Elergencystop. ID of train to stop");
if (`.p) continue;
/* all arguments successfully obtained */
EmergencyStop(train?id);
break;
case ST1TIOJ?STOP:
int train?id; /* identifier of the train */
enum StationstopMode new?val;
p Getlnt(p, atrain?id, "Stationstop. ID of train");
if (!p) continue;
sprintf(prompt, "Jew mode CXd disabled, Y,d : enabled)",
70
SS?DISABLEDr SS?EIABLEfl);
p = Getlnt(p, &new?val, prompt);
/* all arguments successfully obtained */
StationStop(train?id, new?val);
break;
case GET?HLOCK?OCCUPAJCY:
int block?id; /* identifier of the block
enum Occupancy occ; /* return value from
the query */
p = Getint(p, ?block?id, "GetBlockOccupancy. ID of block');
if (ip) continue;
/* all arguments successfully obtained */
occ : GetHlockOccupancy(block?id);
if (occ :: OC?FREE)
printf("Block `itd is FREE (unoccupied).\n", block?id);
else if (occ :: Oc?Occ?IED)
printf("Block Xd is OCCUPIED.\n" block?id);
else /* occ =: OC?ERROR */
printf("Unable to obtain occupancy for block Y.d\n", block?id);
break;
case GET?SWITCH?POSITIOJ:
int block?id; /* identifier of the switch block */
enum Switchposit posit; /* return value from the query */
p = Getlnt(p, block?id, "GetSwitchposition. ID of switch block");
if (ip) continue;
/* all arguments successfully obtained */
polit = GetSwitchPosition(block?id);
if (posit =: SP?STRAIGwT)
printf("Switch block o+/.d is STRAIGHT\n", block?id);
else if (posit == SP?TUuED)
printf("Switch block Y.d is TURJED\n", block?id);
else if (posit == SP?UIDEFI1ED)
71
printf("Switch block Y.d has UNDEFINED posftion\n'? block?id);
else /* posit =: sP?ERROR */
printf(Unable to obtain position of switch block Y.d\n?, block?id);
break;
case GET?TRAIJ?STATUS:
int train?id; /* identifier of the
enum TrainStatus status; /* return
train */
value front the query */
p = Getlnt(p, ?train?id, IGetTrainstatus. ID of train'1);
if (!p) continue;
/* all arguments successfully obtained */
status = GetTrainStatus(train?id);
if (status :: TS?CRASHED)
printf(1Train `/.d has CRASHED\n"
else if (status == TS?RUNJIMG)
printf("Train Y1d is RUNIIMG\n"?
else /* status == TS?ERROR */
printf("Unable to obtain status
break;
case GET?TRAIM?NOTIOJ:
int train?id; /* identifier of the
enum TrainMotion motion; /* return
train?id);
train?id);
of train Y1d\n", train?id);
train */
value from the query */
p = Getlnt(p, ?train?id, "GetTrainNotion. ID of train");
if (!p) continue;
/* all arguments successfully obtained */
motion = GetTrainNotion(train?id);
if (motion Th?STOPPED)
printf("Train 7.d is STOPPED\n", train?id);
else if (motion == TM?NOVI'G)
printf("Train `itd is MOVIJG\n"? train?id);
else /* motion =: TN?ERRoR */
printf("Unable to obtain motion information for train Y.d\n",
72
train?id);
break;
case SET?SE??NUNBER:
int new?val;
int seq?num;
new value for sequence number */
/* return value from Setseqlumber */
p = GetI:nt(p, ?ew?val, `Setseqlumber. New sequence number value');
if (!p) continue;
/* all arguments successfully obtained */
seq?num SetSeqNumber(new?val);
printf("Current sequence number value: Y.d\n", seq?num);
break;
case JEW?SEq?IUNBER:
int seq?num;
/* return value from Mewseqlumber */
seq?num = Jewseqlumber();
printf("Current sequence number value: Y.d\n", seqnum);
break;
case ?UIT:
char confirmENAXLIJE+1];
printf(1,quit.
gets(confiri);
/* confirmation
if (*confirm ==
exit (JORMAL);
break;
/* confirmation string *1
Are you sure you want to quit?\n (y/n, default y) ");
response has been received */
II *confirm == ?y' II *confirm ==
73
case HELP.
struct coninLand *cp; /* pointer into command?tab1e */
printf("Y.-34sY.s\n
entry#?? "ACI command'?);
for (cp = conand?tab1e; cp->code; cp++) ?
printf("Y.-4sY.-3OsY.s\n', cp->code? cp->args, cp->naae);
break;
default:
printf(`Unrecognized co!Bmand \"7.s\"\nEnter ? for help\n", buff);
break;
74
Appendix c
Code Listings: LLI
C.1 cpi .h, Interface Header File
/* $Rcsfile: cpi.h,v $ $Revision: 1.65 $ $Date: 92/11/24 14:41:00 $ */
/* cpi.h -- cpi message formats. */
/* This file automatically generated by m4 from cpi.hm */
/* Messages for initial and final communication with simulator */
/* first message for simulator, identifying self as a CPI client */
#define NSG?CPI?IJIT "CpI o+/.sW/."
#define MSG?CPI?INIT?ARGC 1
#define CreateMsgcpllnit(buff, str) sprintf(buff, NSG?CPI?IJIT, (str))
#define ParseMsgcpllnit(buff, str) sscanf(buff, MSG?CPI?IJIT, (str))
/* standard ac?owledgement message format; client ID is return value */
#define MsG?AcK "ack Y.s'/.7."
#define MSG?ACK?ARGc 1
*define CreateNsgAck(buff, str) sprintf(buff, MSG?ACK, (str))
*define ParseMsgAck(buff, str) sscanf(buff, NSG?ACK, (str))
/* simulator sends the following message to all clients upon normal exit */
#define NsG?qUIT?ALL "qUIT?ALL y,s7.Y."
#define NSG?DUIT?ALL?ARGc 1
*define CreateNsgquitAll(buff, str) sprintf(buff, MSG?qUIT?ALL, (str))
75
#defin. ParseMsgquitAll(buff, str) sscanf(buff, MSG?qUIT?ALL, (str))
/* Nessages for configuration download RAH and JO 4/90 rev 5/91 */
#define NsG?cPI?DowJLD?REqUEsT "CPI Downid request from Y.?Y:/."
#defin. MsG?cPI?DowJLD?REqUEsT?ARGc I
#define CreateMsgcplDownldRequest(buff, str) \
sprintf(buff, MSG?CPI?DOWjLD?REqUEST, (str))
#define ParseMsgcplDownldRequest(buff, str) \
sscanf(buff, NSG?CPI?DOWNLD?REQUEST, (str))
#define MSG?CPI?DOWNLD?GERERAL ,,c Y.d,Y.d,Ylf,Y,lf,Y,lf,Y,d,Y,lf#"
typedef struct <
int block?ct, train?ct; /* number of blocks and trains in layout */
double acc, emer?acc; /* standard and emergency stop acceleration rates */
double switch?time; /* time required for switch block to change posit */
int voting?quorum; /* number of agreeing couands required for an action */
double voting?window; /* time limit after which commands expire */
? NsgcPlflownldGeneral;
#define MSG?CPI?DOWNLD?OEJERAL?ARGC 7
#define MsgcPlDownldGeneralArgs(buff, datap) (buff, NSG?CPI?DOWILD?GEjERAL, \
datap->block?ct , datap->train?ct , datap->acc , datap->emer?acc ,
datap->switch?time , datap->voting?quorum , datap->voting?window )
#define Create)(sgcplflownldGeneral(buff, datap) \
sprintf MsgcPlflownldGeneralligs(buff, (datap))
#define ParseNsgcPlflownldGeneral(buff, datap) \
sscanf NsgcPlflownldGeneralArgs(buff, a(datap))
#define MSG?CPI?flOwJLD?TRAIJ "r/'d,Yaf,7.d,Y,lf,Y,d,7,lf#"
typedef struct <
int train?id; /* numerical name of this train */
double length; /* length of train */
int head?block; /* block?id for block holding train head
double head?offset; /* offset within head?block of train
int tail?block; /* block?id for block holding train tail
double tail?offset; /* offset within tail?block of train
76
head */
tail */
> NsgcplDownldTrain;
#define MSG?CPI?DOWNLD?T?1AIN?ARGC 6
#define MsgcplDownldTrainArgs(buff, datap) (buff, NSG?CPI?DOWNLD?TRAII, \
datap->train?id datap->length datap->head?block , datap->head?offset
datap->tail?block , datap->tail?offset )
#define CreateNsgcplnownldTrain(buff, datap) \
sprintf MsgcplflownldTrainArgs(buff, (datap))
#define ParseNsgcplDownldTrain(buff, datap) \
sscanf NsgcPlflownldTrainArgs(buff, ?(datap))
#define MSG?CPI?nOwJLfl?HLDcK "BY.d,Y,lf,Y,d,Y,lf,Y,lf,Y,lf,Y,d#'
typedef struct <
int block?id; /* numerical name of this block */
double length; /* length of the block in meters */
int type; /* code for type classification of this block */
double max?speed, min?speed; /* speed above/below which derailment occurs*/
double sta?stop?speed; /* highest station stop speed---sta blocks only */
int cross?id; /* id for the cross block---for cross subblocks only */
> NsgcPlflownldBlock;
#define MSG?CPI?DOWJLD?BLOCK?ARGC 7
#define MsgcPlflownldBlockArgs(buff, datap) (buff, NSG?CPI?DOWJLfl?HLOCK, \
datap->block?id , datap->length , datap->type , datap->max?speed ,
datap->min?speed , datap->sta?stop?speed , datap->cross?id )
#define CreateNsgcplflownldBlock(buff, datap) \
sprintf MsgcPlDownldHlockArgs(buff, (datap))
#define ParseNsgcplflownldBlock(buff, datap) \
sscanf MsgcPlflownldBlockArgs(buff, a(datap))
#d.fine NSG?CPI?flOWJLD?LIJK `LY,d,Y.d,Y.d,Y.d,Y,d#"
typedef struct ?
int block?id; /* numerical
int type; 1* code for type
int tail, headi, head2; /*
NsgcplflownldLink;
name of this block */
classification of this block */
block?ids for adjoining blocks */
#define MSG?CPI?DOWJLD?LIJK?ARGC s
#define ?sgCPIflownldLinkligs(buff, datap) (buff, MSG?CPI?DOWJLD?LIJK, \
77
datap->block?id , datap->type datap->tail , datap->headi , datap->head2 )
#define CreateMsgcplflownldLink(buff, datap) \
sprintf NsgcPlflownldLinkArgs(buff, (datap))
#define ParseMsgcplDownldLink(buff, datap) \
sscanf NsgcPlnownldLinkArgs(buff, a(datap))
/* The end of an cpi config download may be recognized by searching for
CPI?flOWwLD?TERM beginning CPI?DOWJLD?TERN?OFFSET from final char received*/
#define CPI?DOwJLD?TERx ,D'
#define CPI?DowJLD?TERN?oFFsET -4
#define MSG?CPI?DOWJLD?DOIE "D `/,d#'
#define NSG?CPI?flOWJLD?DOIE?ARGC I
#define CreateNsgcplflownldDone(buff, val) \
sprintf(buff, NSG?CP1?DOWJLD?DOJE, (val))
#define ParseNsgcPlDownldDone(buff, val) \
sscanf(buff, NSG?CPI?DOWJLD?DOME, &(val))
/* Commands */
#define MSG?Cp1?SET?SW1TCH `?Xh'/.d,Y.d:Y.d:?/.d#'
typedef struct ?
int client?id; /* unique identifier for this client */
int seq?num; /* label for this command */
int block?id; /* simulator block number */
int new?posit; /* new switch position--O for straight and I for turned */
> NsgcPlsetswitch;
#defino MSG?CPI?SET?SWITCH?ARGC 4
#define NsgcPisetswitchArgs(buff, datap) (buff, NSG?CPI?sET?swITCH, \
datap->client?id , datap->seq?nui , datap->block?id , datap->new?posit )
#defino CreateMsgcpisetswitch(buff, datap) \
sprintf MsgcPisetswitchArgs(buff, (datap))
#define ParseNsgcplsetswftch(buff, datap) \
sscanf MsgCPlSetswitchligs(buff, ?(datap))
78
#define NSG_CPI_ACCEL "XaY,d,Y,d:Y.d:Y,lf#"
typedef struct <
int client?id; /* unique identifier for this client */
int seq?num; /* label for this command */
int train?id; /* simulator train number */
double duration; /* time to accelerate in seconds */
> NsgCplAccel;
#define NSG?CPI?ACCEL?ARGC 4
#define NsgcPlAccelArgs(buff, datap) (buff, MSG?CPI?ACCEL, \
datap->client?id , datap->seq?num , datap->train?id , datap->duration
#define CreateNsgcplAccel(buff, datap) sprintf NsgcPlAccelArgs(buff, (datap))
#define ParseNsgcplAccel(buff, datap) sscanf NsgcplAccelArgs(buff, a(datap))
#define NSG?CPI?DECEL "XdY,d,Y4:Y,d:Y,lf#"
typedef struct <
int client?id; /* unique identifier for this client */
int seq?num; /* label for this command */
int train?id; /* simulator train number */
double duration; /* time to decelerate in seconds */
? MsgCPIDecel;
#define MSG?CPI?DECEL?ARGC 4
*define MsgcPlflecelArgs(buff, datap) (buff, NSG?CPI?DECEL, \
datap->client?id , datap->seq?num , datap->train?id , datap->duration )
#define CreateNsgcPlflecel(buff, datap) sprintf MsgcPlDecelArgs(buff, (datap))
#define ParseNsgcPlflecel(buff, datap) sscanf NsgcplflecelArgs(buff, a(datap))
#define NSG?CPI?SET?SPEEfl "XvY,d,Y,d:Y,d:Y,lf#"
typedef struct ?
int client?id; /* unique identifier for this client */
int seq?num; /* label for this command */
int train?id; /* simulator train number */
double goal?speed; /* new desired velocity for the train */
> NsgCPISetSpeed;
#define MSG?CPI?SET?sPEED?ARGC 4
#define NsgcplsetspeedArgs(buff, datap) (buff, MSG?CPI?sET?sPEED, \
datap->client?id , datap->seq?num , datap->train?id , datap->goal?speed )
79
#define CreateMsgcPlsetspeed(buff, datap) \
sprintf NsgcplsetspeedArgs(buff, (datap))
#define ParseNsgcplsetspeed(buff, datap) \
sscanf MsgcplsetspeedArgs(buff, a(datap))
#define MSG?CPI?SET?DIR "XfY.d,Y,d:Y.d:Y.d#'
typedef struct ?
int client?id;
int seq?num;
int train?id;
int new?dir;
> NsgCPISetDir;
/* unique identifier for this client */
/* label for this command */
/* simulator train number */
/* indicator of nev train direction */
#define MSG?CPI?SET?DIR?ARGC 4
#define MsgcPlsetDirArgs(buff, datap) (buff, NSG?CPI?SET?DIR, \
datap->client?id , datap->seq?num , datap->train?id , datap->new?dir )
#define CreateMsgcplsetflir(buff, datap) sprintf NsgCPISetDirArgs(buff, (datap))
#define ParsexsgcplsetDir(buff, datap) sscanf NsgcPlsetDirArgs(buff, a(datap))
#define NSG?CPI?ENER?STOp o+`XeY.d,Y.d:Y.d#"
typedef struct <
int client?id; /* unique identifier for this client */
int seq?num; /* label for this command */
int train?id; /* simulator train number */
1 NsgcplEmerstop;
#define MSG?CPI?EMER?STOP?ARGc 3
#define NsgcplEmerstopArgs(buff, datap) (buff, NSO?CPI?EMER?STOP,
datap->client?id , datap->seq?num , datap->train?id )
#dofine CreateMsgcplEaorstop(buff, datap) \
sprintf NsgcplEierstopArgs(buff, (datap))
#define ParseNsgcplEierstop(buff, datap) \
sicanf MsgcPlEmerstopArgs(buff, a(datap))
#define MSG?CPI?STA?STOp "XsY.d,Xd:Y.d:Y.d#"
typedef struct <
int client?id; /* unique identifier for this client */
int seq?num; /* label for this command */
80
int train?id; /* simulator train number */
int new?val; /* 0 to disable and I to enable */
Nsgcplstastop;
#define NSG?CPI?STA?STOP?ARGC 4
#define NsgCPlstastopArgs(buff, datap) (buff, MSG?CPI?STA?STOP, \
datap->client?id , datap->seq?num , datap->train?id , datap->new?val )
#define CreateMsgcplstastop(buff, datap) \
sprintf NsgcPlstastopArgs(buff, (datap))
#define ParseNsgcplstastop(buff, datap) sscanf NsgCPlstastopArgs(buff, a(datap))
/* oueries */
#define MSG?CP1?BLOCK?OCC "XoY.d,Xd:Y.d#"
typedef struct ?
int client?id; /* unique identifier for this client */
int seq?num; /* label for this cofland */
int block?id; /* simulator block number */
? MsgCPlBlockOcc;
#defino MSG?CPI?BLOCK?OCC?ARGc 3
#define MsgCPlBlockOccArgs(buff, datap) (buff, NSG?CPI?BLOCK?OCC,
datap->client?id , datap->seq?num , datap->block?id )
#define CreateMsgcPlBlockocc(buff, datap) \
sprintf MsgcPlBlockOccArgs(buff, (datap))
#define ParseNsgCPlHlockOcc(buff, datap) \
sscanf NsgCPIBlockt)ccArgs(buff, ?(datap))
#define NSO?CP1?SW1TCH?POS1T "XvY.d,Y.d:
typodef struct <
int client?id; /* unique identifier for this client */
iat seq?num; /* label for this cofland */
int block?id; /* simulator block number */
1 Nsgcplswitchposit;
#define NsG?cPI?swITcH?PosIT?ARGc 3
#define MsgCPISwitchPositArgs(buff, datap) (buff, NSG?CP1?SWITCH?POS1T, \
81
datap->client?id , datap->seq?num , datap->block?id
#define CreateNsgcplsvitchposit(buff datap) \
sprintf NsgcPlslitchpositArgs(buff, (datap))
#define ParseMsgcplsvitchposit(buff, datap) \
sscanf MsgCPlswitchPositArgs(buff, &(datap))
#define MSG?CPI?TRAIM?STATUS o+?xt?/.d,Y.d:y?d##
typedef struct <
int client?id; /* unique identifier for this client */
int seq?num; /* label for this conand */
int train?id; /* siiulator train number */
1 NsgcPlTrainstatus;
#define MSG?cPI?TRAIJ?sTATUs?ARGc 3
#define NsgCPlTrainstatusArgs(buff, datap) (buff, NSG?CPI_TRAIN_STATUS,
datap->client?id , datap->seq?num , datap->train?id )
#define CreateMsgcPlTrainstatus(buff, datap) \
sprintf MsgcPlTrainStatusArgs(buff, (datap))
#define ParseNsgcPlTrainStatus(buff, datap) \
sscanf NsgcPlTrainStatusArgs(buff, a(datap))
#define NSG?CPI?TRAIJ?NOTIOM "X17.d Y.d:
typedef struct ?
int client?id; /* unique identifier for this client */
int seq?num; /* label for this conand */
int train?id; /* siaulator train number */
1 NsgCPITrai?Motion;
#define MSG?cPI?TRAIJ?NoTIoN?ARGc 3
#define NsgcPlTrainMotionArgs(buff, datap) (buff, NSG?CPI?TRAIN?NOTIoN, \
datap->client?id , datap->seq?num , datap->train?id
#define CreateMsgcPlTrainMotion(buff, datap) \
sprintf NsgcPlTrainNotionArgs(buff, (datap))
o+dofine ParseRsgCPITrainNotion(buff, datap) \
sscanf NsgCPlTrainMotionArgs(buff, ?(datap))
82
C.2 aci. c, Example Using the LLI Interface
$RCSfile: aci.c,v $ $Revision: 1.1 $ $Date: 92/07/17 10:58:20 $ */
aci.c -- implementation of the Automatic Control Interface of the CPI
R. Brown 6/91, based on specifications and earlier versions */
flinclude
#include
#include
#include
#include
<stdio.h> /* for standard error and NULL */
<sys/types.h> /* required for acisys.h */
"cpi.h /* low-level interface of the CpI */
`?aci.h' /* for data type definitions */
"acisys.h" /* operating system-specific routines */
#define BUFFSIZE 200 /* maximum size of a message */
#define RETRY?INTERVAL 5.0 /* seconds between retries for ConnectTosim */
#define q?RY?TINE0UT 5.0 /* seconds before giving up on a query response */
#define EPSILOl 0.0001 /* tolerance for floating point comparisons */
Laycutnata layout?data, *ldp : ?layout?data;
/* data structure for storing the information passed in the download */
int chan; /* socket for communicating with simulator */
int client?id; /* identifier for this client, obtained from simulator*/
int seq?num : 0; /* sequence number, for voting purposes */
1* SendinitMessage sends the simulator the initialization message for this
application. Returns nonzero on success, zero on failure
RAB 11/30/89 */
int
SendlnitMessage(chan, string)
int chan; /* channel descriptor for communication with simulator */
char stringO; /* string to send as part of the message to sim */
char buffEBUFFSIZE]; /* buffer for init message and ack */
int n; /* length of received init message */
char client?id?string(BUFFSIZE]; /* string representing client?id */
/* send the initialization message */
sprintf(buff, MSG?CpI?IJIT, string);
if (SendNsg(chan, buff, strlen(buff)) < 0) <
perror("SendlnitMessage: SendMsg failed'?);
return (0);
83
/* wait for, verify acknowledgement */
if ((n:RecvMsg(chan, buff, BUFFSIZE)) < 0) <
perror("SendlnitNessage: can't receive acknowledgement");
return CO);
buff(n) =
if (sscanf(buff NSG?ACK, client?id?string) 1) ?
fprintf(stderr, "SendinitNessage: expected ack, received \"Y.s\"\n", buff);
return (0);
sscanf(client?id?string, "Y.d", aclient?id); /* extract unique id */
return (I);
/*
We trust the simulator's downicad...
Layoutflata *
Getflownload(hostname, simnum, progname, timeout)
char *hostname; /* machine running a simulation */
int simnum; /* identifies a running simulation */
char *progname; /* name of invoking program */
double timeout; /* in seconds */
static GetflownloadSucceeded : 0; /* set non-zero on first successful call */
char buff(BUFFSIZE?; /* buffer for holding one message */
union ?
MsgcPlflownldGeneral gen; /* for unpacking message of general attributes */
MsgCPIflownldBlock block; /* for unpacking message of block attributes */
NsgCPIDownldLink link; /* for unpacking message of block attachments */
MsgcplflownldTrain train; /* for unpacking message of train attributes */
int val; /* for receiving integer value passed with NsgCPIDownldDone */
1 msg;
int incomplete; /* non-zero if the download is found to be incomplete */
int recv?msg?status; /* return value from RecvNsg */
int i; /* loop control */
84
if (Getflownloadsucceeded) (
fprintf(stderr, `GetDownload has already been called successfully\n");
return (NULL);
/* there have been no prior successful calls */
if (timeout <: 0) <
fprintf(stderr, `?GetDownload:
return (NULL);
/* valid timeout was specified */
if (!*hostname) ?
GetLocalHost(buff, sizeof(buff));
hostname = buff;
Non-positive timeout Y..1f\n?, timeout);
while ((chan ConnectTosim(hostname, simnum)) -1) <
timeout -= RETRY?INTERVAL;
if (timeout < EPSILON) <
fprintf(stderr, ?GetDownload:
return (NULL);
> else <
fprintf(" retrying..
Sleep(RETRY?INTERVAL);
Could not connect to simulator\n");
a connection to the desired simulation has been established */
if (`.SendlnitXessage(chan, progname)) ?
fprintf(stderr, "GetDownload: Could not send init message\n?);
Closechan(chan);
return (NULL);
/* Cpi initialization message has been sent and acknowledged */
CreateMsgCPlflownldRequest(buff, progname);
if (SendMsg(chan, buff, strlen(buff)) < 0 ) ?
perror("XsgcPlflownldRequest Sendxsg failed");
fprintf(stderr, "Getflownload: Could not send request for download\n");
CloseChan(chan);
return (NULL);
85
/* Downicad has been requested.
The simulator's expected response is the download itself. */
/* receive download */
/* invar: all CPIDownld messages so far have been parsed */
while C(recv?msg?status = RecvflownldMsg(chan, buff, BUFFsIzE)) > 0 a:a
ParseNsgCPIDownldDone(buff, msg.val) =
MSG?CPI?DOWJLfl?DOME?ARGc) ?
new message of length recv?msg?status that is not DownldDone
has been received */
buff(recv?msg?status] =
if (ParseNsgcPlflownldGeneral(buff, amsg.gen)
NSG?CPI?DOWJLD?GEJERAL?ARGc) ?
ldp->block?ct = msg.gen.block?ct;
ldp->train?ct = msg.gen.train?ct;
ldp->acc = msg.gen.acc;
ldp->emer?acc = msg.gen.emer?acc;
ldp->switch?tiie = msg.gen.switch?time;
ldp->voting?quorum = msg.gen.voting?quorum;
ldp->voting?window = msg.gen.voting?window;
if (ldp->block?ct > MA1?BLocKs I I ldp->train?ct > NAX?TRAIJS) ?
fprintf(stderr,
"GetDownload: NAXTRAIMS CY0d) or MAX?BLocKs (7.d) too small\n",
NAX?T?IJS, NA1?HLOCKS);
return (lULL);
else if (Parse)(sgCPIDownldBlock(buff, ?msg.block)
NsG?cPI?DowJLn?HLocK?ARGc) <
int indox = 1sg.block.block?id - 1;
/* this block's index in ldp->blocksO */
ldp->blocks(index) .block?id = msg block.block?id;
ldp->blocksEindox) length = msg.block.length;
ldp->blocks(index).type = (onus HlockType) msg.block.type;
ldp->blocks(indox) .max?speed = msg.block.max?speed;
ldp->blocks(index) .min?speod = msg.block.min?speed;
if ((enus BlockType) msg.block.type == HT?sTATraJ)
ldp->blocks(index] .t.st.sta?stop?speed = msg.block.sta?stop?speed;
86
if ((enum BlockType) msg.block.type == BT?CROSs)
ldp->blocks(index] .t.cr.cross?id = msg.block.cross?id;
> else if (ParseNsgCPIDownldLink(buff, amsg.link) ==
MSG?CPI?DOWNLD?LINK?ARGc) <
int index = msg.lin??.block?id - 1;
/* this block?s index in ldp->blocksO */
switch (ldp->blocks(index] type) <
case			BT?REGULAR:
case			BT?STATIDJ:
case			HT?CRCSS:
ldp->blocks(index] .t.rg.tail = msg.link.tail;
ldp->blocks(index] .t.rg.head = msg.link.headi;
break;
case HT?JOII:
ldp->blocks(index] .t.jn.tail = msg.linx.tail;
ldp->blocks(index] .t.jn.headi = msg.link.headi;
ldp->blocks(index] .t.jn.head2 = msg.link.head2;
break;
case BT?SWITCH:
ldp--H>blocks(index] .t.sw.tail = meg.liak.tail;
ldp->blocks(index] .t.51.straight = meg.link.headi;
ldp->blocks(index] .t.sw.turn = meg.link.head2;
break;
> else if (ParseMsgcPiflownldTrain(buff, amsg.train) ==
NSG?CPI?DOWNLD?TRAII?ARGc) <
int index = msg.block.block_id - 1;
/* this train?s index in ldp->trainsO */
ldp->trains(index] .train?id = meg.train.train?id;
ldp->trains(index] length = msg.train.length;
ldp->trains(index] .head.block = meg.train.head?block;
ldp->trains(index] head.offset = meg train.head?offset;
ldp->trains(index] tail block = msg.train.tail?block;
ldp->trains(index] tail.offset = meg.train.tail?offset;
else ? /* unrecognized message format */
fprintf(stderr, `?GetDownload: Unrecognized message from simulator\n");
fprintf(stderr, Y.s\n', buff);
return (NULL);
87
if (recv?msg?status <= 0) ?
fprintf(stderr, "Getflownload:
return (JULL);
Failed in attempt to receive a message\n");
There were no failures to receive a message, and
all received messages have been successfully parsed and data stored in
layout?data, and one Done message was received */
/* check to see whether download was complete */
incomplete : 0;
we will assume that all of the fields from NsgcPlDownldGeneral were
received and stored properly in layout?data if block?ct is nonzero */
if (!ldp->block?ct) ?
fprintf(stderr, `?GetDownload: incomplete download (missing General)\n");
incomplete++;
for Ci : 0; i < ldp->block?ct; i++) <
/* we will assume that all of the fields from NsgcPlDownloadBlock were
received and stored properly in layout?data if block?id is nonzero */
if (!ldp--H>blocksEi).block?id) <
fprintf(stderr, "Getflownload: incomplete download (missing Block Y.d)\n"
i + 1);
incomplete++;
we will assule that all of the fields from MsgcPlflownloadLink were
received and stored properly in layout?data if tail is nonzero */
if (`.ldp--H>blocks(i].t.rg.tail) (
fprintf(stderr, ?GetDownload: incomplete download (missing Link Y.d)\n'?,
i + I);
incomplete++;
for (i=O; i < ldp--H>train?ct; i++)
/* we will assuine that all of the fields from NsgcPlDownloadTrain were
received and stored properly in layout?data if train?id is nonzero */
if (Ildp->trains(i].train?id) <
fprintf(stderr, ?GetDownload: incomplete download (missing Train Y.d)\n'
i + I);
88
incomplete++;
if (incomplete)
return (NULL);
a complete downicad was successfully received */
GetflownloadSucceeded++;
return (alayout?data);
/* commands */
void
SetSwitch(block?id, new?posit)
int block?id;
enum Mewposit new?posit;
NsgCPlSetSwitch msg; /* data structure for message construction */
char buffEBUFFSIZE]; /* buffer for outgoing message */
client */
msg.client?id : client?id; /* identifier of this
msg.seq?num = seq?num; /* voting sequence number
msg.block?id = block?id;
1sg.neI?posit (int) new?posit;
Create?sgCPISetSwitch(buff, amsg);
if (SendNsg(chan, buff, strlen(buff)) < 0) ?
perror("Setswitch: SendNsg failed");
void
Accelerate(train?id, duration)
int train?id;
double duration;
NsgCPIAccel msg; /* data structure for message construction */
char buff(HUFFSIZE]; /* buffer for outgoing message */
89
msg.client?id = client?id; /* identifier of this client */
msg.seq?num = seq?num; /* voting sequence number */
msg.train?id = train?id;
msg.duration = duration;
CreateNsgcplAccel(buff, &msg);
if (SendNsgCchan, buff, strlen(buff)) < 0) <
perror("Accelerate: SendNsg failed");
void
DecelerateCtrain?id, duration)
int train?id;
double duration;
Msgcplflecel msg; /* data structure for message construction */
char buffEHUFFSIZE); /* buffer for outgoing message */
msg.client?id client?id; /* identifier of this client */
msg.seq?num = seq?num; /* voting sequence number */
msg.train?id = train?id;
msg.duration = duration;
CreateMsgcplflecel(buff, aIflsg);
if (SendMsg(chan, buff, strlen(buff)) < 0) <
perror("flecelerate: SendNsg failed");
void
SetSpeed(train?id, goal?speed)
int train?id;
double goal?speed;
Msgcplsetspeed msg; /* data structure for message construction */
char buff(BUFFSZZE); /* buffer for outgoing message */
msg.client?id client?id; /* identifier of this client */
msg.seq?num = seq?num; /* voting sequence number */
msg.train?id : train?id;
msg.goal?speed = goal?speed;
90
CreateMsgcplsetspeed(buff, amsg);
if (SendNsg(chan, buff, strlen(buff)) < 0) <
perror("Setspeed: SendNsg failed");
void
SetDirection(train?id, nev?dir)
int train?id;
int new?dir;
MsgCPISetDir msg; /* data structure for message construction */
char buff(BUFFSIZE); /* buffer for outgoing message */
msg.client?id : client?id; /* identifier of this client */
msg.seq?num = seq?num; /* voting sequence number */
msg.train?id = train?id;
msg.neI?dir = nev?dir;
CreateNsgcplsetflir(buff, amsg);
if (SendMsg(chan, buff, strlen(buff)) < 0) ?
perror("Setflirection: SendNsg failed");
void
Emergencystop(train?id)
int train?id;
MsgcPlEierstop meg; /* data structure for message construction */
char buff(BUFFSIZE]; /* buffer for outgoing message */
msg.client?id = client?id; /* identifier of this client */
msg.seq?num = seq?num; /* voting sequence number */
msg.train?id = train?id;
CreateNsgcplEmerstop(buff, aleg);
if (SendNsg(chan, buff, strlen(buff)) < 0) ?
perror("Emergencystop: SendReg failed");
91
void
StationStop(train?id, new?val)
int train?id;
en'utL StationstopNode nev?val;
Nsgcplstastop msg; /* data structure for message construction */
char buff(HUFFSIZE]; /* buffer for outgoing message */
/t identifier of this client */
voting sequence number */
msg,client?id : client?id;
msg.seq?num = seq?nu1; /*
msg.train?id train?id;
msg.new?val (int) new?val;
CreateMsgcplstastop(buff, amsg);
if (SendMsg(chan, buff, strlen(buff)) < 0) ?
perror("Stationstop: SendMsg failed");
/* queries */
enum Occupancy
GetBiockOccupancyCblock?id)
int block?id;
MsgCPIBlockOcc msg; /* data structure for message construction */
char buff(BUFFSIZE); /* buffer for outgoing message */
int n; /* return status from RecvNsgTimed */
msg.client?id : client?id; /* identifier of this client */
msg.seq?nu1 : seq?num; /* voting sequence number */
msg.bloch?id = block?id;
CreateMsgcPlBlockOcc(buff, amsg);
if (SendNsg(chan, buff, strlen(buff)) < 0) <
perror("GetHlockoccupancy: SendNsg failed");
92
if ((n = RecvNsgTimed(chan, buff, BUFFsIZE, q?RY?TINEoUT)) < 0)
return (OC?ERRCR);
/* response successfully received from simulator and stored in buff */
switch (buff(0]) <
case `0': return (CC?OCCuPIED);
case `f': return (0C?FREE);
default:
buffEn] =
fprintf(stderr "GetBlockOccupancy: unknown simulator response \"7.s\'\n',
buff);
return (OC?ERRoR);
enum SwitchPosit
GetSwitchPosition(block?id)
int block?id;
MsgcPlswitchposit meg; /* data structure for message construction */
char buff(BUFFSIZE]; /* buffer for outgoing message */
int n; /* return status from RecvMsgTimed */
msg.client?id = client?id; /* identifier of this client */
msg.seq?num : seq?num; /* voting sequence number */
msg.block?id = block?id;
CreateMsgcPlswitchposit(buff, ameg);
if (SendMsg(chan, buff, strlen(buff)) < 0) <
perror(1?GetSwitchPosition: SendMsg failed'?);
if ((n = RecvMsgTimed(chan, buff, HUFFSIZE, q?RY?TINEoUT)) < 0)
return (SP?ERROR);
/* response successfully received from simulator and stored in buff */
switch (buff(0]) <
case `s': return
case `t': return
case `u': return
default:
buffEn) =
fprintf(stderr,
(SP?STRAIGHT);
(SP?TuRJED);
(SP?uinEFIMED);
o+`GetSwitchPosition: unknown simulator response \"7.s\'\n",
93
buff);
return (SP?ERROR);
enum Trainstatus
GetTrainStatus(train?id)
int train?id;
NsgcplTrainstatus meg; /* data structure for message construction */
char buffEHUFFSIZE]; /* buffer for outgoing message */
int n; /* return status from RecvMsgTimed */
msg.client?id = client?id; /* identifier of this client */
msg.seq?num = seq?num; /* voting sequence number */
msg.train?id : train?id;
CreateNsgcplTrainstatus(buff, ameg);
if (SendNsg(chan, buffr strlen(buff)) < 0) <
perror(1GetTraiflstatus: SendMsg failed?);
if ((n = RecvNsgTimed(chan, buff? BUFFsIzE, q?Ry?TINEoUT)) < 0)
return (TS?ERR()R);
1* response successfully received from simulator and stored in buff */
switch (buff(0]) <
case `c?: return (TS?CRASHEfl);
case `r?: return (TS?RUIJIMG);
default:
buffEn] =
fprintf(stderr, `GetTrainStatus: un:known simulator response \"Y.s\"\n",
buff);
return (TS?ERRoR);
enum TrainMotion
GetTrainMotion(train?id)
int train?id;
MsgCPlTrainMotion meg; /* data structure for message construction */
char buff(BUFFSIZE]; /* buffer for outgoing message */
94
int n; /* return status from RecvNsgTimed */
msg.client?id = client?id; /* identifier of this client */
msg.seq?num = seq?num; /* voting sequence number */
msg.train?id = train?id;
CreateNsgCPlTraixixotion(buff, &msg);
if (SendNsg(chan, buff, strlen(buff)) < 0) ?
perror('?GetTrainMotion: SendNsg failed?');
if ((n = RecvNsgTimed(chan, buff, BUFFSIZE, qUERY?TINEOUT)) < 0)
return (TN?ERROR);
/* response successfully received from simulator and stored in buff */
switch (buffEO]) <
case `s': return (TN?STDPPED);
case `m': return (TM?NoVING);
default:
buffEn)
fprintf(stderr, `GetTrainMotion: `inknown simulator response \"Y.s\'\n'
buff);
return (TN?ERROR);
/* Voting Sequence Number */
int
SetSeqJumber(new?val)
int new?val;
if (new?val < 0)
return (-I);
else <
seq?num = new?val;
return (seq?num);
95
int
NewSeqN?ber()
ret?n (++seq?n?);
y
96
Appendix D
Reference Pages
acitest(1)
ts(1)
tsed(1)
tsim(1)
tspanel(1)
98
99
111
117
119
97
acitest(1)
Name
acitest - manual test of CPI interface in Trainset
Syntax
acitest E -d ] ( simhost [ simnum ]]
Description
acitest is an example program included with the Trainset
software. Its source code illustrates the use of the upper
layer (called the ACI) of the Control Program Interface
(CPI). When invoked and connected to a running railroad
simulation (see tsim(i)), it enables a user to interactively
perform each ACI command and query. The effects of each
action may be observed using the monitor programs tspanel(1)
and tsview(1)
Options and Arguments
-d Causes the state information received from a simulation
to be printed during startup
simhost
Specifies the name of a host that is running a simula-
tion of a railroad. If an empty string is specified?
the local host is used. If the simhost argument is
omitted, a value is requested interactively.
simnum
An integer that determines which invocation of the tsim
simulator (at the indicated host) is to be used. The
default value is 0. The monitor programs and the
desired invocation of tsim should use the same value
for number.
See Also
ts(i), tsim(1), tspanel(1), tsview(1).
98
ts(1)
Name
ts - launch Trainset applications
Syntax
ts F flags... 3
ts -edit F file 3 E -layout file ] [ -tsroot dir 3 ( Xli-
options 3
Description
ts invokes (``launches'') applications related to the Train-
set railroad simulation software in a computing environment
that may involve a heterogeneous network of computing sys-
tems, With no options, ts typically starts a demonstration
consisting of a simulation of a sample railroad, an Xii
application that graphically displays the current state of
the railroad, and another Xii application that provides for
manual control of the railroad. ts options enable the user
to select which applications are to be launched, choose the
railroad layout to be simulated, specify the host to launch
from, etc
Programmers may write software that interacts with Trainset,
including programs that automatically control a railroad and
custom versions of the basic Trainset software. ts provides
a configuration mechanism for adding launch information
describing such programs to its database
When invoked with -edit as the first command-line option, ts
invokes the tsed editor for railroad layouts and launches no
other applications. The options -tsroot and -layout are
recognized by ts in the case of -edit, and may appear either
in an environment variable TSOPTS or on the command line.
file names the layout data file to be edited. file may be
presented as the second command line argument if it does not
begin with a dash `-`. It may also be provided using the
-layout option. -tsroot specifies the tsed search paths for
layout data files, as discussed in Search Paths below. In
addition, any additional options on the command line, in
99
particular options recognized by Xii, are passed on to tsed.
Applications
Trainset consists of application programs that support
interaction with a simulated railroad, Railroads are
represented by layouts that consist of trains and blocks of
track. Users of Trainset may write programs that use the
Control Program Interface (cpi) to control such a railroad.
For more information, see the manuals for Trainset.
The standard applications that comprise Trainset include the
following.
tsim
tsed
tsview
tspanel
Simulator of railroad layouts.
Editor for interactive creation and modifica-
tion of a layout.
A graphical display of the state of a layout
during a simulation.
control panel for manually
layout's trains and switch
A graphical
operating a
blocks
In addition, programs that demonstrate the CPI, including
acitest and demo, are provided in the Trainset distribution.
Control Codes
In ts, applications are referenced by control codes that are
strings consisting of an uppercase letter optionally fol-
lowed by any combination of lowercase letters, digits,
underscores and periods. The following control codes are
conventionally defined in the main configuration file for
ts:
Sim, 5, Tsim Trainset railroad siniulator.
ts(I)
lO()
ts(1)
Panel, P, Tspanel
Control panel application.
Viewer, V, Tsview
Viewer application for observing a simula-
tion.
Acitest, A
Demo, D
Acitest program that allows manual experimen-
tation with CPI interface features
Demonstration of the CPI interface discussed
in the Trainset manual.
Any of the ts options -args, -delay, -direct, -display,
-host, -tsroot, -xterm and -xtermargs cam be localized to
apply to a specific application by prefixing that option
with the appropriate control code(s). For example, either
CSim:?host loki' or `S:-host loki' specifies that the simu-
lator should be launched at the site loki. Also,
CpV:?di5play thor:O.O' causes the panel and viewer applica-
tions to display graphics output on the XII server thor:O.O.
Arbitrary arguments can be passed to applications using the
-args option prefixed by the appropriate control code, e.g.,
eMyprog:?arg5 "-level 4 -trace"'. quoting (e.g., using "``)
enables spaces to be included in the argument string.
If the colon that terminates a prefix is followed immedi-
ately by a character (e.g., dash c?)) that cannot be part
a control code name, then that colon may be omitted. For
example, `Sim-host loki' is equivalent to Sim:-host loki'
of
An alias facility is provided for associating multiple con-
trol codes with a single application. Aliases may be
declared in configuration files (discussed below) or by
using the -alias option. The alias feature can be used to
change the associations between control codes and applica-
tions. For example, suppose that a control code
Train?control?2 is associated with an application that has
101
been written locally. Then the ts option `-alias
T=Train?control?2' assigns the control code T to represent
that local application. Train?control?2 is a better identif-
ier, but the alias T is more convenient as a prefix. Also,
`-alias Demo:T' could be used to associate the control code
Demo with that local application instead of the standard
Trainset demonstration program
The -dup option may be used to invoke multiple copies of an
application. This feature supports the launching of repli-
cated programs. -dup clones the launch information gathered
so far for the application in question and associates a con-
trol code with the copy. The launch attributes in the two
copies may thereafter be modified independently.
Search Paths
The directory tsroot identifies the location of the Trainset
installation in the local file system. Tsroot may be speci-
fied as the value of an environment variable TSROOT or on
the command line using the -tsroot option.
Ts searches for its own configuration files in the direc-
tories ., ./lib, tsroot and tsroot/lib (in that order)
Layout data files for a simulation are sought in the direc-
tories ., ./layouts, tsroot, tsroot/layouts and
tsroot/trainset/layouts. The default extension `.1' is
appended (if omitted) to a layout data file name by Trainset
applications and searching is repeated if a file with the
given name was not found. If no layout data file is speci-
fied, the file tsroot/layouts/sample.l is used.
Hinaries for Trainset applications are sought using the
environment variable PATH as usual
Launching Applications
Unless the -edit option is specified, ts gathers information
102
ts(i)
ts(1)
from the following sources, in order, before launching
applications
Main
configuration file
This file associates control codes with applications,
contains invocation information for applications (e.g.,
name of executable and default argunents) and provides
a default list of applications to be launched. See
ts.config(5) for the file format. The name of the main
configuration file may be specified with the -config
option. The default main configuration file is
tsroot/ts.config
Auxiliary configuration files
One or more additional configuration files may be
designated to supplement the main configuration file.
These are specified by the ts options -confign where n
is a single digit. Auxiliary configuration files are
processed in increasing order of n. The values of n
need not be contiguous. Auxiliary configuration files
enable individual users and groups of users to install
their own applications and customize launching informa-
tion for applications that appear in the main confi-
guration file. Attributes for the launch are either
overridden or appended as appropriate. If a non-empty
list of applications to be launched is included in an
auxiliary configuration file, it overrides any prior
list. There are no default auxiliary configuration
files
Environment variables TSROOT, TSOPTS and DISPLAY
If there is an environment variable TSROOT, it is
treated as the default value for tsroot (see Search
Paths above). If there is an environment variable
TSOPTS, it is treated as a list of ts options that are
parsed prior to the options specified on the command
line, If there is an environment variable DISPLAY with
value XII-display, then the option -xtermargs Xli-
display is iniplicitly processed just prior to any
103
options in TSOPTS
Command line options
See Options below
The significance of the ordering above is that later values
override or are appended to earlier values. For example, a
-host option in TSOPTS overrides any HOST entries in confi-
guration files, and `Sim:-args "-window S"? on the command
line appends to the arguments for the Sim application that
were collected from configuration files and TSOPTS.
Launching at a remote host is accomplished by invoking ts at
the remote site, as described in ts.remote(S). The
information that is passed to a remote ts consists of an
options list that is assembled by the local ts after scan-
ning the local sources listed above. That options list con-
sists of the following.
A -launch option listing the applications to be
launched at that site.
unless no -display attribute was
no local DISPLAY environment vari-
A -display option,
given and there is
able
Any applicable control-code specific -display options.
-simhost and -simnum options.
Any local command-line options that remain after remov-
ing -host and -launch (+) options.
The remote ts gets its launch information first from
remote-site configuration files, then from remote-site
environment variables and finally from the options list that
was assembled at the local site. (Note that values for
attributes such as -delay and -tsroot may be passed to the
remote site only if they are specified in local command-line
ts(i)
104
ts(1)
options.)
ts provides two methods for launching applications.
xterm Method
If the configuration file attribute XTERN for an appli-
cation is a string of positive length or if the -xterm
option is used, then the application is invoked in an
xterm(i) window that is created for that purpose. That
application's standard input and output will be associ-
ated with the xterm window. Arguments can be passed to
xterm using the -xtermargs option discussed below.
Direct Method
Applications that are launched without using the xterm
method are invoked asynchronously by the ts process,
and share that process's standard output. The standard
input for all such applications is /dev/null, except
the last application launched by the direct method
inherits the ts process's standard input as well as
standard output. Thus, an interactive application
(e.g., acitest(1)) may be launched using the direct
method provided that it the last such application
specified. See the Examples section.
xterm is the default launch method. The direct method is
currently implemented for the local host only, i.e., the
host at which the ts command was entered. Thus, any -host
options are ignored for applications launched by the direct
method.
Options
-alias controli:control2
Declares the control code controli to be an alias for
the control code control2. The two codes may then be
used interchangeably for referring to the application
launch attributes associated with control2, if there is
such an application. The symbol `:` is optional.
105
-dup controli=control2
Make a copy controlI of the application launch attri-
butes associated with control2. Since the two control
codes represent different copies, a change in the
attributes referenced by controlI will have no effect
on the attributes referenced by control2. This is use-
ful for invoking multiple copies of the same applica-
tion, e.g., parallel copies of the same CPI program for
controlling a railroad. The symbol `=` is optional.
-args arglist
Provides program-specific arguments to be passed to
applications. This option is ordinarily prefixed by
control codes, e.g., `Sim:-args "-update O.S"?
-config filename
Specifies the name of the main configuration file
-confign filename
Specifies the name of the nth auxiliary configuration
file.
-delay seconds
Specifies an integer number of seconds to pause just
after launching each application.
-direct
Specifies that applications should be launched using
the direct method.
-display XIi-display
Indicates the display for Xii output. The environment
variable DISPLAY is set to XII-display for the applica-
tions that are launched, and C?di5play XIi-display' is
appended to the list of arguments for xterm. The
latter effect is approximately equivalent to -xtermargs
"--Hdisplay XIi-display"; this does not affect applica-
tions launched by the direct method, but such applica-
ts(i)
106
ts(i)
tions can still obtain the value XII-display by
examining the environment variable DISPLAY
-edit filename
Invoke the tsed editor to create or modify a layout
data file of blocks and trains to be simulated, If
filename is a relative path (does not begin with `/,),
a file with that name is searched for using the search
path for layouts. If such a file is found, its path is
passed to tsed for modification. Otherwise, filename
is passed, so that a new layout data file with that
name may be created relative to the current working
directory. The -edit option should not be used in com-
bination with any other ts options.
-host hostname
ts seeks
the site
by other
configuration files and invokes executables at
hostname? except when specifically overridden
ts options.
-layout filename
The railroad layout to be simulated is described in
filename, which must be in the format produced by tsed.
See Search Paths above
-launch control..
Specifies that the indicated applications (only) should
be invoked. The default list of applications to launch
is determined by the first lines of configuration
files.
-quorum n
Sets the simulator voting quorum value to n.
Equivalent to C5im:?arg5 "-quorum n"'
-tsroot dirname
Specifies the value of tsroot.
-simhost hostname
107
Specifies the host on which to invoke the simulator.
Equivalent to `Sim:-host hostname'
-simnum number
number determines which well-known port is to be used
when initializing communication with a railroad simula-
tion. Permissible values for number are installation-
dependent and typically include the range 0 to 99
Applications that are launched with a given value for
number can only connect with a simulation that was
launched with that same -simnum value number; thus,
multiple railroad simulations may be run simultaneously
on the same host if they use different values for
number. It is convenient to assign several unique
values of number to each Trainset user in order to
avoid collisions among users.
-update seconds
Specifies the frequency that the simulator sends state
update reports to graphics monitor programs (control
panel and viewer). seconds may be a decimal value,
e.g., 0.5. Equivalent to CSim:?arg5 "-update
seconds"'
-window seconds
Sets the simulator voting window value to seconds
Equivalent to C5im:?arg5 "-window seconds"'.
-iterm
Specifies that applications should be launched using
the xterm method.
-xtermargs args
Provides arguments to be passed to xterm(1), for appli-
cations to be launched using the xterm method. Enclose
args in quotes if it contains embedded spaces.
+control.
Same as -launch control.
ts(1)
108
ts(1)
args
Any options that are not listed above are passed to
xterm(1) for applications launched using the xterm
method. Thus, the options args is approximately
equivalent to -xtermargs args"
Examples
Start the standard programs (as listed in in configuration
files) using their default methods. Use the default layout
data file to define the railroad being simulated. Send all
graphics output to the Xii server specified in the environ-
ment variable DISPLAY, and interpret any options listed in
the environment variable TSOPTS.
ts
Seek a file oval.l as described in Search Paths above for
layout data files. If it is found, then start the standard
programs as before, with the simulator tsim using that file
oval.l to define the railroad layout
ts -layout oval.l
Start the standard programs as before, except send output
for the Panel application to thor:O and send all other
graphics output to the Xii server loki:O. Send updated
information to any running graphics monitor programs (e.g.,
Viewer and Panel) every 3/4 second.
ts -display loki:O -update .75 Panel:"-display thor:O"
109
ts(1)
Bugs
Launch the applications 5, V and A using their default
methods. Unless these control codes have been reassigned,
they refer to the Simulator, Viewer and Acitest applica-
tions
ts +SVA
Launch the applications 5, V and A using the direct method.
Output for the various applications will be intermingled on
the screen. Since A is specified last, that application can
be operated interactively. The direct method is useful for
debugging new applications and for systems that do not sup-
port the program xterm(1).
ts +SVA -direct
The remote invocation facility is not currently implemented.
Unpredictable results may occur if a control code is made a
duplicate of itself
See Also
tsed(t), tsim(i), tspanel(1), tsview(1), ts.config(S)
ts.remote(8).
110
tsed(i)
Name
tsed - Trainset layout editor
Syntax
tsed E file ] E -layout file ] E -tsroot dir ] E Xii-options
Description
tsed is an interactive graphics editor for defining and
modifying railroad layouts for use in the Trainset railroad
simulation software. On startup, tsed displays two windows:
(I) a canvas window on which the layout is created. The
window contains a set of pulidown menus along the top, a
message area at the bottom, and is overlaid with an align-
ment grid, and (2) a tools window consisting of icons
representing the operations available for creating and modi-
fying blocks and trains.
With no file or options specified, tsed starts with a blank
unnamed layout. The options -tsroot and -layout are recog-
nized by tsed and may appear either in the environment vari-
able TSOPTS or on the command line. file names the layout
data file to be edited. file may be presented as the second
conand line argument if it does not begin with a dash `-`
It may also be provided via the -layout option. -tsroot
identifies the location of the ts installation in the file
systein and is used to determine the location of auxiliary
files needed by tsed. These are searched for in tsroot,
tsroot/lib and tsroot/trainset/lib if tsroot is specified
and then according to the PATH environment variable. tsed
uses tsroot to search for layout data files in the following
order: ., ./layouts, tsroot, tsroot/layouts and
tsroot/trainset/layouts. The default extension `.1' is
appended to a layout data file name and the search repeated
if the given file was not found.
tsed also recognizes the standard Xii application options.
111
tsed(1)
Layout Tools
The layout tools lie in two columns of icons in the tools
window and are called (top to bottom, left to right) Select,
Erase, Straight Block, Station Block, Arc Block 1, Arc Block
2, Cross Block, Join Block, Switch Block 1, Switch Block 2,
Train, and Rotate. Select, Erase, and Rotate manipulate
existing blocks. Straight Block and Station Block create
linear blocks. Arc Block I and Arc Block 2 create circular
arc blocks. Cross Block, Join Block, Switch Block 1, and
Switch Block 2 create fixed size iconic blocks. The current
tool is shown highlighted by inverting its colors and is set
by clicking on it. Clicking the left mouse button in the
canvas area invokes the current layout tool. tsed provides
the following layout tools:
Select
Erase
Nove block labels by dragging within their
boundaries. Select a block by clicking on it
whether or not it's already selected and dis-
card any other selection.
Remove a block or train from a layout by
clicking on it.
The four tools, Straight Block, Station Block, Arc Block I,
and Arc Block 2, create regular and station blocks using a
drag technique to determine the position of the head and
tail terminators of the block. If the start point or end
point of the drag is within a few pixels of the terminator
of an existing block, tsed attempts to establish a connec-
tion. In such a case, tsed constrains the slope of the new
block to iatch the slope of the existing block at the termi-
nator.
Straight Block Create a straight regular block. The block
is constrained to be vertical, horizontal, or
at + or - 45 deg diagonal.
Station Block Create a station block. Created and con-
strained in the same way as straight block
112
above.
tsed(I)
Arc Block I
Create a circular arc regular block. The
start point of a drag determines the location
of the head terminator of the block. The
radius of the circular arc is two grid divi-
sions. The extent of the arc is determined
by the angle given by the head terminator and
the end point of the drag. The extent of the
arc is constrained to be a multiple of 46
deg.
Arc Block 2 The same as Arc Block 1 but with a radius of
four grid divisions.
The four tools, Cross Block, Join Block, Switch Block I, and
Switch Block 2, create iconic blocks, blocks that have a
fixed size and shape. They are created by simply clicking
the mouse, whose cursor takes the form of the icon for the
current tool. Along the outer circle of such a cursor are
enlarged points called hot spots at which connections with
other blocks can be made. When a connection is established,
the newly created iconic block is constrained so that the
slope at the connecting hot spot and the slope at the exist-
ing teriinator agree. tsed does not establish more than one
connection when an iconic block is created.
Cross Block			Create a cross block.
Join Block			Create a join block.
Switch Block I Create a switch block whose turn head is 45
deg counter clockwise from its straight head.
Switch Block 2 Create a switch block whose turn head is 46
deg clockwise from its straight head.
Train Create a train. The drag operation for
creating a train begins at the position
113
tsed(i)
Rotate
desired for the head end of a train, contin-
ues along the blocks to be occupied by that
train, and stops at the position desired for
that train's tail end. The head end of a
train is indicated by an angle bracket, and
the tail end is indicated by a square
bracket. All blocks that are occupied by a
train are highlighted. The head end of a
train is constrained to start in a regular or
station block.
Rotate an iconic block clockwise about its
center to the next hot spot.
Pulldown Nenus
The pulldown menus File and Customize lie in a menu bar
along the top of the canvas window. They contain commands
which you execute by pulling down the menu and releasing the
mouse button on the command
The File menu contains the following commands to operate on
files:
New
Open
Save
Clear the current layout and create a new
empty layout, requesting a layout name from
the user.
Open an existing layout using a layout data
file typed by the user. The search is the
same as specified above.
Save the current layout under the current
file name, if one exists. If one does not,
then it is the same as executing Save As .
Save As ... Save the current layout under a new name
specified by the user
Close Close the current layout and clear the can-
114
tsed(1)
quit
The
the
the
vas.
Exit the application after first checking for
any unsaved changes.
Customize menu contains the following commands to alter
appearance of the current layout and the attributes of
blocks contained within:
Change Block Attributes
Change the attributes of a selected block
(See Select above). If no block is selected,
a warning beep is sounded.
Change Default
Labels
Hide/Show Grid
Block Attributes
Change the default creation attributes of
block type. This is a hierarchical menu
whose submenu are the various block types.
a
Change the appearance of the labels on the
current layout. This is a hierarchical menu
whose submenu entries are: Names -- Labels
are block names; Speeds -- Labels indicate
the minimum, maximum, and stop velocities
associated with each block; Hide Labels --
Hide the labels in the current layout.
Hide (Show) the grid lines in the canvas win-
dow. Note that this does not turn the grid
alignment and constraints off, it merely
renoves the grid from view.
Files
tsroot/lib/tsed.uid
See Also
ts(i), layout data file description
Bugs / Restrictions
115
tsed(I)
Due to a bug in the DEC Xqdsg server, tsed causes the X
server to crash when using the Arc Block I and Arc Block 2
tools with X servers from Ultrix 4.0 and Ultrix 4.1. There
is no problem in Ultrix 4.2 and the Ultrix 4.2 X server can
be used with Ultrix 4.0 and 4.1
116
tsim(1)
Name
tsim - Trainset railroad simulation
Syntax
tsim E flags...
Description
tsim simulates a Trainset railroad. The Trainset software
consists of this simulator, an interactive graphics editor
tsed(I) for defining railroad layouts and graphics monitor
programs tspanel(1) and tsview(1) for displaying the state
of the railroad and manually controlling it. See The Train-
set Railroad Simulation for more information.
The programs tsim, tspanel and tsview are ordinarily invoked
using the launch program ts(1).
Interfaces
tsim provides three interfaces. The control program inter-
face (CPI) is used by computer programs that control a
Trainset railroad. The CPI is fully documented and enables
researchers and students to write control programs for
investigating issues such as real-time computing and fault-
tolerance. Programs such as acitest(1) that illustrate the
CPI are included in the software distribution.
The other two interfaces are used internally by Trainset
They are: the layout interface, for reading files that
describe a railroad and its initial state, as produced by
tsed; and the monitor interface, for coflunication between
tsii, tspanel and tsview.
Options
-layout layoutfile
The railroad layout to be simulated is described in the
layout data file layoutfile, which must be in the for-
117
tsim(1)
mat produced by tsed. Specify a complete absolute or
relative path for layoutfile.
-quorum n
Sets the voting quorum value for this simulation to n.
-simnum number
Number determines which well-known ports are to be used
when other programs are initializing communication with
this simulation. Permissible values for number are
installation-dependent and typically include the range
o to ICC. Nultiple instances of tsim may be run simul-
taneously on the same host if they use different values
for number.
-update seconds
Specifies the frequency that tsim is to send state
update reports to graphics monitor programs. Seconds
may be a decimal value; e.g., 0.5.
-window seconds
Sets the voting window value for this simulation to
seconds.
See Also
ts(1), tsed(1), tspanel(1); tsview(i); acitest(1).
118
tspanel(1)
Name
tspanel, tsview - Trainset railroad monitor programs
Syntax
tspanel ( -sinihost hostname ] E -simnum number ] [ -tsroot
dir ]
tsview ( -simhost hostname ] ( -simnum number ) E -tsroot
dir )
Description
tspanel is a graphical interface for manually controlling
and displaying quantitative state information about a simu-
lation of a Trainset railroad. tsview displays the state ?f
a Trainset railroad, including location of trains and set-
tings of switches. These programs enable a user to estab-
lish initial conditions of train motion and switch settings
in a railroad and to monitor the progress of a simulation.
Each program receives regular reports of the railroad
layout's state from the Trainset simulator, tsim(1). See
The Trainset Railroad Simulation for more information.
tspanel and tsview are ordinarily invoked using the launch
program ts(i)
Options
-simhost hostname
Specifies the host that is running the simulator tsim.
-siinui nuiber
number determines which invocation of the tsim simula-
tor (at the indicated host) is to be used. The monitor
programs and the desired invocation of tsim should use
the same value for number.
-tsroot dir
(flEcwindows implementation only.) Specifies directory
for locating uid startup file, e.g., tsroot/tsview.uid
119
tspanel(I)
or tsroot/lib/tsview,uid.
Limitations
Currently implemented on DECwindows only.
See Also
ts(1)? tsim(t).
120
