Skip to content

Commit

Permalink
Add dcp_fmu example
Browse files Browse the repository at this point in the history
  • Loading branch information
klausschuch committed Sep 3, 2024
1 parent 892a304 commit 1d5788b
Show file tree
Hide file tree
Showing 3 changed files with 335 additions and 0 deletions.
85 changes: 85 additions & 0 deletions dcp/cosine/Main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright: 2024 AVL List GmbH

#include "Slave.hpp"

#include <cstdint>
#include <exception>
#include <iostream>
#include <string>


static void show_usage(const std::string & name) {
std::cerr << "Usage: " << name << " <option(s)>"
<< "Options:\n"
<< "\t-p PORT \tPort used to listen for control packets\n"
<< "\t-o DESTINATION\tSlave description file written by the slave\n"
<< "\n"
<< "If binding on PORT is not possible (due to it being used by another\n"
<< "application, the first free port (greater than PORT) will be used.\n"
<< std::endl;
}


enum class ArgMode {
pos_args,
port_def,
output_def
};


int main(int argc, char *argv[]) {
if (argc < 1) {
show_usage(argv[0]);
return 1;
}

std::string host = "127.0.0.1";
uint16_t port = 40100;
std::string output_file = "";

ArgMode mode = ArgMode::pos_args;
for (size_t i = 1; i < static_cast<size_t>(argc); i++) {
std::string arg = argv[i];
if (mode == ArgMode::pos_args && arg == "-p") {
mode = ArgMode::port_def;
continue;
} else if (mode == ArgMode::pos_args && arg == "-o") {
mode = ArgMode::output_def;
continue;
} else if (mode == ArgMode::pos_args) {
show_usage(argv[0]);
return 1;
}

if (mode == ArgMode::port_def) {
port = std::atoi(arg.c_str());
mode = ArgMode::pos_args;
continue;
} else if (mode == ArgMode::output_def) {
output_file = arg;
mode = ArgMode::pos_args;
continue;
} else {
show_usage(argv[0]);
return 1;
}
}

while (true) {
try {
std::cout << "Control port: " << port << std::endl;
Slave slave(host, port, output_file);
slave.start();
break;
} catch (const std::system_error &) {
std::cout << "Port in use" << std::endl;
port++;
continue;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
return -1;
}
}

return 0;
}
199 changes: 199 additions & 0 deletions dcp/cosine/Slave.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright: 2024 AVL List GmbH

#ifndef SLAVE_H_
#define SLAVE_H_

#include <dcp/helper/Helper.hpp>
#include <dcp/log/OstreamLog.hpp>
#include <dcp/logic/DcpManagerSlave.hpp>
#include <dcp/model/constant/DcpLogLevel.hpp>
#include <dcp/model/constant/DcpDataType.hpp>
#include <dcp/driver/ethernet/udp/UdpDriver.hpp>
#include <dcp/xml/DcpSlaveDescriptionWriter.hpp>

#include <cstdint>
#include <cstdio>
#include <stdarg.h>
#include <thread>
#include <cmath>
#include <memory>
#include <string>


