Model Train-related Notes Blog -- these are personal notes and musings on the subject of model train control, automation, electronics, or whatever I find interesting. I also have more posts in a blog dedicated to the maintenance of the Randall Museum Model Railroad.

2019-05-12 - Conductor 2: Concept of Routes & Virtual Blocks

Category Rtac

One of the main concepts to be added in Conductor 2 is the concept of routes.

Conductor 1 already has “route” definitions. These are actually intended for display on the remote tablets. A route is defined using:

  • A name,
  • A DCC throttle object, used to display the engine speed and direction;
  • The JMRI sensor used for the automation toggle on/off.
  • A counter of number of times the automation started (incremented by the script).

The counter thing was a quick attempt at gathering some stats initially and never used -- it’s technically displayed by RTAC on the tablet, but the UI layout was designed for 2 routes, and with the current 3 routes the counter is ironically not visible. Even when it was visible, nobody was looking at it. Thus I suggest dropping it eventually, which will remove a couple lines from the script.

The concept of routes in Conductor 2 is supposed to be more elaborate. However I’d posit it can just be an extension of what is in Conductor 1 at the syntax level in the script.

So what properties would I want in a Conductor 2 route?

Earlier I was trying to separate “engine profiles” from routes. However that can be explored later as the idea seem either moot, or flawed, or both.

What seems relevant however is to have the following route properties:

  • 1- The list of blocks & turnouts that compose a route, for simulation and prediction.
  • 2- A route status as being active or idle.
  • 3- A simple route sequencing, e.g. to alternate between routes A and B.
  • 4- Automated route navigation modes influencing a localized window or global block locking scheme.
  • 5- Associating on..then events to a route rather than being global.

The ordering matters in this list. Step 1 is something I can use right now. Step 2 and 3 are possible easy extensions. Steps 4 and 5 are more involved extensions, to be done much later.

To make it clear, routes can and are designed to overlap in their layout block/turnout usage.

In the current script, deciding which of the passenger or freight route runs next is done using a flip-flop enum. This could be equally done using a property on the route indicating which one is active or idle, and permuting that state between runs.

Let’s define what a route’s block and turnout list should look like.

In my case, all I need are extremely linear routes. There is no branching possible, that is a route is fixed and cannot change.

A generic description of a route is a graph with nodes and edges. Each node is an occupancy block (real or virtual) or a turnout, and each edge is a direction of travel between two nodes. Edges are directional.

For a more generic description, one could potentially imagine an automation case where a route is a non-cyclic graph with branches, and not just linear. That could be useful when associated with an exec engine that would select branches either randomly or other criteria. In the case of a limited number of choices, this is trivially simulated by choosing between multiple linear routes, which is exactly what I have here with the freight vs passenger routes.

One idea behind placing the turnouts in the route list is not for branching but for marking them as occupied/locked, and more important to define their default state. For example a fixed route list could indicate “B321, T330(N), B330”, thus indicating the required position of the turnout. This makes it possible to have a controller automatically setting the turnout to the requested position when the train reaches the block on either side.

As for marking a turnout as occupied, this allows it to be done automatically when a block on either side is occupied, so in the example above T330 would be marked as occupied as long as either B321 or B330 are occupied. This is mostly a display thing.

All my routes are currently operating in shuttle mode. This opens another choice, whether only half the route is defined, or whether the whole entire trip-and-back route is expressly defined. In other words, can blocks be repeated more than once. This also depends on whether there’s some kind of coordinator defining how the blocks are used along the route. Mapping only half means essentially that the route has a dynamic graph (or two graphs), depending on the direction of travel, or has directional edges which can dynamically change directions. Thus to keep it simple for now, a route will have the exact list of all blocks travelled, in the “forward” direction, which means double the numbers of blocks for a shuttle automation.

Thus a route definition will look something like this:

routeF = route {

    name = "Freight" // defaults to variable name if omitted

    toggle = PA_Toggle

    throttle = SP

    status = "PA_State"

   route = [ B311, T311, T321, T320, B321, T330, B330, B340, B350v, B360, T370, T371, B370, T371, T370, B360, B350v, B340, T330, B330, B321, T320, T321, T311, B311 ]

   active = false  // implied

}

routeF.active = true

on { routeF.active } then { … }

The “route” property inside a route denotes the linear list of blocks and turnouts, which form the nodes of an implicit graph with forward-only edges.

The mention of the turnouts is for now to be omitted as they won’t be used for now. If present they could be marked as “locked” on a display map, or marked as occupied when any block next to them are occupied.

Virtual Blocks

In the example above, B350 is marked as “B350v” to denote it would be a virtual block. A virtual block is a sensor that has no physical backing (e.g. no JMRI sensor). These are blocks that are sandwiched between actual blocks, and on which the physical presence of a train is a given, thus I have not mapped a physical sensor to them. There are two ways to deal with them:

  • Automatically. Based on the direction of travel, a controller can trivially mark a virtual block as occupied when a train occupies the block before it, and release it when the next block is occupied. In the route example above, both “B340 B350v B360” and “B360 B350v B340” give us enough information to occupy and release the virtual block.
  • By script. Being a sensor, the same can be done by programmatic rules. 4 rules are needed here per virtual block.

For my current usage, I have two such blocks, one on the mainline and one on the branchline.

My current suggestion is to create them explicitly as such:

B340  = block “NS784”

B350v = virtualBlock [auto=true]

Both “block” and “virtualBlock” are aliases for the current “sensor” creation. They all return a CSensor object to the script. However blocks have extra read-only properties for Conductor, not related to a physical JMRI sensor:

block.occupied = true | false (== sensor.active if backed by real sensor)

block.locked = true | false

Block.position = float [0 … 1.0] or NaN

A new “block” keyword is actually introduced, and the syntax “sensor ‘NS785’” creates a non-block sensor, for example a toggle switch.

Block Position

The “position” property is for display only. It would be an estimation of the position of the train in the block, if known.

  • NaN is used when the block is not occupied or the position is not known.
  • 0..1 maps to the start-end of the block, with 0.5 being the middle.
  • For each block I define a default “forward” direction -- in this layout, our default running direction is clockwise, and this is how the “forward” DCC direction is defined in the script.
  • 0 is defined as the location where a train would enter the block in forward direction, and 1 is defined as the location where a train would enter the block in reverse direction. 0..1 corresponds to the start-end comparatively to the forward direction.
  • The default location, when unknown for an occupied block, is in the middle at 0.5.
  • The location is reported as NaN for a non-occupied block.
  • For example in a shuttle run, the position would first change from 0…0.5…1 when the train first goes up on a block, and then it would change from 1 … 0.5 … 0 when the train comes back.

I’d also define an “occupancy controller” whose purpose is to govern these.


 Generated on 2024-04-22 by Rig4j 0.1-Exp-e7e2d73