Channels
Many simulation fields have a concept of data being passed between objects. Examples of this include factories where totes move from conveyer to conveyer and money streams between banks. ERS provides channels as built-in functionality to handle these scenarios. An entity may own any number of channels. A channel is considered either output or input and operates in a pair. This is a one-to-one relation, meaning one output channel may be "connected" to one input channel. Initially all channels are considered "closed", meaning data cannot be passed. A channel may be marked as "open" to indicate that it is willing to receive or send data. Data may only be sent once a pair of connected input/output channels are both open, in which case they are "ready" (for the transaction).
Resources and channels
Channels are implemented through ChannelComponent.
Entities that want to utilize channels are known as a "resource" and must have a ResourceComponent.
This component keeps track of all the input and output channels which exist on children of the entity that has the ResourceComponent.
A schematic overview of this relation can be seen below.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class ResourceA
class ChannelA
class ChannelB
class ResourceB
ResourceA "1" *-- "1..*" ChannelA
ChannelA "1" *-- * "1" ChannelB
ResourceB "1" *-- "1..*" ChannelB
ScriptBehaviors on resource entities (e.g. that have a ResourceComponent) gain access to additional callbacks:
OnInputChannelReadyfires when one of the resource's input channels is ready.OnOutputChannelReadyfires when one of the resource's output channels is ready.OnReceivefires when a child is passed to this resource through an input channel.
Channels may be created using ChannelComponent.AddChannelComponent.
You must specify the entity that you wish the ChannelComponent to be added to, the type of channel (input/output) and the entity that has the ResourceComponent that this channel will be added to.
The channel entity will become a child of the resource entity.
Channel logic is handled by static functions on ChannelComponent such as Open, Close and Send.
Calling Send with an improper configuration (e.g. at least one of the connected channels is closed or the channel is not connected in the first place) causes it to fail.
This can be caught using the returned boolean value which in this case will be false.
Note that when a pair of channels remain ready after Send is called (and the subsequent OnReceive callback fires), OnInputChannelReady and OnOutputChannelReady fire immediately.
Usage example
For instance, channels are very useful for material handling simulators. Details of such a system can be seen in the Source-Queue-Server-Sink model of the Getting Started section. Each simulation object is a resource with channels that link it with the other objects it is connected to, such as a queue (output) that feeds into a server (input). The act of sending a child corresponds to passing the material to the next object, an act that can only happen if the input channel is ready to accept it, e.g. the channel is open.
Code example
class MyComponent : public ScriptBehaviorComponent
{
public:
void OnInputChannelReady(EntityID inputChannel)
{
// Callback 1
// Fires when an input channel is ready
}
void OnOutputChannelReady(EntityID outputChannel)
{
// Callback 2
// Fires when an output channel is ready
}
void OnReceive(EntityID inputChannel, EntityID child)
{
// Callback 3
// Fires when a child is passed through an input channel
}
};
// ...
void UseChannelAndResource(SubModel subModel)
{
// Setup
EntityID resource1_entity = subModel.CreateEntity("Resource 1");
EntityID resource2_entity = subModel.CreateEntity("Resource 2");
subModel.AddComponent<ResourceComponent>(resource1_entity);
subModel.AddComponent<MyComponent>(resource1_entity);
subModel.AddComponent<ResourceComponent>(resource2_entity);
subModel.AddComponent<MyComponent>(resource2_entity);
EntityID channel1_entity = subModel.CreateEntity("Channel 1");
EntityID channel2_entity = subModel.CreateEntity("Channel 2");
ChannelComponent::AddChannelComponent(channel1_entity, ChannelType::Output, resource1_entity);
ChannelComponent::AddChannelComponent(channel2_entity, ChannelType::Input, resource2_entity);
ChannelComponent::Connect(channel1_entity, channel2_entity);
EntityID child_entity = subModel.CreateEntity("Child");
subModel.UpdateParentOnEntity(child_entity, resource1_entity);
// Execution
ChannelComponent::Open(channel1_entity);
ChannelComponent::Open(channel2_entity);
// Child's parent is still Resource 1, Callback 2 fires on Resource 1, callback 1 fires on Resource 2
ChannelComponent::Send(channel1_entity, child_entity);
// Child's parent is now Resource 2, Callback 3 fires on Resource 2
}
class MyComponent : ScriptBehaviorComponent
{
public override void OnInputChannelReady(Entity inputChannel)
{
// Callback 1
// Fires when an input channel is ready
}
public override void OnOutputChannelReady(Entity outputChannel)
{
// Callback 2
// Fires when an output channel is ready
}
public override void OnReceive(Entity inputChannel, Entity child)
{
// Callback 3
// Fires when a child is passed through an input channel
}
}
// ...
public void UseChannelAndResource(SubModel subModel)
{
// Setup
Entity resource1_entity = subModel.CreateEntity("Resource 1");
Entity resource2_entity = subModel.CreateEntity("Resource 2");
resource1_entity.AddComponent<ResourceComponent>();
resource1_entity.AddComponent<MyComponent>();
resource2_entity.AddComponent<ResourceComponent>();
resource2_entity.AddComponent<MyComponent>();
Entity channel1_entity = subModel.CreateEntity("Channel 1");
Entity channel2_entity = subModel.CreateEntity("Channel 2");
ChannelComponent.AddChannelComponent(channel1_entity, ChannelType.Output, resource1_entity);
ChannelComponent.AddChannelComponent(channel2_entity, ChannelType.Input, resource2_entity);
ChannelComponent.Connect(channel1_entity, channel2_entity);
Entity child_entity = subModel.CreateEntity("Child");
subModel.UpdateParentOnEntity(child_entity, resource1_entity);
// Execution
ChannelComponent.Open(channel1_entity);
ChannelComponent.Open(channel2_entity);
// Child's parent is still Resource 1, Callback 2 fires on Resource 1, callback 1 fires on Resource 2
ChannelComponent.Send(channel1_entity, child_entity);
// Child's parent is now Resource 2, Callback 3 fires on Resource 2
}