class Slave {
public:
Slave(const std::string & host, uint16_t port, const std::string & output_file) :
host_(host),
port_(port),
output_file_(output_file == "" ? "slave.dcpx" : output_file),
std_log_(std::cout)
{
this->udp_driver_ = std::make_shared<UdpDriver>(this->host_, this->port_);

this->generate_slave_description();

this->manager_ = std::make_shared<DcpManagerSlave>(*this->slave_description_, this->udp_driver_->getDcpDriver());

// set callbacks
this->manager_->setInitializeCallback<SYNC>(std::bind(&Slave::initialize, this));
this->manager_->setConfigureCallback<SYNC>(std::bind(&Slave::configure, this));

this->manager_->setSynchronizingStepCallback<SYNC>(std::bind(&Slave::doStep, this, std::placeholders::_1));
this->manager_->setSynchronizedStepCallback<SYNC>(std::bind(&Slave::doStep, this, std::placeholders::_1));
this->manager_->setRunningStepCallback<SYNC>(std::bind(&Slave::doStep, this, std::placeholders::_1));

this->manager_->setSynchronizingNRTStepCallback<SYNC>(std::bind(&Slave::doStep, this, std::placeholders::_1));
this->manager_->setSynchronizedNRTStepCallback<SYNC>(std::bind(&Slave::doStep, this, std::placeholders::_1));
this->manager_->setRunningNRTStepCallback<SYNC>(std::bind(&Slave::doStep, this, std::placeholders::_1));

this->manager_->setTimeResListener<SYNC>(std::bind(&Slave::setTimeRes, this, std::placeholders::_1, std::placeholders::_2));

// display log messages in the console
this->manager_->addLogListener(std::bind(&OstreamLog::logOstream, this->std_log_, std::placeholders::_1));
this->manager_->setGenerateLogString(true);
}

void generate_slave_description() {
slave_description_ = make_SlaveDescription_ptr(1, 0, "DCPCosine", "b5279485-720d-4542-9f29-bee4d9a75ef9");

// <OpMode>
slave_description_->OpMode.SoftRealTime = make_SoftRealTime_ptr();
slave_description_->OpMode.NonRealTime = make_NonRealTime_ptr();

// <TimeRes>
slave_description_->TimeRes.resolutionRanges.push_back(make_ResolutionRange(1, 100000, 10000));

// <TransportProtocols>
slave_description_->TransportProtocols.UDP_IPv4 = make_UDP_ptr();
slave_description_->TransportProtocols.UDP_IPv4->Control = make_Control_ptr(host_, port_);
slave_description_->TransportProtocols.UDP_IPv4->DAT_input_output = make_DAT_ptr(host_);
slave_description_->TransportProtocols.UDP_IPv4->DAT_input_output->availablePortRanges.push_back(make_AvailablePortRange(2048, 65535));
slave_description_->TransportProtocols.UDP_IPv4->DAT_parameter = make_DAT_ptr(host_);
slave_description_->TransportProtocols.UDP_IPv4->DAT_parameter->availablePortRanges.push_back(make_AvailablePortRange(2048, 65535));

// <CapabilityFlags>
slave_description_->CapabilityFlags.canAcceptConfigPdus = true;
slave_description_->CapabilityFlags.canHandleReset = true;
slave_description_->CapabilityFlags.canHandleVariableSteps = false;
slave_description_->CapabilityFlags.canMonitorHeartbeat = false;
slave_description_->CapabilityFlags.canProvideLogOnRequest = true;
slave_description_->CapabilityFlags.canProvideLogOnNotification = true;

// <Variables>
std::shared_ptr<CommonCausality_t> amplitude = make_CommonCausality_ptr<float64_t>();
amplitude->Float64->start = std::make_shared<std::vector<float64_t>>();
amplitude->Float64->start->push_back(4.0);
slave_description_->Variables.push_back(make_Variable_input("amplitude", 1, amplitude));

std::shared_ptr<Output_t> cosine = make_Output_ptr<float64_t>();
cosine->Float64->start = std::make_shared<std::vector<float64_t>>();
cosine->Float64->start->push_back(4.0);
slave_description_->Variables.push_back(make_Variable_output("cosine", 0, cosine));

// <Log>
slave_description_->Log = make_Log_ptr();
slave_description_->Log->categories.push_back(make_Category(1, "DCP_SLAVE"));
slave_description_->Log->templates.push_back(make_Template(1, 1, (uint8_t) DcpLogLevel::LVL_INFORMATION, "[Time = %float64]: cosine = %float64 * cos(%float64) = %float64"));
slave_description_->Log->templates.push_back(make_Template(2, 1, (uint8_t) DcpLogLevel::LVL_INFORMATION, "INIT: cosine = %float64 * cos(%float64) = %float64"));
slave_description_->Log->templates.push_back(make_Template(3, 1, (uint8_t) DcpLogLevel::LVL_INFORMATION, "CONFIG: cosine = %float64"));
slave_description_->Log->templates.push_back(make_Template(4, 1, (uint8_t) DcpLogLevel::LVL_INFORMATION, "CONFIG: amplitude = %float64"));

writeDcpSlaveDescription(*slave_description_, output_file_.c_str());
}

void configure() {
this->current_step_ = 0;
this->time_ = 0.0;

// inputs
this->amplitude_ = this->manager_->getInput<float64_t*>(this->amplitude_vr_);
*this->amplitude_ = 4.0;
#if defined(DEBUG)
this->manager_->Log(this->config_log_amplitude_, *this->amplitude_);
#endif

// outputs
this->cosine_ = this->manager_->getOutput<float64_t*>(this->cosine_vr_);
*this->cosine_ = 4.0;
#if defined(DEBUG)
this->manager_->Log(this->config_log_cosine_, *this->cosine_);
#endif
}

void initialize() {
*this->cosine_ = *this->amplitude_ * std::cos(0.0);
#if defined(DEBUG)
this->manager_->Log(this->init_log_cosine_, *this->amplitude_, this->time_, *this->cosine_);
#endif
}

void doStep(uint64_t steps) {
double delta_time = static_cast<double>(this->numerator_) / this->denominator_ * steps;

*this->cosine_ = *this->amplitude_ * std::cos(this->time_ + delta_time);

#if defined(DEBUG)
this->manager_->Log(this->sim_log_cosine_, this->time_, *this->amplitude_,
this->time_ + delta_time, *this->cosine_);
#endif

this->time_ += delta_time;
this->current_step_ += steps;
}

void setTimeRes(const uint32_t numerator, const uint32_t denominator) {
this->numerator_ = numerator;
this->denominator_ = denominator;
}

void start() {
this->manager_->start();
}

private:
// DCPLib handles
OstreamLog std_log_;

std::shared_ptr<DcpManagerSlave> manager_;
std::shared_ptr<UdpDriver> udp_driver_;
std::shared_ptr<SlaveDescription_t> slave_description_;

// control variables
uint64_t current_step_;
float64_t time_;

uint32_t numerator_;
uint32_t denominator_;

std::string host_;
uint16_t port_;

std::string output_file_;

// value references
const uint32_t cosine_vr_ = 0;
const uint32_t amplitude_vr_ = 1;

// variables
float64_t *cosine_;
float64_t *amplitude_;

// log templates
const LogTemplate sim_log_cosine_ = LogTemplate(1, 1, DcpLogLevel::LVL_INFORMATION,
"[Time = %float64]: cosine = %float64 * cos(%float64) = %float64",
{DcpDataType::float64, DcpDataType::float64, DcpDataType::float64, DcpDataType::float64});

const LogTemplate init_log_cosine_ = LogTemplate(2, 1, DcpLogLevel::LVL_INFORMATION,
"INIT: cosine = %float64 * cos(%float64) = %float64",
{DcpDataType::float64, DcpDataType::float64, DcpDataType::float64});

const LogTemplate config_log_cosine_ = LogTemplate(3, 1, DcpLogLevel::LVL_INFORMATION,
"CONFIG: cosine = %float64",
{DcpDataType::float64});

const LogTemplate config_log_amplitude_ = LogTemplate(4, 1, DcpLogLevel::LVL_INFORMATION,
"CONFIG: amplitude = %float64",
{DcpDataType::float64});
};

