vectorgrp / sil-kit

Vector SIL Kit – Open-Source Library for Connecting Software-in-the-Loop Environments
https://vectorgrp.github.io/sil-kit-docs
MIT License
107 stars 32 forks source link

How to exchange frames during a simulation step with SetSimulationStepHandlerAsync? #53

Closed VladislavNeufeld closed 5 months ago

VladislavNeufeld commented 5 months ago

Hello,

according to your documentation for Virtual Time Synchronization it is possible to exchange messages during a simulation step if the function SetSimulationStepHandlerAsync is used.

This [...] allows that messages can be sent and received during the simulation step. Using the SimulationStepHandlerAsync instead of the SetSimulationStepHandler enables the following scenarios: [...] Two-way communication inside the simulation step.

I have tried to exchange messages between sender and receiver process during a simulation step (simulate a delay in a step) but it is not possible. What I am doing wrong? The AddFrameHandler is only called after completion of the stepHandler:

`canController->AddFrameHandler([&](ICanController /ctrl/, const CanFrameEvent& /ctrl*/) { std::cout << "AddFrameHandler" << std::endl; });

    if (benchmark.isReceiver)
    {
        timeSyncService->SetSimulationStepHandlerAsync(
            [&](std::chrono::nanoseconds now, std::chrono::nanoseconds duration) {
                if (warmupCount < benchmark.warmup_cycles)
                {
                    if (stepCount % 2 == 0)
                    {
                        std::cout << "start delay" << std::endl;
                        std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // Simulate a delay
                        std::cout << "end delay" << std::endl;
                        stepCount++;

                    }
                    else
                    {
                        canController->SendFrame(canFrame);
                        warmupCount++;
                        stepCount++;
                    }
                }
                timeSyncService->CompleteSimulationStep();
            },
            milliseconds(1));
    }
    else
    {
        timeSyncService->SetSimulationStepHandlerAsync(
            [&](std::chrono::nanoseconds now, std::chrono::nanoseconds duration) {

                if (warmupCount < benchmark.warmup_cycles)
                {
                    if (stepCount % 2 == 0)
                    {
                        canController->SendFrame(canFrame);
                        warmupCount++;
                        stepCount++;

                        if (warmupCount == benchmark.warmup_cycles)
                        {
                            signal_warmup_completed = true;
                        }
                    }
                    else
                    {
                        stepCount++;
                    }
                }
                timeSyncService->CompleteSimulationStep();
            },
            milliseconds(1));
    }`
VDanielEdwards commented 5 months ago

Hello Vladislav,

in the code you are showing, the communication is still happening inside of the simulation step callback. To enable communication you have to trigger (e.g., using a condition variable) sending the frames from another thread (e.g., the main thread) and only complete the step afterwards - outside of the callback - with the appropriate delay.

Blocking (e.g., using std::this_thread::sleep) inside of any callback from SIL Kit will stop communication from this participant for the duration of the blocking call.

In general, you should aim to return from any callback as quickly as possible.

The example code for std::condition_variable should be a good starting point. If you need further support, just ask!

Best wishes, Daniel

VladislavNeufeld commented 5 months ago

Thank You!