Skip to content

Commit bdb9ebe

Browse files
#36 Add SetStepDuration and TimeAdvanceModes
1 parent f6f4159 commit bdb9ebe

File tree

23 files changed

+371
-31
lines changed

23 files changed

+371
-31
lines changed

Demos/api/Orchestration/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
make_silkit_demo(SilKitDemoAutonomous Autonomous.cpp OFF)
66
make_silkit_demo(SilKitDemoCoordinated Coordinated.cpp OFF)
77
make_silkit_demo(SilKitDemoSimStep SimStep.cpp OFF)
8+
make_silkit_demo(SilKitDemoDynSimStep DynSimStep.cpp OFF)
89
make_silkit_demo(SilKitDemoSimStepAsync SimStepAsync.cpp OFF)
910

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// SPDX-FileCopyrightText: 2024 Vector Informatik GmbH
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include <iostream>
6+
#include <random>
7+
8+
#include "silkit/SilKit.hpp"
9+
10+
using namespace std::chrono_literals;
11+
12+
std::ostream& operator<<(std::ostream& out, std::chrono::nanoseconds timestamp)
13+
{
14+
out << std::chrono::duration_cast<std::chrono::milliseconds>(timestamp).count() << "ms";
15+
return out;
16+
}
17+
18+
int main(int argc, char** argv)
19+
{
20+
if (argc != 2)
21+
{
22+
std::cerr << "Wrong number of arguments! Start demo with: " << argv[0] << " <ParticipantName>" << std::endl;
23+
return -1;
24+
}
25+
std::string participantName(argv[1]);
26+
27+
try
28+
{
29+
// Setup participant, lifecycle, time synchronization and logging.
30+
const std::string registryUri = "silkit://localhost:8500";
31+
const std::string configString = R"({"Logging":{"Sinks":[{"Type":"Stdout","Level":"Info"}]}})";
32+
auto participantConfiguration = SilKit::Config::ParticipantConfigurationFromString(configString);
33+
34+
auto participant = SilKit::CreateParticipant(participantConfiguration, participantName, registryUri);
35+
auto logger = participant->GetLogger();
36+
37+
auto* lifecycleService =
38+
participant->CreateLifecycleService({SilKit::Services::Orchestration::OperationMode::Coordinated});
39+
40+
auto* timeSyncService = lifecycleService->CreateTimeSyncService(SilKit::Services::Orchestration::TimeAdvanceMode::ByMinimalDuration);
41+
42+
const auto stepSize = 10ms;
43+
static int stepCounter = 0;
44+
std::random_device rd;
45+
std::mt19937 rng(rd());
46+
auto bounded_rand = [&rng](unsigned range) {
47+
std::uniform_int_distribution<unsigned> dist(1, range);
48+
return dist(rng);
49+
};
50+
51+
52+
timeSyncService->SetSimulationStepHandler(
53+
[logger, timeSyncService, participantName, bounded_rand](std::chrono::nanoseconds now,
54+
std::chrono::nanoseconds duration) {
55+
// The invocation of this handler marks the beginning of a simulation step.
56+
{
57+
std::stringstream ss;
58+
ss << "--------- Simulation step T=" << now << ", duration=" << duration << " ---------";
59+
logger->Info(ss.str());
60+
}
61+
62+
if (bounded_rand(10) == 1)// && participantName == "P1")
63+
{
64+
auto rndStepDuration = bounded_rand(10);
65+
timeSyncService->SetStepDuration(std::chrono::milliseconds(rndStepDuration));
66+
std::stringstream ss;
67+
ss << "--------- Changing step size to " << rndStepDuration << "ms ---------";
68+
logger->Info(ss.str());
69+
}
70+
71+
std::this_thread::sleep_for(500ms);
72+
// All messages sent here are guaranteed to arrive at other participants before their next simulation step is called.
73+
// So here, we can rely on having received all messages from the past (< now).
74+
// Note that this guarantee only holds for messages sent within a simulation step,
75+
// not for messages send outside of this handler (e.g. directly in a reception handler).
76+
77+
// Returning from the handler marks the end of a simulation step.
78+
}, stepSize);
79+
80+
auto finalStateFuture = lifecycleService->StartLifecycle();
81+
finalStateFuture.get();
82+
}
83+
catch (const std::exception& error)
84+
{
85+
std::cerr << "Something went wrong: " << error.what() << std::endl;
86+
return -2;
87+
}
88+
89+
return 0;
90+
}

