    

 ## On this page

  

 

 # Implement and use a standalone activity

 Last update: 19.09.2025 

<a class="anchor" id="autotoc_md42"></a> Simulation Framework provides the possibility to inject any custom logic into a standalone activity, which is a separate process running outside `simfwk_cli`, but is able to connect with the simfwk core and communicate with other activities.

### <a class="anchor" id="autotoc_md43"></a>Interfaces and topic definition in RefSim

To implement one customized standalone activity, you need to have a look on essential interfaces: `<a class="el" href="base__activity_8h_source.xhtml">core/lifecycle/activity/base_activity.h</a>` , `<a class="el" href="standalone__activity__service_8h_source.xhtml">core/service/standalone_activity_service/standalone_activity_service.h</a>` , `<a class="el" href="i__standalone__activity__creator_8h_source.xhtml">core/service/standalone_activity_service/standalone_activity_creator/i_standalone_activity_creator.h</a>` and topic definitions `<a class="el" href="topic__registry_8h_source.xhtml">core/communication/topic_registry.h</a>` .

`BaseActivity` is the basic implementation of `IActivity` interface and used as base class of all customized activities. Inheriting classes, i.e. a concrete simulation activity, should implement their logic in `ExecuteStep()` and add needed pub/sub in `AddPublisherAndSubscriber()` method to achieve communications between others.

`IStandaloneActivityCreator` is the interface class for creating a standalone activity service. It defines how a customized activity needs to be instantiated through or not through `StandaloneInitData`.

`StandaloneActivityService` provides a running process of desired activity returned by interface `IStandaloneActivityCreator` and keeps it communicating and being scheduled by Simulation Framework core process.

`<a class="el" href="namespacetopic__registry.xhtml" title="The namespace for topic registry in communication module under namespace simulation_framework::core.">topic_registry</a>` is a dedicated namespace where defines list of concrete Topics that can be used for creation of communication channels with given underlying message. As default, DDS message/communication type will be applied. The name of Topic ( `TopicId` ), which is passed into constructor to Topic, is globally unique and represents one single Topic, the underlying message could be the same though.

Currently following Topics and Message Type are available to use with Simulation Framework core library:

Topic Id (string) C++ Topic Type Underlying DDS Message Type Info KpiLoggerTopic `KpiMessageTopicType` `rtidds::KpiMessage` Message to KpiLoggerActivity, each `KpiContent` inside `KpiMessage` will be logged into json DriverInputTopic `KpiMessageTopicType` `rtidds::KpiMessage` Message to KpiLoggerActivity as type 'log', to mimic driver behaviour data only SensorViewTopic `GenericBytesTopicType` `rtidds::GenericBytesMessage` Message to store osi `SensorView` protobuf message as bytes array TrafficUpdateTopic `GenericBytesTopicType` `rtidds::GenericBytesMessage` Message to store osi `TrafficUpdate` protobuf message as bytes array TrafficCommandTopic `GenericBytesTopicType` `rtidds::GenericBytesMessage` Message to store osi `TrafficCommand` protobuf message as bytes array ### <a class="anchor" id="autotoc_md44"></a>Limitation of topic

As mentioned in the sections above, activities can exchange data using messages through `Topics`. Users must be careful to ensure deterministic behavior of the simulation. This means that one `Topic` should have only one publisher. The issue with having multiple publishers on the same topic is that the first message from the faster publisher may arrive on the subscriber side and get consumed, while the second message from the slower publisher might be neglected. Hence, Simulation Framework enforces a "single publisher only" to ensure deterministic behavior of the simulation. If multiple publishers for one topic are instantiated, an exception will be thrown to prevent this.

### <a class="anchor" id="autotoc_md45"></a>Customize your topic

By means of any existing message type provided by `<a class="el" href="topic__registry_8h_source.xhtml">core/communication/topic_registry.h</a>`, you can create any Topic you need for your simulations. Example of creating a new Topic with Message Type `rtidds::GenericBytesMessage` in one line:

auto your_topic = std::make_shared&lt;Topic&lt;rtidds::GenericBytesMessage&gt;&gt;("YourTopicId");



After that, `your_topic` is ready to be used to instantiate your activities.

### <a class="anchor" id="autotoc_md46"></a>Create a Topic using GenericBytesMessage

