ZoneCrossing

class ZoneCrossing(parent: ModelElement, val space: GuidedPathSpace, val zones: List<Zone>, var arbiter: CrossingArbiterIfc = BoundedBatchArbiter(batchSize = 3, maxWait = 5.0), name: String? = null) : ModelElement, ZonePopulationHostIfc, ZoneHoldActionIfc(source)

A place where people cross guide-path space that vehicles also want.

Thin, because the exclusion is not its job. A crossing is a set of zones, an admission policy, and its own statistics -- everything about keeping vehicles and people out of each other's way is already the zone's, and this adds no new rule about it.

Why this is a ModelElement, when a holder is not

The contrast is worth stating because it is the one place the two kinds of occupancy meet. A spill cannot be a ModelElement: it happens at minute 137.4 on whichever aisle the sample picks, and nothing about it can be declared before the run, which is why ZoneHolderIfc is an interface with two members. A crossing is the opposite. It is a piece of the layout -- it has a location, a name, statistics that accumulate across a replication, and a discipline that may carry state between turns -- and there is exactly one of it, known when the model is built. So it is an element, and it gets initialize() and a place in the model tree for the same reasons a Link would if links were elements.

How a turn works

The mechanism is the one the subsystem already has, used for the turn rather than for the people.

  1. The arbiter is asked whether vehicles should be barred. When it says yes the crossing requests its zones as a holder -- so traffic already on them drains off through the ordinary all-or-nothing machinery, and the turn opens on every zone at one instant or on none. Nothing is evicted and no vehicle is stranded half way across.

  2. Once granted, the crossing holds the zones and admits people onto what it holds -- it is a ZonePopulationHostIfc, which is the whole of what that interface means. Vehicles are excluded because the zones are held; people are admitted because the holder is the crossing.

  3. The turn ends when the arbiter stops barring vehicles and the last person is off. A turn always finishes: the arbiter can stop admitting, never evict.

The design record originally said vehicles would be excluded by the population count rather than by anything holding on the pedestrians' behalf. That does not work, and the reason is worth keeping: a count excludes traffic only once somebody is already standing in the road, which is too late to be how a crossing gathers people safely. One holder for the crossing is not the thing that was being warned against -- a holder per pedestrian would have been.

What it earns its place by absorbing

  • The population edges. A walker that balks, is interrupted, or is destroyed must still leave the count, or the crossing stays shut to vehicles for the rest of the replication with nobody on it. This is the single most likely hand-rolling error, and ProcessModel.Entity cleans it up at the same two exits that give back guide-path space.

  • Admission control, which is where the modelling actually lives, and which is a substitutable CrossingArbiterIfc rather than a policy baked in here.

  • The statistics: turns taken, time shut to traffic, how long people wait and how many.

Parameters

parent

where this sits in the model

space

the guide path whose zones are being crossed

zones

the zones the crossing covers, all on that guide path, distinct, at least one

arbiter

who decides whose turn it is; substitutable, and the point of the construct

name

the crossing's name in the model

Constructors

Link copied to clipboard
constructor(parent: ModelElement, space: GuidedPathSpace, zones: List<Zone>, arbiter: CrossingArbiterIfc = BoundedBatchArbiter(batchSize = 3, maxWait = 5.0), name: String? = null)

Properties

Link copied to clipboard
Link copied to clipboard
@set:KSLStringControl(allowedValues = ["PedestrianPriority", "VehiclePriority"], comment = "Which admission discipline the crossing runs under")
var arbiterName: String

The discipline by name, so a scenario or an app can change it without holding an arbiter.

Link copied to clipboard
override val awaitedZone: Zone?

Never anything. A crossing waits for space through its request, not by queuing on a zone, so it contributes no queuing edge to the wait-for graph.

Link copied to clipboard

How many people got across.

Link copied to clipboard

The fraction of time the crossing was shut to vehicles, counting the drain as shut.

Link copied to clipboard

True while the crossing holds its zones, so no vehicle may enter.

Link copied to clipboard

True while the crossing has asked for its zones and traffic is still draining off them.

Link copied to clipboard

How long the one at the head of the queue has been waiting, or zero when nobody is.

Link copied to clipboard

How many are on the crossing now.

Link copied to clipboard

How many were waiting to cross, time-weighted.

Link copied to clipboard

How many are waiting to step on.

Link copied to clipboard
Link copied to clipboard

How many pedestrian turns the crossing opened.

Link copied to clipboard

How long each person waited before stepping on. Zero when the crossing was already open.

Link copied to clipboard

Functions

Link copied to clipboard
open override fun holdBegan(allocation: ZoneAllocation)

Called once, when the space has drained and the hold has taken effect.

Link copied to clipboard
open override fun holdEnded(allocation: ZoneAllocation)

Called once, when the space has been given back -- by the holder, or by the clock.

Link copied to clipboard
protected open override fun initialize()

This method should be overridden by subclasses that need actions performed to initialize prior to a replication. It is called once before each replication occurs if the model element wants initialization. It is called after beforeReplication() is called