Skip to content

Commit

Permalink
Demos: Overhaul SilKitApplication
Browse files Browse the repository at this point in the history
  • Loading branch information
KonradBkd committed Nov 7, 2024
1 parent ddcb5eb commit 89094ee
Show file tree
Hide file tree
Showing 8 changed files with 620 additions and 213 deletions.
3 changes: 3 additions & 0 deletions Demos/Can/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ target_sources(SilKitDemoCan
PRIVATE DemoCan.silkit.yaml
PRIVATE NetworkSimulatorConfig.yaml
)

make_silkit_demo(SilKitDemoCanWriter CanWriterDemo.cpp)
make_silkit_demo(SilKitDemoCanReader CanReaderDemo.cpp)
67 changes: 67 additions & 0 deletions Demos/Can/CanBehavior.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

#include "silkit/services/can/all.hpp"
#include "silkit/services/can/string_utils.hpp"
#include "silkit/services/logging/ILogger.hpp"

using namespace SilKit::Services::Can;

namespace std {
namespace chrono {
std::ostream& operator<<(std::ostream& out, nanoseconds timestamp)
{
auto seconds = std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(timestamp);
out << seconds.count() << "s";
return out;
}
} // namespace chrono
} // namespace std

namespace CanBehavior {

void FrameTransmitHandler(const CanFrameTransmitEvent& ack, ILogger* logger)
{
std::stringstream buffer;
buffer << ">> " << ack.status << " for CAN frame with timestamp=" << ack.timestamp
<< " and userContext=" << ack.userContext;
logger->Info(buffer.str());
}

void FrameHandler(const CanFrameEvent& frameEvent, ILogger* logger)
{
std::string payload(frameEvent.frame.dataField.begin(), frameEvent.frame.dataField.end());
std::stringstream buffer;
buffer << ">> CAN frame: canId=" << frameEvent.frame.canId << " timestamp=" << frameEvent.timestamp << " \""
<< payload << "\"";
logger->Info(buffer.str());
}

void SendFrame(ICanController* controller, ILogger* logger)
{
CanFrame canFrame{};
canFrame.canId = 3;
canFrame.flags |= static_cast<CanFrameFlagMask>(CanFrameFlag::Fdf) // FD Format Indicator
| static_cast<CanFrameFlagMask>(CanFrameFlag::Brs); // Bit Rate Switch (for FD Format only)

static int msgId = 0;
const auto currentMessageId = msgId++;

std::stringstream payloadBuilder;
payloadBuilder << "CAN " << (currentMessageId % 100);
auto payloadStr = payloadBuilder.str();

std::vector<uint8_t> payloadBytes;
payloadBytes.resize(payloadStr.size());
std::copy(payloadStr.begin(), payloadStr.end(), payloadBytes.begin());

canFrame.dataField = payloadBytes;
canFrame.dlc = static_cast<uint16_t>(canFrame.dataField.size());

void* const userContext = reinterpret_cast<void*>(static_cast<intptr_t>(currentMessageId));

controller->SendFrame(std::move(canFrame), userContext);
std::stringstream buffer;
buffer << "<< CAN frame sent with userContext=" << userContext;
logger->Info(buffer.str());
}

} // namespace CanDemo
271 changes: 203 additions & 68 deletions Demos/Can/CanDemo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#include "silkit/services/can/string_utils.hpp"


#include "SilKitApplication.hpp"

using namespace SilKit::Services::Orchestration;
using namespace SilKit::Services::Can;
using namespace SilKit::Services::Logging;
Expand All @@ -56,97 +54,234 @@ std::ostream& operator<<(std::ostream& out, nanoseconds timestamp)
} // namespace chrono
} // namespace std

void FrameTransmitHandler(const CanFrameTransmitEvent& ack, ILogger* logger)
{
std::stringstream buffer;
buffer << ">> " << ack.status << " for CAN frame with timestamp=" << ack.timestamp
<< " and userContext=" << ack.userContext;
logger->Info(buffer.str());
}

void FrameHandler(const CanFrameEvent& frameEvent, ILogger* logger)
{
std::string payload(frameEvent.frame.dataField.begin(), frameEvent.frame.dataField.end());
std::stringstream buffer;
buffer << ">> CAN frame: canId=" << frameEvent.frame.canId << " timestamp=" << frameEvent.timestamp << " \""
<< payload << "\"";
logger->Info(buffer.str());
}