#endif /* SLAVE_H_ */
51 changes: 51 additions & 0 deletions examples/dcp_fmu/model.ssd
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<SystemStructureDescription xmlns="http://ssp-standard.org/SSP1/SystemStructureDescription"
xmlns:ssc="http://ssp-standard.org/SSP1/SystemStructureCommon"
xmlns:ssv="http://ssp-standard.org/SSP1/SystemStructureParameterValues"
version="1.0"
name="connections">
<System name="Root">
<Elements>
<Component name="Sinus 1" source="file:../../fmus/sinusGenerator.fmu">
<Connectors>
<Connector name="sinus_out" kind="output">
<ssc:Real/>
</Connector>
</Connectors>
</Component>

<Component name="Cosine 1" source="" type="application/avl-mcx-dcp">
<Connectors>
<Connector name="amplitude" kind="input">
<ssc:Real/>
</Connector>

<Connector name="cosine" kind="output">
<ssc:Real/>
</Connector>
</Connectors>

<Annotations>
<!-- custom annotation for defining OpenMCx specific component data -->
<ssc:Annotation type="com.avl.model.connect.ssp.component.dcp" xmlns:mc="com.avl.model.connect.ssp.component.dcp">
<mc:SpecificData
host="127.0.0.1"
numerator="10"
denominator="1000"
mode="NRT"
slaveDescription="../../install/dcp/cosine/slave.dcpx" />
</ssc:Annotation>
</Annotations>
</Component>
</Elements>

<Connections>
<!-- connection between two scalar ports -->
<Connection startElement="Sinus 1" startConnector="sinus_out" endElement="Cosine 1" endConnector="amplitude">
</Connection>

</Connections>
</System>

<DefaultExperiment stopTime="10.0"/>
</SystemStructureDescription>

0 comments on commit 1d5788b

Please sign in to comment.