Flow Control fundamentals. Here are the basics of how it works:
About TSE equipment
OnDispatchOut (this happens whenever a transfer has been received, after the OnProcessComplete event has finished)
The built-in function scans the TransferState.Processed list (all loads this component has in its possession), and tries to find the best transfer out, and if one is found, the Transfer object is created.
The best transfer will be the first load in the Processed list that is not marked as DispatchOutEnabled = false, and has a valid path out, and perhaps some more conditions.
About Flow Control
When using Flow Control, we Order loads from OrderList objects, Convey loads to Target Visuals, and then do one of the following:
[Add (load) To OrderList (orderList)]
[Release (load)]
[Delete (load)]
When we Order a load, we are actually either getting a new load from a LoadCreatorVisual, or we are getting a load from an OrderList (a Custom Property of type Channel, on the Visual we are ordering from).
When we Convey a load, we push a RoutingTarget to it, Release it, and then Wait until the load has arrived at that RoutingTarget .
.....Once the ConveyTo thread completes, we do one of the following:
When we choose to [Add (load) To OrderList (orderList)], the operating methods under the hood, set a flag on the load to be DispatchOutEnabled = false (this is done dynamically adding a custom property to the load). As mentioned above, the TSE equipment that received a load with this condition, will not automatically dispatch this load out; this gives us absolute control over when the load that is to be transferred out, and to where it will go.
When we Release a load, we simply remove the DispatchOutEnabled = false flag, and Transfer.Dispatch(the TSE component), which means the equipment will do its normal thing, and find the best transfer out for the load.
When we Delete the load, we do exactly that, but we also clean up any related Transfer Protocol related info, so the equipment that possessed the load is notified that it is no longer loaded, and is free to accept another Transfer In.
Creating equipment components that work with Flow Control
The key fundamental for making equipment component that work with Flow Control, is to make any decision point components respect RoutingTarget.
Respecting the RoutingTarget is essential for any equipment that needs to make a decision on where to send the load next. In practice, Demo3D uses ConnectedRoutingDijkstra( Visual from, Visual to ) to find a connected route from a load's CurrentStation to its CurrentRoutingTarget (or item[0] from its RoutingTargets list). From this route, the decision point can determine what connector it is to send the load out to, in order to do its part in respecting the routing target (respecting Flow Control routing). For TSE components, where the OnDispatchOut handler has not been overridden with User Code, the finding of the outbound connector is found automatically; this information is passed into any Transfer-related event handlers as part of the given Transfer Object. In short, we deliver the load to a Connector.
For TSE (Transfer State Enabled) Equipment
For example, consider the OnTxBeforeTransfer handler method below. The second argument passed into any Tx and Rx event handlers is the Transfer object. The Transfer object contains information about who the sender and receiver of the load(s) is, for this particular transfer. More importantly for Flow Control compatibility, the Transfer tells us which connector the sending component is to send the load out to, and which connector on the receiving component the load will be received from. Since we know the sending and receiving connectors, our custom equipment components only need to account for any animations handing to get the load from where it is, to where it is going. In the example below (from the script of a Popup Transfer), the scripting chooses to Raise or Lower a child Chain visual, based on which connector the load is to be sent out to.
[Auto] IEnumerable OnTxBeforeTransfer( ConveyorVisual sender, Transfer transfer )
{
var chain = ChainTransfer;
if (TransferIsToChain(transfer)) {
chain.Motor.Direction = transfer.TxConnector.Name == "Left" ? MotorDirection.Reverse : MotorDirection.Forwards;
yield return RaiseChain(chain);
}
else {
sender.Motor.Direction = transfer.TxConnector.Name == "Start" ? MotorDirection.Reverse : MotorDirection.Forwards;
yield return LowerChain(chain);
}
}
For non-TSE equipment
In the case of non-TSE equipment, there is no OnDispatchOut to examine the loads for the existence of a RoutingTarget, so we need to use some scripting to gather the required information from the load, in order to find out which connector we should send the load out to (just as the TSE equipment does), and then perform our physical handling accordingly.
For this example, consider the Divert Right component from the Quick Start catalog, in the Flow Control folder (the box-based sensor that is attached to the straight conveyor section). This component looks at where the load is going, and then sets the Conveyor's DivertAngle to accomplish the out-bound routing, with respect to the load's RoutingTarget.
In practice, a custom, non-TSE decision point component will need some way of getting a reference to the load being handled (usually done with a sensor). Once we have the reference to the load, we can use the flowing code to get the outbound connector pair from it:
ConnectorPair FindDeliveryConnector( Visual load )
{
var target = GetTarget(load);
if (target == null) return null;
var routing = new Demo3D.Visuals.ConnectedRoutingDijkstra(Conv, target);
return routing.FirstTransfer;
}
Visual GetTarget( Visual load )
{
var target = load.CurrentRoutingTarget;
if (target == null) return null;
var parent = target.Parent == null ? document.Scene : target.Parent;
return parent.HasProperty("IsMotorOn") ? parent : target;
}
With this information, we can handle any animation related control, needed for seeing the given load out to the destination that respects its RoutingTarget.