`rtidds::GenericBytesMessage` is a generic message type which allows users to put any content as a serialized byte array. E.g. `GenericBytesTopicType`, which stores the [OSI protobuf](https://github.com/OpenSimulationInterface/open-simulation-interface) message as underlying payload, is a good example to show how this mechanism works out. If you have a protobuf message which you want to send, you only need to call protobuf C++ API like following:

 

YourProtobufMessage protobuf_msg;

 

rtidds::GenericBytesMessage generic_msg{};

 

generic_msg.timestamp().seconds(1);

generic_msg.timestamp().nanoseconds(0)

 

size\_t size = protobuf_msg.ByteSizeLong();

std::vector&lt;unsigned char&gt; proto_data_in_bytes;

proto_data_in_bytes.resize(size);

 

protobuf_msg.SerializeToArray(proto_data_in_bytes.data(), size);

 

generic_msg.size(size);

generic_msg.bytes_array(proto_data_in_bytes);



Then you will have a filled `GenericBytesMessage` generic\_msg with your desired content and can publish it into the Topic defined with "YourTopicId".

On the subscriber side, you just need to decode the message from bytes array.

YourProtobufMessage protobuf_msg;

if (!protobuf_msg.ParseFromArray(generic_msg.bytes_array().data(),

 generic_msg.size()))

{

 throw std::runtime_error("Error parsing proto msg from bytes array in generic\_msg! ");

}

ProcessYourProtoMsg(protobuf_msg);



For complete implementation example into your simulation, please refer `/example/my_customized_topic/my_activities.h` after installation.

### <a class="anchor" id="autotoc_md47"></a>Example standalone activity

There is an example implementation of `Standalone Activity` provided by the Simulation Framework delivery, namely `simulation_framework/example/my_activity/my_activity.h` . This `Activity` can be executed independently from `simfwk_cli` , and they will wait for simulation requests from `simfwk_cli` if they are selected in the simulation config.

Following the code API documentation and instruction in the comments of this example implementation, you can learn how to build your own `Activity` step by step and use it in a simulation.

### <a class="anchor" id="autotoc_md48"></a>Use one standalone activity

You can use the example standalone activity `my_test_activity` by defining the following JSON content in `simulation_scheduling`.

{

 "sim_instance_name": "simulation_config_uses_my_test_activity",

 "foxglove":

 {

 "data_streaming": true,

 "host_name": "localhost",

 "port": 8700,

 },

 "save_mcap": true,

 "activities": [

 {

 "name": "groundtruth_generator_activity",

 "is_primary_activity": true,

 "topics_cycling_info": [

 {

 "topic_id": "__all__",

 "topic_cycle_time_in_ms": 100

 }

 ],

 "type": "built-in"

 },

 {

 "name": "driver_model_activity",

 "depends_on": [

 "groundtruth_generator_activity"

 ],

 "type": "built-in"

 },

 {

 "name": "my_test_activity",

 "depends_on": [

 "groundtruth_generator_activity"

 ],

 "type": "standalone"

 },

 {

 "name": "kpi_evaluator_activity",

 "depends_on": [

 "groundtruth_generator_activity"

 ],

 "type": "built-in"

 },

 {

 "name": "kpi_logger_activity",

 "depends_on": [

 "kpi_evaluator_activity"

 ],

 "type": "built-in"

 }

 ]

}



and use it in a simulation by running `simfwk_cli`:

./simfwk_cli -s my_config.json



### <a class="anchor" id="autotoc_md49"></a>Access Global Static Parameters and Timestamp

The `IActivity` interface and its derived class, `<a class="el" href="base__activity_8h_source.xhtml">core/lifecycle/activity/base_activity.h</a>`, provide several APIs to retrieve information related to the current simulation context. These include the current simulation timestamp, the cycle time for an activity, the simulation output directories, and input files. Available APIs are as follows:

#### <a class="anchor" id="autotoc_md50"></a>Simulation timestamp

This API returns the current simulation timestamp, allowing your logic to access the exact simulation time at any given moment during execution.

time::Timestamp GetCurrentTimestamp() const final;



#### <a class="anchor" id="autotoc_md51"></a>Cycle time

This API returns the cycle time for this activity, based on the scheduling configuration you have provided for simulation.

std::chrono::milliseconds GetCycleTime() const final;



#### <a class="anchor" id="autotoc_md52"></a>Simulation output directory

This API returns the directory path where the outputs of the simulation are stored.

std::string GetSimulationOutputDirectory() const final;



#### <a class="anchor" id="autotoc_md53"></a>4. Customized parameter from comamnd line

This API returns the customized parameters for the simulation as a map.

const SimulationParameters::CustomizedParameters&amp; GetCustomizedParameters() const final;



In Simulation Framework Autonomy, three common input files, i.e. open scenario, driver input and user settings are assigned as "customized parameters" to the whole simulation and they can be retrieved by above API without any issue if the solver settings are correctly provided. The predefined names of these three common inputs can be found here `<a class="el" href="simulation__input__definition_8h_source.xhtml">autonomy/simulation/sim_instance/simulation_input_definition.h</a>`. To ensure any other custom input files or parameters are available via this API, they must be provided through CLI arguments using the pattern "name:value". For more instructions, please see [command line tool](md_documentation_2markdown_2command__line__tool.xhtml)