Demos/api/Orchestration/SimStep.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,18 @@ int main(int argc, char** argv)
3838

3939
auto* timeSyncService = lifecycleService->CreateTimeSyncService();
4040

41-
const auto stepSize = 1ms;
41+
const auto stepSize = 2ms;
42+
4243
timeSyncService->SetSimulationStepHandler(
43-
[logger](std::chrono::nanoseconds now, std::chrono::nanoseconds /*duration*/) {
44+
[logger](std::chrono::nanoseconds now, std::chrono::nanoseconds duration) {
4445
// The invocation of this handler marks the beginning of a simulation step.
46+
{
47+
std::stringstream ss;
48+
ss << "--------- Simulation step T=" << now << ", duration=" << duration << " ---------";
49+
logger->Info(ss.str());
50+
}
4551

46-
std::stringstream ss;
47-
ss << "--------- Simulation step T=" << now << " ---------";
48-
logger->Info(ss.str());
49-
52+
std::this_thread::sleep_for(500ms);
5053
// All messages sent here are guaranteed to arrive at other participants before their next simulation step is called.
5154
// So here, we can rely on having received all messages from the past (< now).
5255
// Note that this guarantee only holds for messages sent within a simulation step,

SilKit/IntegrationTests/Hourglass/MockCapi.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,12 @@ extern "C"
614614
return globalCapi->SilKit_TimeSyncService_Create(outTimeSyncService, lifecycleService);
615615
}
616616

617+
SilKit_ReturnCode SilKitCALL SilKit_TimeSyncService_Create_With_TimeAdvanceMode(SilKit_TimeSyncService** outTimeSyncService,
618+
SilKit_LifecycleService* lifecycleService, SilKit_TimeAdvanceMode timeAdvanceMode)
619+
{
620+
return globalCapi->SilKit_TimeSyncService_Create_With_TimeAdvanceMode(outTimeSyncService, lifecycleService, timeAdvanceMode);
621+
}
622+
617623
SilKit_ReturnCode SilKitCALL SilKit_TimeSyncService_SetSimulationStepHandler(
618624
SilKit_TimeSyncService* timeSyncService, void* context, SilKit_TimeSyncService_SimulationStepHandler_t handler,
619625
SilKit_NanosecondsTime initialStepSize)
@@ -641,6 +647,12 @@ extern "C"
641647
return globalCapi->SilKit_TimeSyncService_Now(timeSyncService, outNanosecondsTime);
642648
}
643649

650+
SilKit_ReturnCode SilKitCALL SilKit_TimeSyncService_SetStepDuration(SilKit_TimeSyncService* timeSyncService,
651+
SilKit_NanosecondsTime stepDuration)
652+
{
653+
return globalCapi->SilKit_TimeSyncService_SetStepDuration(timeSyncService, stepDuration);
654+
}
655+
644656
SilKit_ReturnCode SilKitCALL SilKit_Experimental_TimeSyncService_AddOtherSimulationStepsCompletedHandler(
645657
SilKit_TimeSyncService* timeSyncService, void* context,
646658
SilKit_Experimental_TimeSyncService_OtherSimulationStepsCompletedHandler_t handler,

SilKit/IntegrationTests/Hourglass/MockCapi.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,9 @@ class MockCapi
325325
MOCK_METHOD(SilKit_ReturnCode, SilKit_TimeSyncService_Create,
326326
(SilKit_TimeSyncService * *outTimeSyncService, SilKit_LifecycleService* lifecycleService));
327327

328+
MOCK_METHOD(SilKit_ReturnCode, SilKit_TimeSyncService_Create_With_TimeAdvanceMode,
329+
(SilKit_TimeSyncService * *outTimeSyncService, SilKit_LifecycleService* lifecycleService, SilKit_TimeAdvanceMode timeAdvanceMode));
330+
328331
MOCK_METHOD(SilKit_ReturnCode, SilKit_TimeSyncService_SetSimulationStepHandler,
329332
(SilKit_TimeSyncService * timeSyncService, void* context,
330333
SilKit_TimeSyncService_SimulationStepHandler_t handler, SilKit_NanosecondsTime initialStepSize));
@@ -339,6 +342,9 @@ class MockCapi
339342
MOCK_METHOD(SilKit_ReturnCode, SilKit_TimeSyncService_Now,
340343
(SilKit_TimeSyncService * timeSyncService, SilKit_NanosecondsTime* outNanosecondsTime));
341344

345+
MOCK_METHOD(SilKit_ReturnCode, SilKit_TimeSyncService_SetStepDuration,
346+
(SilKit_TimeSyncService * timeSyncService, SilKit_NanosecondsTime stepDuration));
347+
342348
MOCK_METHOD(SilKit_ReturnCode, SilKit_Experimental_TimeSyncService_AddOtherSimulationStepsCompletedHandler,
343349
(SilKit_TimeSyncService * timeSyncService, void* context,
344350
SilKit_Experimental_TimeSyncService_OtherSimulationStepsCompletedHandler_t handler,

SilKit/IntegrationTests/Hourglass/Test_HourglassOrchestration.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ class Test_HourglassOrchestration : public SilKitHourglassTests::MockCapiTest
148148
.WillByDefault(DoAll(SetArgPointee<0>(mockLifecycleService), Return(SilKit_ReturnCode_SUCCESS)));
149149
ON_CALL(capi, SilKit_TimeSyncService_Create(_, _))
150150
.WillByDefault(DoAll(SetArgPointee<0>(mockTimeSyncService), Return(SilKit_ReturnCode_SUCCESS)));
151+
ON_CALL(capi, SilKit_TimeSyncService_Create_With_TimeAdvanceMode(_, _, _))
152+
.WillByDefault(DoAll(SetArgPointee<0>(mockTimeSyncService), Return(SilKit_ReturnCode_SUCCESS)));
151153
ON_CALL(capi, SilKit_SystemMonitor_Create(_, _))
152154
.WillByDefault(DoAll(SetArgPointee<0>(mockSystemMonitor), Return(SilKit_ReturnCode_SUCCESS)));
153155
ON_CALL(capi, SilKit_Experimental_SystemController_Create(_, _))
@@ -362,6 +364,23 @@ TEST_F(Test_HourglassOrchestration, SilKit_TimeSyncService_Create)
362364
mockLifecycleService};
363365
}
364366

367+
TEST_F(Test_HourglassOrchestration, SilKit_TimeSyncService_Create_With_TimeAdvanceMode)
368+
{
369+
EXPECT_CALL(capi, SilKit_TimeSyncService_Create_With_TimeAdvanceMode(testing::_, mockLifecycleService,
370+
SilKit_TimeAdvanceMode_ByMinimalDuration));
371+
372+
SilKit::DETAIL_SILKIT_DETAIL_NAMESPACE_NAME::Impl::Services::Orchestration::TimeSyncService
373+
timeSyncService_ByMinimalDuration{
374+
mockLifecycleService, SilKit::Services::Orchestration::TimeAdvanceMode::ByMinimalDuration};
375+
376+
EXPECT_CALL(capi, SilKit_TimeSyncService_Create_With_TimeAdvanceMode(testing::_, mockLifecycleService,
377+
SilKit_TimeAdvanceMode_ByOwnDuration));
378+
379+
SilKit::DETAIL_SILKIT_DETAIL_NAMESPACE_NAME::Impl::Services::Orchestration::TimeSyncService
380+
timeSyncService_ByOwnDuration{
381+
mockLifecycleService, SilKit::Services::Orchestration::TimeAdvanceMode::ByOwnDuration};
382+
}
383+
365384
TEST_F(Test_HourglassOrchestration, SilKit_TimeSyncService_SetSimulationStepHandler)
366385
{
367386
const std::chrono::nanoseconds initialStepSize{0x123456};
@@ -415,6 +434,20 @@ TEST_F(Test_HourglassOrchestration, SilKit_TimeSyncService_Now)
415434
EXPECT_EQ(timeSyncService.Now(), nanoseconds);
416435
}
417436

437+
TEST_F(Test_HourglassOrchestration, SilKit_TimeSyncService_SetStepDuration)
438+
{
439+
const std::chrono::nanoseconds stepDuration{0x123456};
440+
441+
SilKit::DETAIL_SILKIT_DETAIL_NAMESPACE_NAME::Impl::Services::Orchestration::TimeSyncService timeSyncService{
442+
mockLifecycleService};
443+
444+
EXPECT_CALL(capi, SilKit_TimeSyncService_SetStepDuration(mockTimeSyncService, testing::_))
445+
.WillOnce(Return(SilKit_ReturnCode_SUCCESS));
446+
447+
timeSyncService.SetStepDuration(stepDuration);
448+
}
449+
450+
418451
TEST_F(Test_HourglassOrchestration, SilKit_Experimental_TimeSyncService_AddOtherSimulationStepsCompletedHandler)
419452
{
420453
using testing::_;

SilKit/include/silkit/capi/Orchestration.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ typedef int8_t SilKit_OperationMode;
9494
#define SilKit_OperationMode_Autonomous ((SilKit_OperationMode)20)
9595

9696

97+
/*! The TimeAdvanceMode. */
98+
typedef int8_t SilKit_TimeAdvanceMode;
99+
100+
#define SilKit_TimeAdvanceMode_ByOwnDuration ((SilKit_TimeAdvanceMode)0)
101+
#define SilKit_TimeAdvanceMode_ByMinimalDuration ((SilKit_TimeAdvanceMode)10)
102+
97103
/*! Details about a status change of a participant. */
98104
typedef struct
99105
{
@@ -452,6 +458,20 @@ SilKitAPI SilKit_ReturnCode SilKitCALL SilKit_TimeSyncService_Create(SilKit_Time
452458
typedef SilKit_ReturnCode(SilKitFPTR* SilKit_TimeSyncService_Create_t)(SilKit_TimeSyncService** outTimeSyncService,
453459
SilKit_LifecycleService* lifecycleService);
454460

461+
/*! \brief Create a time sync service at this SIL Kit simulation participant.
462+
* \param outTimeSyncService Pointer that refers to the resulting time sync service (out parameter).
463+
* \param lifecycleService The lifecyle service at which the time sync service should be created.
464+
* \param timeAdvanceMode The time advance mode for this time sync service.
465+
*
466+
* The object returned must not be deallocated using free()!
467+
*/
468+
SilKitAPI SilKit_ReturnCode SilKitCALL SilKit_TimeSyncService_Create_With_TimeAdvanceMode(SilKit_TimeSyncService** outTimeSyncService,
469+
SilKit_LifecycleService* lifecycleService, SilKit_TimeAdvanceMode timeAdvanceMode);
470+
471+
typedef SilKit_ReturnCode(SilKitFPTR* SilKit_TimeSyncService_Create_With_TimeAdvanceMode_t)(SilKit_TimeSyncService** outTimeSyncService,
472+
SilKit_LifecycleService* lifecycleService,
473+
SilKit_TimeAdvanceMode timeAdvanceMode);
474+
455475
/*! \brief The handler to be called if the simulation task is due
456476
*
457477
* \param context The user provided context passed in \ref SilKit_TimeSyncService_SetSimulationStepHandler
@@ -528,6 +548,18 @@ typedef SilKit_ReturnCode(SilKitFPTR* SilKit_TimeSyncService_Now_t)(SilKit_TimeS
528548
SilKit_NanosecondsTime* outNanosecondsTime);
529549

530550

551+
/*! \brief Set the duration of the next simulation step
552+
*
553+
* \param timeSyncService The time sync service obtained via \ref SilKit_TimeSyncService_Create.
554+
* \param stepDuration The step size in nanoseconds.
555+
*/
556+
SilKitAPI SilKit_ReturnCode SilKitCALL SilKit_TimeSyncService_SetStepDuration(SilKit_TimeSyncService* timeSyncService,
557+
SilKit_NanosecondsTime stepDuration);
558+
559+
typedef SilKit_ReturnCode(SilKitFPTR* SilKit_TimeSyncService_SetStepDuration_t)(SilKit_TimeSyncService* timeSyncService,
560+
SilKit_NanosecondsTime stepDuration);
561+
562+
531563
/*
532564
*
533565
* System Monitor

SilKit/include/silkit/detail/impl/services/orchestration/LifecycleService.hpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class LifecycleService : public SilKit::Services::Orchestration::ILifecycleServi
6666

6767
inline auto CreateTimeSyncService() -> SilKit::Services::Orchestration::ITimeSyncService* override;
6868

69+
inline auto CreateTimeSyncService(SilKit::Services::Orchestration::TimeAdvanceMode timeAdvanceMode)
70+
-> SilKit::Services::Orchestration::ITimeSyncService* override;
71+
6972
private:
7073
SilKit_LifecycleService* _lifecycleService{nullptr};
7174

@@ -315,11 +318,22 @@ auto LifecycleService::Status() const -> const SilKit::Services::Orchestration::
315318

316319
auto LifecycleService::CreateTimeSyncService() -> SilKit::Services::Orchestration::ITimeSyncService*
317320
{
318-
_timeSyncService = std::make_unique<TimeSyncService>(_lifecycleService);
321+
_timeSyncService = std::make_unique<TimeSyncService>(
322+
_lifecycleService, SilKit::Services::Orchestration::TimeAdvanceMode::ByOwnDuration);
319323

320324
return _timeSyncService.get();
321325
}
322326

327+
// TODO bkd: Needed or can I use a default in the function above?
328+
auto LifecycleService::CreateTimeSyncService(SilKit::Services::Orchestration::TimeAdvanceMode timeAdvanceMode)
329+
-> SilKit::Services::Orchestration::ITimeSyncService*
330+
{
331+
_timeSyncService = std::make_unique<TimeSyncService>(_lifecycleService, timeAdvanceMode);
332+
333+
return _timeSyncService.get();
334+
}
335+
336+
323337
} // namespace Orchestration
324338
} // namespace Services
325339
} // namespace Impl

SilKit/include/silkit/detail/impl/services/orchestration/TimeSyncService.hpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ namespace Orchestration {
2222
class TimeSyncService : public SilKit::Services::Orchestration::ITimeSyncService
2323
{
2424
public:
25+
2526
inline explicit TimeSyncService(SilKit_LifecycleService* lifecycleService);
2627

28+
inline explicit TimeSyncService(SilKit_LifecycleService* lifecycleService,
29+
SilKit::Services::Orchestration::TimeAdvanceMode timeAdvanceMode);
30+
2731
inline ~TimeSyncService() override = default;
2832

2933
inline void SetSimulationStepHandler(SimulationStepHandler task, std::chrono::nanoseconds initialStepSize) override;
@@ -35,7 +39,9 @@ class TimeSyncService : public SilKit::Services::Orchestration::ITimeSyncService
3539

3640
inline auto Now() const -> std::chrono::nanoseconds override;
3741

38-
public:
42+
inline void SetStepDuration(std::chrono::nanoseconds stepDuration) override;
43+
44+
public:
3945
inline auto ExperimentalAddOtherSimulationStepsCompletedHandler(
4046
SilKit::Experimental::Services::Orchestration::OtherSimulationStepsCompletedHandler) -> SilKit::Util::HandlerId;
4147

@@ -85,6 +91,12 @@ TimeSyncService::TimeSyncService(SilKit_LifecycleService* lifecycleService)
8591
ThrowOnError(returnCode);
8692
}
8793

94+
TimeSyncService::TimeSyncService(SilKit_LifecycleService* lifecycleService, SilKit::Services::Orchestration::TimeAdvanceMode timeAdvanceMode)
95+
{
96+
const auto returnCode = SilKit_TimeSyncService_Create_With_TimeAdvanceMode(&_timeSyncService, lifecycleService, static_cast<SilKit_TimeAdvanceMode>(timeAdvanceMode));
97+
ThrowOnError(returnCode);
98+
}
99+
88100
void TimeSyncService::SetSimulationStepHandler(SimulationStepHandler task, std::chrono::nanoseconds initialStepSize)
89101
{
90102
auto ownedHandlerPtr = std::make_unique<SimulationStepHandler>(std::move(task));
@@ -140,6 +152,12 @@ auto TimeSyncService::Now() const -> std::chrono::nanoseconds
140152
return std::chrono::nanoseconds{nanosecondsTime};
141153
}
142154

155+
void TimeSyncService::SetStepDuration(std::chrono::nanoseconds stepDuration)
156+
{
157+
const auto returnCode = SilKit_TimeSyncService_SetStepDuration(_timeSyncService, stepDuration.count());
158+
ThrowOnError(returnCode);
159+
}
160+
143161
inline auto TimeSyncService::ExperimentalAddOtherSimulationStepsCompletedHandler(std::function<void()> handler)
144162
-> SilKit::Util::HandlerId
145163
{

SilKit/include/silkit/services/orchestration/ILifecycleService.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ class ILifecycleService
191191
/*! \brief Return the ITimeSyncService for the current ILifecycleService.
192192
*/
193193
virtual auto CreateTimeSyncService() -> ITimeSyncService* = 0;
194+
195+
virtual auto CreateTimeSyncService(TimeAdvanceMode timeAdvanceMode) -> ITimeSyncService* = 0;
194196
};
195197

196198
} // namespace Orchestration

0 commit comments

Comments
 (0)