class CanApplication : public SilKitApplication
void SendFrame(ICanController* controller, ILogger* logger)
{
private:
ICanController* canController_{nullptr};
CanFrame canFrame{};
canFrame.canId = 3;
canFrame.flags |= static_cast<CanFrameFlagMask>(CanFrameFlag::Fdf) // FD Format Indicator
| static_cast<CanFrameFlagMask>(CanFrameFlag::Brs); // Bit Rate Switch (for FD Format only)

public:
using SilKitApplication::SilKitApplication; //constructors
static int msgId = 0;
const auto currentMessageId = msgId++;

void OnSetup() override
{
std::stringstream payloadBuilder;
payloadBuilder << "CAN " << (currentMessageId % 100);
auto payloadStr = payloadBuilder.str();

canController_ = Participant().CreateCanController("CAN1", "CAN1");
std::vector<uint8_t> payloadBytes;
payloadBytes.resize(payloadStr.size());
std::copy(payloadStr.begin(), payloadStr.end(), payloadBytes.begin());

canController_->AddFrameTransmitHandler( std::bind(&CanApplication::FrameTransmitHandler, this, std::placeholders::_1, std::placeholders::_2));
canController_->AddFrameHandler(std::bind(&CanApplication::FrameHandler, this, std::placeholders::_1, std::placeholders::_2));
}

void OnCommunicationReady() override
{
std::cout << "Communication ready for " << ParticipantName() << std::endl;
canController_->SetBaudRate(10'000, 1'000'000, 2'000'000);
canController_->Start();
}
canFrame.dataField = payloadBytes;
canFrame.dlc = static_cast<uint16_t>(canFrame.dataField.size());

// Synchronous work: do simulation steps
void SimulationStepHandler(std::chrono::nanoseconds now, std::chrono::nanoseconds duration) override
{
std::cout << "now=" << now << ", duration=" << duration << std::endl;
SendFrame();
std::this_thread::sleep_for(100ms);
}
void* const userContext = reinterpret_cast<void*>(static_cast<intptr_t>(currentMessageId));

controller->SendFrame(std::move(canFrame), userContext);
std::stringstream buffer;
buffer << "<< CAN frame sent with userContext=" << userContext;
logger->Info(buffer.str());
}

// Asynchronous work: just send as fast as possible
void DoWork() override
/**************************************************************************************************
* Main Function
**************************************************************************************************/

int main(int argc, char** argv)
{
if (argc < 3)
{
std::cout << "DoWork: sending a frame" << std::endl;
SendFrame();
std::this_thread::sleep_for(1000ms);
std::cerr << "Missing arguments! Start demo with: " << argv[0]
<< " <ParticipantConfiguration.yaml|json> <ParticipantName> [RegistryUri] [--async]" << std::endl
<< "Use \"CanWriter\" or \"CanReader\" as <ParticipantName>." << std::endl;
return -1;
}

void FrameTransmitHandler(ICanController* /*unused*/, const CanFrameTransmitEvent& ack)
if (argc > 5)
{
std::stringstream buffer;
buffer << ">> " << ack.status << " for CAN frame with timestamp=" << ack.timestamp
<< " and userContext=" << ack.userContext;
Logger().Info(buffer.str());
std::cerr << "Too many arguments! Start demo with: " << argv[0]
<< " <ParticipantConfiguration.yaml|json> <ParticipantName> [RegistryUri] [--async]" << std::endl
<< "Use \"CanWriter\" or \"CanReader\" as <ParticipantName>." << std::endl;
return -1;
}

void FrameHandler(ICanController* /*unused*/, const CanFrameEvent& frameEvent)
std::string participantName(argv[2]);

if (participantName != "CanWriter" && participantName != "CanReader")
{
std::string payload(frameEvent.frame.dataField.begin(), frameEvent.frame.dataField.end());
std::stringstream buffer;
buffer << ">> CAN frame: canId=" << frameEvent.frame.canId << " timestamp=" << frameEvent.timestamp << " \""
<< payload << "\"";
Logger().Info(buffer.str());
std::cout << "Wrong participant name provided. Use either \"CanWriter\" or \"CanReader\"." << std::endl;
return -1;
}

void SendFrame()
try
{
CanFrame canFrame{};
canFrame.canId = 3;
canFrame.flags |= static_cast<CanFrameFlagMask>(CanFrameFlag::Fdf) // FD Format Indicator
| static_cast<CanFrameFlagMask>(CanFrameFlag::Brs); // Bit Rate Switch (for FD Format only)
std::string participantConfigurationFilename(argv[1]);

std::string registryUri = "silkit://localhost:8500";

bool runSync = true;

std::vector<std::string> args;
std::copy((argv + 3), (argv + argc), std::back_inserter(args));

static int msgId = 0;
const auto currentMessageId = msgId++;
for (auto arg : args)
{
if (arg == "--async")
{
runSync = false;
}
else
{
registryUri = arg;
}
}

std::stringstream payloadBuilder;
payloadBuilder << "CAN " << (currentMessageId % 100);
auto payloadStr = payloadBuilder.str();
auto participantConfiguration =
SilKit::Config::ParticipantConfigurationFromFile(participantConfigurationFilename);
auto sleepTimePerTick = 1000ms;

std::vector<uint8_t> payloadBytes;
payloadBytes.resize(payloadStr.size());
std::copy(payloadStr.begin(), payloadStr.end(), payloadBytes.begin());
std::cout << "Creating participant '" << participantName << "' with registry " << registryUri << std::endl;

canFrame.dataField = payloadBytes;
canFrame.dlc = static_cast<uint16_t>(canFrame.dataField.size());
auto participant = SilKit::CreateParticipant(participantConfiguration, participantName, registryUri);

void* const userContext = reinterpret_cast<void*>(static_cast<intptr_t>(currentMessageId));
auto* logger = participant->GetLogger();
auto* canController = participant->CreateCanController("CAN1", "CAN1");

canController_->SendFrame(std::move(canFrame), userContext);
std::stringstream buffer;
buffer << "<< CAN frame sent with userContext=" << userContext;
Logger().Info(buffer.str());
canController->AddFrameTransmitHandler([logger](ICanController* /*ctrl*/, const CanFrameTransmitEvent& ack) {
FrameTransmitHandler(ack, logger);
});
canController->AddFrameHandler(
[logger](ICanController* /*ctrl*/, const CanFrameEvent& frameEvent) { FrameHandler(frameEvent, logger); });

auto operationMode = (runSync ? OperationMode::Coordinated : OperationMode::Autonomous);

auto* lifecycleService = participant->CreateLifecycleService({operationMode});

// Observe state changes
lifecycleService->SetStopHandler([]() { std::cout << "Stop handler called" << std::endl; });
lifecycleService->SetShutdownHandler([]() { std::cout << "Shutdown handler called" << std::endl; });
lifecycleService->SetAbortHandler(
[](auto lastState) { std::cout << "Abort handler called while in state " << lastState << std::endl; });

if (runSync)
{
lifecycleService->SetCommunicationReadyHandler([canController, &participantName]() {
std::cout << "Communication ready for " << participantName << std::endl;
canController->SetBaudRate(10'000, 1'000'000, 2'000'000);
canController->Start();
});

auto* timeSyncService = lifecycleService->CreateTimeSyncService();

if (participantName == "CanWriter")
{
timeSyncService->SetSimulationStepHandler(
[canController, logger, sleepTimePerTick](std::chrono::nanoseconds now,
std::chrono::nanoseconds duration) {
std::cout << "now=" << now << ", duration=" << duration << std::endl;
SendFrame(canController, logger);
std::this_thread::sleep_for(sleepTimePerTick);
},
5ms);
}
else
{
timeSyncService->SetSimulationStepHandler(
[sleepTimePerTick](std::chrono::nanoseconds now, std::chrono::nanoseconds duration) {
std::cout << "now=" << now << ", duration=" << duration << std::endl;
std::this_thread::sleep_for(sleepTimePerTick);
}, 5ms);
}

auto finalStateFuture = lifecycleService->StartLifecycle();
auto finalState = finalStateFuture.get();

std::cout << "Simulation stopped. Final State: " << finalState << std::endl;
std::cout << "Press enter to end the process..." << std::endl;
std::cin.ignore();
}
else
{
std::atomic<bool> isStopRequested = {false};
std::thread workerThread;

std::promise<void> promiseObj;
std::future<void> futureObj = promiseObj.get_future();
lifecycleService->SetCommunicationReadyHandler([&]() {
std::cout << "Communication ready for " << participantName << std::endl;
canController->SetBaudRate(10'000, 1'000'000, 2'000'000);

workerThread = std::thread{[&]() {
futureObj.get();
while (lifecycleService->State() == ParticipantState::ReadyToRun
|| lifecycleService->State() == ParticipantState::Running)
{
if (participantName == "CanWriter")
{
SendFrame(canController, logger);
}
std::this_thread::sleep_for(sleepTimePerTick);
}
if (!isStopRequested)
{
std::cout << "Press enter to end the process..." << std::endl;
}
}};
canController->Start();
});

lifecycleService->SetStartingHandler([&]() { promiseObj.set_value(); });

lifecycleService->StartLifecycle();
std::cout << "Press enter to leave the simulation..." << std::endl;
std::cin.ignore();

isStopRequested = true;
if (lifecycleService->State() == ParticipantState::Running
|| lifecycleService->State() == ParticipantState::Paused)
{
std::cout << "User requested to stop in state " << lifecycleService->State() << std::endl;
lifecycleService->Stop("User requested to stop");
}

if (workerThread.joinable())
{
workerThread.join();
}
std::cout << "The participant has shut down and left the simulation" << std::endl;
}
}
catch (const SilKit::ConfigurationError& error)
{
std::cerr << "Invalid configuration: " << error.what() << std::endl;
std::cout << "Press enter to end the process..." << std::endl;
std::cin.ignore();
return -2;
}
catch (const std::exception& error)
{
std::cerr << "Something went wrong: " << error.what() << std::endl;
std::cout << "Press enter to end the process..." << std::endl;
std::cin.ignore();
return -3;
}
};

int main(int argc, char** argv)
{
CanApplication app{"CanWriter"};
return app.Main(argc, argv);
return 0;
}
Loading

0 comments on commit 89094ee

Please sign in to comment.