Custom Network Simulator
With the Network Simulator API, a custom network simulation for CAN, LIN, Ethernet or Flexray is made accessible. The core concept is that all controller communication is redirected to a network simulator participant who performs the detailed simulation and is responsible for the final message distribution. The scope of the Network Simulator API is to manage the message flow for incoming and outgoing messages, not to perform the simulation logic.
Warning
The Network Simulator API is experimental and might be changed or removed in future versions of the SIL Kit.
Using the Network Simulator API
The usage of the Network Simulator API can be split into a configuration- and a simulation phase. In the configuration phase, the Network Simulator defines the simulated networks while other participants create their controllers. The SIL Kit takes care of informing the Network Simulator about the emerging controllers. Vice versa, the controllers are informed that their network is simulated by the Network Simulator. The controllers on a simulated network will route their outgoing messages exclusively to the Network Simulator. Note that this behavior switch does not require any action on the participant that holds the controller. If the controller was configured with a network name that the Network Simulator defined as a simulated network, the new routing will take place automatically.
In the simulation phase, each message (e.g., controller configuration, frame requests) triggers a callback on the Network Simulator. These callbacks will drive the simulation implemented by the user. For outgoing messages, the Network Simulator API allows to produce events that can target individual controllers. E.g., a frame request will finally result in a frame transmission for all controllers and an acknowledge message for the triggering controller.
Configuration phase
The entry point of the Network Simulator API is the creation of an INetworkSimulator
at a participant.
Only one network simulator per participant is allowed:
INetworkSimulator* networkSimulator = SilKit::Experimental::Participant::CreateNetworkSimulator(participant.get());
On the INetworkSimulator
instance, simulated networks can be registered.
This affects controller of the given type in the whole SIL Kit simulation, if their configured network name and type matches.
auto mySimulatedCanNetwork = std::make_unique<MySimulatedCanNetwork>("CAN1");
networkSimulator->SimulateNetwork("CAN1", SimulatedNetworkType::CAN, std::move(mySimulatedCanNetwork));
The instance mySimulatedCanNetwork
passed in SimulateNetwork
represents a specific, named simulated network.
The underlying class must inherit from the ISimulatedNetwork
interface and must be implemented by the user of the Network Simulator API.
Its purpose is twofold:
Providing a producer object to send out messages to targeted controllers. This happens once per network by a a call from the SIL Kit to
SetEventProducer()
, here on the mySimulatedCanNetwork instance. InSetEventProducer()
, anIEventProducer
instance is handed over to the user. After casting to the specific event producer for the corresponding network type (e.g. toICanEventProducer
), the event producer can send targeted messages to the desired recipients.
void MySimulatedCanNetwork::SetEventProducer(std::unique_ptr<IEventProducer> eventProducer) {
// Store the incoming event producer
_eventProducer = std::move(eventProducer);
}
auto MySimulatedCanNetwork::GetCanEventProducer()
{
// Cast and return the CAN event producer
return static_cast<Can::ICanEventProducer*>(_eventProducer.get());
}
Getting informed about emerging remote controllers and providing a target for the redirected communication (i.e. the simulated controller). This happens by calls from the SIL Kit to
ProvideSimulatedController()
, here on the mySimulatedCanNetwork instance, whenever a participant creates a controller. The method provides routing information to address the controller (i.e., the controller descriptor) and expects the user to return an instance that implements theISimulatedController
interface. For the different network types, one of the specific child classes (e.g.,ISimulatedCanController
for CAN networks) must be used. After providing the simulated controller, all messages from the original controller will be routed only to the Network Simulator and trigger message specific reception functions on the provided simulated controller instance. Other controllers will no longer receive the messages.
auto MySimulatedCanNetwork::ProvideSimulatedController(ControllerDescriptor controllerDescriptor) -> ISimulatedController*
{
// Bookkeeping of the controller descriptors
_controllerDescriptors.push_back(controllerDescriptor);
// Store the simulated controllers
_mySimulatedCanControllers.emplace_back(std::make_unique<MySimulatedCanController>(this, controllerDescriptor));
// Return the interface pointer
return _mySimulatedControllers.back().get();
}
The following graph shows the Network Simulator API usage of the configuration phase:
Simulation phase
Via the provided implementation of an ISimulatedController
in ProvideSimulatedController()
, the user receives events generated by the original controller.
An On...()
callback exists for each possible message of a controller.
E.g., if a remote controller on a simulated network intends to sends a CAN frame and calls SendFrame()
, OnFrameRequest()
is triggered on the ISimulatedCanController
.
The On...()
callbacks represent the message input of the Network Simulator.
For outgoing CAN messages, the Produce()
methods from the ICanEventProducer
can be used.
In the following code snippet, the simple simulation logic is to send out a CAN frame transmit event to the requesting controller and the actual CAN frame event as a broadcast to all controllers.
Here, the IEventProducer
was obtained in SetEventProducer()
and is stored in MySimulatedCanNetwork
.
To target only the desired recipients, the receivers must be specified as a span of controller descriptors in all Produce()
methods.
void MySimulatedCanController::OnFrameRequest(const CanFrameRequest& frameRequest)
{
// Create the transmit acknowledge for the sending CAN controller
Services::Can::CanFrameTransmitEvent ack;
ack.canId = frameRequest.frame.canId;
ack.status = Services::Can::CanTransmitStatus::Transmitted;
ack.timestamp = _scheduler->Now(); // The custom scheduler has knowledge about the virtual simulation time
ack.userContext = frameRequest.userContext;
// Define the single receiver (the remote controller)
std::array<ControllerDescriptor, 1> singleReceiverArray{_controllerDescriptor};
auto singleReceiver = SilKit::Util::MakeSpan(singleReceiverArray);
// Produce the transmit acknowledge event
_mySimulatedCanNetwork->GetCanEventProducer()->Produce(std::move(ack), singleReceiver);
// Distribute the frame to all controllers
Services::Can::CanFrameEvent frameEvent;
frameEvent.direction = Services::TransmitDirection::RX;
frameEvent.frame = frameRequest.frame;
frameEvent.timestamp = _scheduler->Now();
frameEvent.userContext = frameRequest.userContext;
std::vector<uint8_t> payloadBytes{frameRequest.frame.dataField.begin(), frameRequest.frame.dataField.end()};
frameEvent.frame.dataField = SilKit::Util::ToSpan(payloadBytes);
// Produce the actual frame event
_mySimulatedNetwork->GetCanEventProducer()->Produce(frameEvent, _mySimulatedNetwork->GetAllReceivers());
}
The following graph shows the corresponding event flow and Network Simulator API for the code snippet:
See the Network Simulator Demo for a complete example with class definitions and a custom scheduler.
Handling simulation time
A central use case of a Network Simulator is to simulate detailed time behavior of a virtual bus network.
Emulating network specific behavior is not part of the Network Simulator API and has to be implemented by the user.
For a simulation with virtual time synchronization, this can be achieved by a custom scheduler.
The scheduler is provided with time-related events in the Network Simulator and updates the simulation time in the SetSimulationStepHandler()
.
When the virtual time advances, the custom scheduler triggers the due events.
Further, the scheduler can provide the current simulation time for the produced events as shown in the code snippets.
Integrating in the SIL Kit Lifecycle
Usually, a Network Simulator is integrated in setup with distributed participants that use a coordinated lifecycle. This means that it is mandatory to know beforehand which participants will take part in the simulation and that all participants agree on a starting point of the simulation. Although it is not enforced by the Network Simulator API, this is the recommended lifecycle operation mode for SIL Kit simulations with a Network Simulator. The central reason is that in using a coordinated lifecycle, the Network Simulator cannot miss any message sent by the controllers, especially initial configuration messages that are essential to perform the bus simulation. The following aspects have to be considered that the Network Simulator is guaranteed to receive all messages in the distributed setup without any restrictions to the starting order of the participants:
The Network Simulator and all participants that hold simulated controllers must use a coordinated lifecycle.
As a consequence, a System Controller must be used that knows all required participant names.
The Network Simulator must call
Start()
before starting his lifecycle viaStartLifecycle()
.The participants must not trigger any bus message before their lifecycle state reaches
CommunicationReady
. Read the documentation on Lifecycle Management for further details.
Event flow
During the simulation, the Network Simulator is notified about all relevant calls of the controllers in the On...()
callbacks.
The Produce()
methods of the Network Simulator then trigger one or more handlers on the controller.
The following diagrams show the connection between this event flow for the various bus systems.
API and Data Type Reference
NetworkSimulator API
-
class INetworkSimulator
Network Simulator interface to register simulated networks and to start the network simulation. A network simulator object can be obtained via SilKit::Experimental::Participant::CreateNetworkSimulator. Note that there can only be one network simulater per participant.
Public Functions
-
virtual ~INetworkSimulator() = default
-
virtual void SimulateNetwork(const std::string &networkName, SimulatedNetworkType networkType, std::unique_ptr<ISimulatedNetwork> simulatedNetwork) = 0
Register a simulated network. The network of the given name and type is subject to a detailed simulation whose logic is provided by the custom network simulator. Controllers with the same network name and type will no longer broadcast their outgoing messages to other controllers. Instead, the traffic is routed to the network simulator participant and will arrive on a ISimulatedController. Based on the incoming messages (e.g., a frame request), the simulation logic can be performed and outgoing events (e.g. acknowledgments, frames) can be produced.
- Parameters
networkName – The name of the simulated network.
networkType – The type of the simulated network.
simulatedNetwork – Provided ISimulatedNetwork object to manage a simulated network. When a controller appears on a simulated network, the ISimulatedNetwork, which was passed as argument to this method, is informed with a call to ISimulatedNetwork::ProvideSimulatedController. There, a specific ISimulatedController (e.g. Can::ISimulatedCanController, Flexray::ISimulatedFlexRayController, etc.) can be provided to receive the messages sent by the original controller.
-
virtual void Start() = 0
Start the network simulation of all previously registered networks. Simulated networks registered via INetworkSimulator::SimulateNetwork will be informed about corresponding controllers in the simulation with calls to ISimulatedNetwork::ProvideSimulatedController. This holds true for remote controllers that have been created before the call to INetworkSimulator::Start. Internally, remote controllers are informed that they are now part of a detailed network simulation and will route their messages to the network simulator. See the documentation for further information about the usage within the SIL Kit Lifecyle in order not to miss any messages on the network simulator.
-
virtual ~INetworkSimulator() = default
-
class ISimulatedNetwork
Interface for a simulated network. An instance of a class inheriting from this interface is passed to the SIL Kit via INetworkSimulator::SimulateNetwork. The interface functions are then invoked by the SIL Kit.
Public Functions
-
virtual ~ISimulatedNetwork() = default
-
virtual auto ProvideSimulatedController(ControllerDescriptor controllerDescriptor) -> ISimulatedController* = 0
Called when the API requires the implementation of a simulated controller. This happens when a controller is created on a simulated network. The network must be registered via SilKit::Experimental::NetworkSimulation::INetworkSimulator::SimulateNetwork beforehand. After a simulated controller is provided, messages sent by the original controller will triggers message specific callbacks on the simulated controller.
- Parameters
controllerDescriptor – The identifier of a remote controller. Used to address a specific controller as receiver when using the eventProducer.
- Returns
The implementation must return a valid pointer to a SilKit::Experimental::NetworkSimulation::ISimulatedController. Note that this interface is abstract, the underlying class must inherit from the appropriate network-specific interface (e.g. Can::ISimulatedCanController, Flexray::ISimulatedFlexRayController, etc.).
-
virtual void SimulatedControllerRemoved(ControllerDescriptor controllerDescriptor) = 0
Deregistration of a simulated controller. Called when the participant that owns the controller leaves the simulation.
- Parameters
controllerDescriptor – The identifier of a remote controller.
-
virtual void SetEventProducer(std::unique_ptr<IEventProducer> eventProducer) = 0
Called once to provide an SilKit::Experimental::NetworkSimulation::IEventProducer. This happens when the first controller appears on a simulated network.
- Parameters
eventProducer – Used to generate events for a given group of receivers. Note that this interface is abstract, the object must be cast to the specific interface for the underlying network type (Can::ICanEventProducer, Flexray::IFlexRayEventProducer, etc.).
-
virtual ~ISimulatedNetwork() = default
-
class ISimulatedController
Base class of a simulated controller.
Subclassed by SilKit::Experimental::NetworkSimulation::Can::ISimulatedCanController, SilKit::Experimental::NetworkSimulation::Ethernet::ISimulatedEthernetController, SilKit::Experimental::NetworkSimulation::Flexray::ISimulatedFlexRayController, SilKit::Experimental::NetworkSimulation::Lin::ISimulatedLinController
Public Functions
-
virtual ~ISimulatedController() = default
-
virtual ~ISimulatedController() = default
-
class ISimulatedCanController : public SilKit::Experimental::NetworkSimulation::ISimulatedController
API for simulated CAN controllers. If a new CAN controller is created on a simulated network, an implementation of this interface can be provided in ISimulatedNetwork::ProvideSimulatedController. Messages generated by the original controller will be redirected to the simulated controller and trigger the On… callbacks.
Public Functions
-
virtual ~ISimulatedCanController() = default
-
virtual void OnSetBaudrate(const CanConfigureBaudrate &canConfigureBaudrate) = 0
Incoming CAN controller message. Triggered when a CAN controller calls SilKit::Services::Can::ICanController::SetBaudRate().
- Parameters
canConfigureBaudrate – The configured baud rate of the controller.
-
virtual void OnFrameRequest(const CanFrameRequest &canFrameRequest) = 0
Incoming CAN controller message. Triggered when a CAN controller calls SilKit::Services::Can::ICanController::SendFrame().
- Parameters
canFrameRequest – The CAN frame requested to be sent.
-
virtual void OnSetControllerMode(const CanControllerMode &canControllerMode) = 0
Incoming CAN controller message. Triggered when a CAN controller calls SilKit::Services::Can::ICanController::Start(), SilKit::Services::Can::ICanController::Stop(), SilKit::Services::Can::ICanController::Sleep() or SilKit::Services::Can::ICanController::Reset().
- Parameters
canControllerMode – The new controller state.
-
virtual ~ISimulatedCanController() = default
-
class ISimulatedEthernetController : public SilKit::Experimental::NetworkSimulation::ISimulatedController
API for simulated Ethernet controllers. If a new Ethernet controller is created on a simulated network, an implementation of this interface can be provided in ISimulatedNetwork::ProvideSimulatedController. Messages generated by the original controller will be redirected to the simulated controller and trigger the On… callbacks.
Public Functions
-
virtual ~ISimulatedEthernetController() = default
-
virtual void OnFrameRequest(const EthernetFrameRequest ðernetFrameRequest) = 0
Incoming Ethernet controller message. Triggered when a Ethernet controller calls SilKit::Services::Ethernet::IEthernetController::SendFrame.
- Parameters
ethernetFrameRequest – The Ethernet frame requested to be sent.
-
virtual void OnSetControllerMode(const EthernetControllerMode ðernetControllerMode) = 0
Incoming Ethernet controller message. Triggered when a Ethernet controller calls SilKit::Services::Ethernet::IEthernetController::Activate or . SilKit::Services::Ethernet::IEthernetController::Deactivate.
- Parameters
ethernetControllerMode – The current Ethernet controller mode.
-
virtual ~ISimulatedEthernetController() = default
-
class ISimulatedLinController : public SilKit::Experimental::NetworkSimulation::ISimulatedController
API for simulated LIN controllers. If a new LIN controller is created on a simulated network, an implementation of this interface can be provided in ISimulatedNetwork::ProvideSimulatedController. Messages generated by the original controller will be redirected to the simulated controller and trigger the On… callbacks.
Public Functions
-
virtual ~ISimulatedLinController() = default
-
virtual void OnFrameRequest(const LinFrameRequest &linFrameRequest) = 0
Incoming LIN controller message. Triggered when a LIN controller calls SilKit::Services::Lin::ILinController::SendFrame.
- Parameters
linFrameRequest – The LIN frame requested to be sent.
-
virtual void OnFrameHeaderRequest(const LinFrameHeaderRequest &linFrameHeaderRequest) = 0
Incoming LIN controller message. Triggered when a LIN controller calls SilKit::Services::Lin::ILinController::SendFrameHeader.
- Parameters
linFrameHeaderRequest – A LIN frame header.
-
virtual void OnWakeupPulse(const LinWakeupPulse &linWakeupPulse) = 0
Incoming LIN controller message. Triggered when a LIN controller calls SilKit::Services::Lin::ILinController::Wakeup.
- Parameters
linWakeupPulse – The Wakeup pulse without any further data except the event timestamp.
-
virtual void OnControllerConfig(const LinControllerConfig &linControllerConfig) = 0
Incoming LIN controller message. Triggered when a LIN controller calls SilKit::Services::Lin::ILinController::Init.
- Parameters
linControllerConfig – The LIN controller configuration and frame response data.
-
virtual void OnFrameResponseUpdate(const LinFrameResponseUpdate &linFrameResponseUpdate) = 0
Incoming LIN controller message. Triggered when a LIN controller calls SilKit::Services::Lin::ILinController::UpdateTxBuffer or SilKit::Services::Lin::ILinController::SendFrame.
- Parameters
linFrameResponseUpdate – An update for the frame response data.
-
virtual void OnControllerStatusUpdate(const LinControllerStatusUpdate &linControllerStatusUpdate) = 0
Incoming LIN controller message. Triggered when a LIN controller calls SilKit::Services::Lin::ILinController::Wakeup, SilKit::Services::Lin::ILinController::WakeupInternal, SilKit::Services::Lin::ILinController::GoToSleep or SilKit::Services::Lin::ILinController::GoToSleepInternal.
- Parameters
linControllerStatusUpdate – The new LIN controller status.
-
virtual ~ISimulatedLinController() = default
-
class ISimulatedFlexRayController : public SilKit::Experimental::NetworkSimulation::ISimulatedController
API for simulated FlexRay controllers. If a new FlexRay controller is created on a simulated network, an implementation of this interface can be provided in ISimulatedNetwork::ProvideSimulatedController. Messages generated by the original controller will be redirected to the simulated controller and trigger the On… callbacks.
Public Functions
-
virtual ~ISimulatedFlexRayController() = default
-
virtual void OnHostCommand(const FlexrayHostCommand &flexrayHostCommand) = 0
Incoming FlexRay controller message. Triggered when a FlexRay controller calls SilKit::Services::Flexray::IFlexrayController::Run(), SilKit::Services::Flexray::IFlexrayController::DeferredHalt(), SilKit::Services::Flexray::IFlexrayController::Freeze(), SilKit::Services::Flexray::IFlexrayController::AllowColdstart(), SilKit::Services::Flexray::IFlexrayController::Wakeup() or SilKit::Services::Flexray::IFlexrayController::AllSlots().
- Parameters
flexrayHostCommand – An identifier of the host command.
-
virtual void OnControllerConfig(const FlexrayControllerConfig &flexrayControllerConfig) = 0
Incoming FlexRay controller message. Triggered when a FlexRay controller calls SilKit::Services::Flexray::IFlexrayController::Configure.
- Parameters
flexrayControllerConfig – The FlexRay cluster parameters, node parameters and initial TX buffer configuration.
-
virtual void OnTxBufferConfigUpdate(const FlexrayTxBufferConfigUpdate &flexrayTxBufferConfigUpdate) = 0
Incoming FlexRay controller message. Triggered when a FlexRay controller calls SilKit::Services::Flexray::IFlexrayController::ReconfigureTxBuffer.
- Parameters
flexrayTxBufferConfigUpdate – The index and the new configuration of a TX buffer .
-
virtual void OnTxBufferUpdate(const FlexrayTxBufferUpdate &flexrayTxBufferUpdate) = 0
Incoming FlexRay controller message. Triggered when a FlexRay controller calls SilKit::Services::Flexray::IFlexrayController::UpdateTxBuffer.
- Parameters
flexrayTxBufferUpdate – The index and the new payload of a TX buffer.
-
virtual ~ISimulatedFlexRayController() = default