Node builders
Node builders are sugar that help you build ports. They are designed to be used with our Domain Specific Language.
DSL helpers
These helper functions have use type safety to ensure you create a valid port. If you understand the concept of a push and pull channel, the DSL should be a intuitive way to express port layouts (without any requirement of understanding all the type weirdness – it’s just here to keep you safe).
Then connect
fun <ItemT, ChannelT : ChannelType<ChannelT>> RegularNodeBuilder<*, out ItemT, ChannelT>
.thenConnect(connection: Connection<in ItemT, ChannelT>)
Connect a node that produces an ItemT to a channel.
Then output
fun <ItemT, ChannelT : ChannelType<ChannelT>> RegularNodeBuilder<*, ItemT, ChannelT>.thenOutput(
outputRef: OutputRef<ItemT, ChannelT>
)
Connects a node output to an OutputRef. Useful in compound nodes, for example.
Arrivals
fun <T> arrivals(
label: String,
generator: Generator<T>
): RegularNodeBuilder<ArrivalNode<T>, T, ChannelType.Push>
Build an arrivals (source) node.
Then delay
fun <T> NodeBuilder<T, *>.thenDelay(
label: String,
delayProvider: DelayProvider,
): RegularNodeBuilder<DelayNode<T>, T, ChannelType.Push>
Build a delay node with a delay provider.
Then pump
fun <T> NodeBuilder<T, ChannelType.Pull>.thenPump(
label: String = "Pump"
): RegularNodeBuilder<PumpNode<T>, T, ChannelType.Push>
Build a pump node.
Then fork
// Push fork with lanes defined in a list
fun <ItemT, R> NodeBuilder<ItemT, ChannelType.Push>.thenFork(
label: String,
lanes: List<(RegularNodeBuilder<PushForkNode<ItemT>, ItemT, ChannelType.Push>) -> R>,
policy: ForkPolicy<ItemT> = forkPolicy(RandomPolicy),
): List<R>
// Push fork with lanes defined in a lambda function
fun <ItemT, R> NodeBuilder<ItemT, ChannelType.Push>.thenFork(
label: String,
numLanes: Int,
policy: ForkPolicy<ItemT> = forkPolicy(RandomPolicy()),
laneAction: (Int, RegularNodeBuilder<PushForkNode<ItemT>, ItemT, ChannelType.Push>) -> R,
): List<R>
// Pull fork with lanes defined in a list
fun <ItemT, R> NodeBuilder<ItemT, ChannelType.Pull>.thenFork(
label: String,
lanes: List<(RegularNodeBuilder<PullForkNode<ItemT>, ItemT, ChannelType.Pull>) -> R>,
): List<R>
// Pull fork with lanes defined in a lambda function
fun <ItemT, R> NodeBuilder<ItemT, ChannelType.Pull>.thenFork(
label: String,
numLanes: Int,
laneAction: (Int, RegularNodeBuilder<PullForkNode<ItemT>, ItemT, ChannelType.Pull>) -> R,
): List<R>
// Syntactic sugar to convert from Pull-output source to Push-input destination lanes
fun <ItemT, R> NodeBuilder<ItemT, ChannelType.Pull>.thenPushFork(
label: String,
numLanes: Int,
policy: ForkPolicy<ItemT> = forkPolicy(RandomPolicy()),
laneAction: (Int, RegularNodeBuilder<PushForkNode<ItemT>, ItemT, ChannelType.Push>) -> R,
): List<R>
Build a fork node, which may be a Push Fork or Pull Fork depending on the output type of the previous node. Returns a List<R> so you can iterate over the outputs of the fork with all the usual sugar.
Then join
// Push join
fun <T> List<NodeBuilder<T, ChannelType.Push>>.thenJoin(
label: String
): RegularNodeBuilder<PushJoinNode<T>, T, ChannelType.Push>
// Pull join
fun <T> List<NodeBuilder<T, ChannelType.Pull>>.thenJoin(
label: String,
policy: JoinPolicy<T> = joinPolicy(RandomPolicy()),
): RegularNodeBuilder<PullJoinNode<T>, T, ChannelType.Pull>
Build a join node of appropriate type, which merges a list of lanes in the DSL to a single pathway.
Then match
fun <MainInputT, SideInputT, OutputT, ChannelT : ChannelType<ChannelT>> NodeBuilder<MainInputT, ChannelT>.thenMatch(
label: String,
side: NodeBuilder<SideInputT, ChannelType.Pull>,
combiner: (MainInputT, SideInputT) -> OutputT,
): RegularNodeBuilder<MatchNode<MainInputT, SideInputT, OutputT, ChannelT>, OutputT, ChannelT>
Build a match node.
Then queue
fun <T> NodeBuilder<T, *>.thenQueue(
label: String,
policy: QueuePolicy<T> = FIFOQueuePolicy(),
): RegularNodeBuilder<QueueNode<T>, T, ChannelType.Pull>
Build an unbounded queue node.
Then service
fun <T> NodeBuilder<T, *>.thenService(
label: String,
delayProvider: DelayProvider,
): RegularNodeBuilder<ServiceNode<T>, T, ChannelType.Push>
Build a service node.
Then split
fun <
InputT,
MainOutputT,
SideOutputT,
ChannelT : ChannelType<ChannelT>
> NodeBuilder<InputT, ChannelT>.thenSplit(
label: String,
splitter: (InputT) -> Pair<MainOutputT, SideOutputT>,
): Pair<
RegularNodeBuilder<SplitNode<InputT, MainOutputT, SideOutputT, ChannelT>, MainOutputT, ChannelT>,
RegularNodeBuilder<SplitNode<InputT, MainOutputT, SideOutputT, ChannelT>, SideOutputT, ChannelType.Push>,
>
Build a split node. Returns a pair of node, one or the main output and one for the side output.
Then sink
fun <T> NodeBuilder<T, *>.thenSink(label: String): SinkNode<T>
Build a sink node.
Then dead end
fun <T> NodeBuilder<T, *>.thenDeadEnd(label: String): DeadEndNode<T>
Build a dead end node.
saveNode
fun <BuilderT : RegularNodeBuilder<NodeT, *, *>, NodeT : NodeGroup> BuilderT.saveNode(
saver: (NodeT) -> Unit
): BuilderT
val savedQueueNode : QueueNode<Truck>
val scenario = buildScenario {
arrivals("Arrivals", ...)
.thenQueue("Queue")
.saveNode { queueNode -> savedQueueNode = queueNode }
.thenService("Service", ...)
...
}
Saving a reference of a node constructed with the DSL
Build scenario
fun buildScenario(
builder:
context(ScenarioBuilderScope, GroupScope)
() -> Unit
): Scenario
Build a scenario from a builder.
With metrics
fun Scenario.withMetrics(builder: MetricsBuilderScope.() -> Unit): Scenario
Adds the metrics to track to a scenario.