Files
tompzf 6ed4b1534e Precommit (#1)
* first commit

* cleanup
2025-11-04 13:28:06 +01:00

1206 lines
46 KiB
C++

#include "fmu_templates.h"
#include "fmu_fmi_templates.h"
#include "fmu.h"
#include <support/any.h>
#include <cmath>
#include <utility>
#include <thread>
CSoftcarFMUGen::CSoftcarFMUGen(const std::filesystem::path& rpathOutputDir, const dbc::CDbcParser& rparser,
const std::string& rsModelIdentifier, const std::string& rsVersion, const std::vector<std::string>& rvecNodes) :
m_rparser(rparser)
{
auto sModelIdentifier = CodeModelIdentifier(rsModelIdentifier);
auto fmuSubPath = "fmu_" + sModelIdentifier;
std::filesystem::path rootPath = (rpathOutputDir / fmuSubPath).lexically_normal();
CleanUpDirectory(rootPath);
auto pathCMakeFileListsFile = rootPath / "CMakeLists.txt";
CKeywordMap mapKeywords;
mapKeywords["cmakefilelists_path"] = pathCMakeFileListsFile.filename().generic_u8string();
UpdateKeywordMap(sModelIdentifier, rsVersion, rvecNodes, mapKeywords);
// create/write CMakeLists.txt only if content has changed
std::string cmakeExisitingContent = "";
auto cmakeNewContent = ReplaceKeywords(szMappingCMakeFileListsTemplate, mapKeywords);
if (std::filesystem::exists(pathCMakeFileListsFile))
{
std::ifstream stream;
stream.open(pathCMakeFileListsFile);
if (stream.is_open())
{
std::stringstream sstream;
sstream << stream.rdbuf();
cmakeExisitingContent = std::move(sstream.str());
}
stream.close();
}
if (cmakeNewContent.compare(cmakeExisitingContent) != 0)
{
std::ofstream fstreamCMakeFile;
fstreamCMakeFile.open(pathCMakeFileListsFile, std::ios::out | std::ios::trunc);
fstreamCMakeFile << cmakeNewContent;
fstreamCMakeFile.close();
}
CreateSourceFiles(rootPath);
CreateIncludeFiles(rootPath);
CreateFMUFiles(rootPath, sModelIdentifier, mapKeywords);
}
void CSoftcarFMUGen::UpdateKeywordMap(const std::string& rsModelIdentifier, const std::string& rsVersion,
const std::vector<std::string>& rvecNodes, CKeywordMap& mapKeywords) const
{
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream sstreamDate;
sstreamDate << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X");
mapKeywords["date"] = sstreamDate.str();
auto vecSources = m_rparser.GetSources();
std::stringstream sstreamDbcSources;
for (const dbc::CDbcSource& rsource : vecSources)
{
if (!sstreamDbcSources.str().empty())
sstreamDbcSources << ", ";
sstreamDbcSources << rsource.Path().filename().generic_u8string();
}
mapKeywords["dbc_sources"] = sstreamDbcSources.str();
mapKeywords["model_Identifier"] = rsModelIdentifier;
mapKeywords["model_guid"] = newUUID();
mapKeywords["buildDescription_xml"] = CodeBuildDescriptionXML(rsModelIdentifier);
mapKeywords["dbc_version"] = CodeDBCFileVersion(rsVersion);
// Safeguards start with "__IDL_GENERATED__", add the file name, add the date and time and end with "__"
std::stringstream sstreamSafeguardConfig;
sstreamSafeguardConfig << "__DBC_GENERATED__CONFIG_H__";
sstreamSafeguardConfig << std::put_time(std::localtime(&in_time_t), "%Y%m%d_%H%M%S") << "_";
sstreamSafeguardConfig << std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() % 1000;
sstreamSafeguardConfig << "__";
mapKeywords["safeguardconfig"] = sstreamSafeguardConfig.str();
// Safeguards start with "__IDL_GENERATED__", add the file name, add the date and time and end with "__"
std::stringstream sstreamSafeguardIdentifier;
sstreamSafeguardIdentifier << "__DBC_GENERATED__SIGNALIDENTIFIER_H__";
sstreamSafeguardIdentifier << std::put_time(std::localtime(&in_time_t), "%Y%m%d_%H%M%S") << "_";
sstreamSafeguardIdentifier << std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() % 1000;
sstreamSafeguardIdentifier << "__";
mapKeywords["safeguardsignalidentifier"] = sstreamSafeguardIdentifier.str();
std::stringstream sstreamValueReferenceList;
std::stringstream sstreamModelDataList;
std::stringstream sstreamMappingFile;
std::stringstream sstreamFMIDescriptionFile;
uint32_t indexFMI = 0;
uint32_t indexConfigH = 0;
std::vector<signalDefinition> vecSignalDefinitions;
auto vecMsgIDs = m_rparser.GetMessageIDs();
for (uint32_t uiRawMsgID : vecMsgIDs)
{
auto prMessage = m_rparser.GetMsgDef(uiRawMsgID);
if (!prMessage.second) continue;
// Run through the transmitter nodes and check whether the receiver of the message is defined in our node list.
bool bPartOfTransmitNode = false;
for (const std::string& rssTransmitter : prMessage.first.vecTransmitters)
{
if (rvecNodes.empty() || std::find(rvecNodes.begin(), rvecNodes.end(), rssTransmitter) != rvecNodes.end())
{
bPartOfTransmitNode = true;
break;
}
}
// Run through the signal definitions and check whether the receiver of the message is defined in our node list.
bool bPartOfReceiveNode = false;
for (const dbc::SSignalDef& rSignal : prMessage.first.vecSignals)
{
if (std::find_if(rSignal.vecReceivers.begin(), rSignal.vecReceivers.end(), [&](const std::string& rssRcvNode)
{
if (rvecNodes.empty()) return true;
return std::find(rvecNodes.begin(), rvecNodes.end(), rssRcvNode) != rvecNodes.end();
}) != rSignal.vecReceivers.end())
{
bPartOfReceiveNode = true;
break;
}
}
if (!bPartOfReceiveNode && !bPartOfTransmitNode) continue;
// Create message definitions
if (bPartOfReceiveNode)
{
sstreamValueReferenceList << CodeConfigH_Rx_ValueReference(prMessage.first, indexConfigH, vecSignalDefinitions);
}
if (bPartOfTransmitNode)
{
sstreamValueReferenceList << CodeConfigH_Tx_ValueReference(prMessage.first, indexConfigH, vecSignalDefinitions);
}
mapKeywords["value_reference"] = std::move(sstreamValueReferenceList.str());
}
mapKeywords["unknown_index"] = to_string(indexFMI);
sstreamModelDataList << CodeConfigH_ModelDataList(vecSignalDefinitions);
mapKeywords["model_data"] = std::move(sstreamModelDataList.str());
sstreamFMIDescriptionFile << CodeFMIFile_VariableList(vecSignalDefinitions);
mapKeywords["model_variables"] = std::move(sstreamFMIDescriptionFile.str());
sstreamMappingFile << CodeVariableList(vecSignalDefinitions);
mapKeywords["variable_list"] = std::move(sstreamMappingFile.str());
mapKeywords["global_signals"] = CodeModelCPP_GlobalDefinitionList(vecSignalDefinitions);
mapKeywords["global_signals_register"] = CodeModelCPP_GlobalRegisterList(vecSignalDefinitions);
mapKeywords["global_signals_register_check"] = CodeModelCPP_GlobalRegisterCheckList(vecSignalDefinitions);
mapKeywords["global_signals_reset"] = CodeModelCPP_GlobalResetList(vecSignalDefinitions);
mapKeywords["vapi_load_config_files"] = CodeModelCPP_OpenAPILoadFunction();
mapKeywords["getFloat64"] = CodeModelCPP_GetFloat64(vecSignalDefinitions);
mapKeywords["getInt32"] = CodeModelCPP_GetInt32(vecSignalDefinitions);
mapKeywords["setFloat64"] = CodeModelCPP_SetFloat64(vecSignalDefinitions);
mapKeywords["setInt32"] = CodeModelCPP_SetInt32(vecSignalDefinitions);
mapKeywords["event_update"] = CodeModelCPP_EventUpdateList(vecSignalDefinitions);
mapKeywords["signals"] = Code_AllSignalsIdentifierList(vecSignalDefinitions);
mapKeywords["object_prefix"] = rsModelIdentifier;
}
void CSoftcarFMUGen::CreateFMUFiles(const std::filesystem::path& rRootPath, const std::string& rsModelIdentifier, CKeywordMap& rmapKeywords) const
{
if (!CreateDirectories(rRootPath, rsModelIdentifier))
return;
auto fmuPath = rRootPath / rsModelIdentifier;
std::ofstream fstreamBuidDescription; ///< Build description XML file
std::ofstream fstreamConfigH; ///< Config.h file, used by the FMI code
std::ofstream fstreamFMI2XML; ///< fmi2.xml contains 'fmiModelDescription'
std::ofstream fstreamModelCPP; ///< model.cpp contains code to load and exchange signals to VAPI components
std::ofstream fstreamSignalIdentifierFile;
auto pathBuidDescription = fmuPath / "buildDescription.xml";
auto pathConfigH = fmuPath / "config.h";
auto pathFMI2XML = fmuPath / "FMI2.XML";
auto pathModelCPP = fmuPath / "model.cpp";
auto pathSignalIdentifierFile = fmuPath / "signal_identifier.h";
fstreamBuidDescription.open(pathBuidDescription, std::ios::out | std::ios::trunc);
fstreamConfigH.open(pathConfigH, std::ios::out | std::ios::trunc);
fstreamFMI2XML.open(pathFMI2XML, std::ios::out | std::ios::trunc);
fstreamModelCPP.open(pathModelCPP, std::ios::out | std::ios::trunc);
fstreamSignalIdentifierFile.open(pathSignalIdentifierFile, std::ios::out | std::ios::trunc);
rmapKeywords["buiddescription_path"] = pathBuidDescription.filename().generic_u8string();
rmapKeywords["configH_path"] = pathConfigH.filename().generic_u8string();
rmapKeywords["fmi2xml_path"] = pathFMI2XML.filename().generic_u8string();
rmapKeywords["modelcpp_path"] = pathModelCPP.filename().generic_u8string();
rmapKeywords["signalidentifierfile_path"] = pathSignalIdentifierFile.filename().generic_u8string();
fstreamBuidDescription << ReplaceKeywords(szBuildDescriptionTemplate, rmapKeywords);
fstreamBuidDescription.close();
fstreamConfigH << ReplaceKeywords(szConfigHTemplate, rmapKeywords);
fstreamConfigH.close();
fstreamFMI2XML << ReplaceKeywords(szFMI2XMLTemplate, rmapKeywords);
fstreamFMI2XML.close();
fstreamModelCPP << ReplaceKeywords(szModelCPPTemplate, rmapKeywords);
fstreamModelCPP.close();
fstreamSignalIdentifierFile << ReplaceKeywords(szMappingSignalIdentifierTemplate, rmapKeywords);
fstreamSignalIdentifierFile.close();
CreateResourcesFiles(fmuPath);
}
void CSoftcarFMUGen::CreateResourcesFiles(const std::filesystem::path& rRootPath) const
{
if (!CreateDirectories(rRootPath, "resources"))
return;
auto resourcesPath = rRootPath / "resources";
std::ofstream fstreamDataDispatchTomlFile;
std::ofstream fstreamSimulationTaskTimerTomlFile;
std::ofstream fstreamTaskTimerTomlFile;
auto pathDataDispatchTomlFile = resourcesPath / "data_dispatch_config_file.toml";
fstreamDataDispatchTomlFile.open(pathDataDispatchTomlFile, std::ios::out | std::ios::trunc);
fstreamDataDispatchTomlFile << szDataDispatchServiceTomlFile;
fstreamDataDispatchTomlFile.close();
auto pathSimulationTaskTimerFile = resourcesPath / "simulation_task_timer_config_file.toml";
fstreamSimulationTaskTimerTomlFile.open(pathSimulationTaskTimerFile, std::ios::out | std::ios::trunc);
fstreamSimulationTaskTimerTomlFile << szSimulationTaskTimerServiceTomlFile;
fstreamSimulationTaskTimerTomlFile.close();
auto pathTaskTimerTomlFile = resourcesPath / "task_timer_config_file.toml";
fstreamTaskTimerTomlFile.open(pathTaskTimerTomlFile, std::ios::out | std::ios::trunc);
fstreamTaskTimerTomlFile << szTaskTimerhServiceTomlFile;
fstreamTaskTimerTomlFile.close();
}
void CSoftcarFMUGen::CreateSourceFiles(const std::filesystem::path& rRootPath) const
{
if (!CreateDirectories(rRootPath, "src"))
return;
auto sourcePath = rRootPath / "src";
std::ofstream fstreamCosimulationSrcFileTemplate;
std::ofstream fstreamFmi2FunctionsSrcFileTemplate;
auto pathCosimulationSrcFileTemplate = sourcePath / "cosimulation.c";
auto pathFmi2FunctionsSrcFileTemplate = sourcePath / "fmi2Functions.c";
fstreamCosimulationSrcFileTemplate.open(pathCosimulationSrcFileTemplate, std::ios::out | std::ios::trunc);
fstreamFmi2FunctionsSrcFileTemplate.open(pathFmi2FunctionsSrcFileTemplate, std::ios::out | std::ios::trunc);
fstreamCosimulationSrcFileTemplate << szCosimulationSrcFileTemplate;
fstreamCosimulationSrcFileTemplate.close();
fstreamFmi2FunctionsSrcFileTemplate << szFmi2FunctionsSrcFileTemplate1;
fstreamFmi2FunctionsSrcFileTemplate << szFmi2FunctionsSrcFileTemplate2;
fstreamFmi2FunctionsSrcFileTemplate.close();
}
void CSoftcarFMUGen::CreateIncludeFiles(const std::filesystem::path& rRootPath) const
{
if(!CreateDirectories(rRootPath, "include"))
return;
auto includePath = rRootPath / "include";
std::ofstream fstreamCosimulationHeaderTemplate;
std::ofstream fstreamFMI2FunctionsHeaderTemplate;
std::ofstream fstreamFMI2FunctionTypesHeaderTemplate;
std::ofstream fstreamFMI2TypesPlatformHeaderTemplate;
std::ofstream fstreamModelHeaderTemplate;
auto pathCosimulationHeaderTemplate = includePath / "cosimulation.h";
auto pathFMI2FunctionsHeaderTemplate = includePath / "fmi2Functions.h";
auto pathFMI2FunctionTypesHeaderTemplate = includePath / "fmi2FunctionTypes.h";
auto pathFMI2TypesPlatformHeaderTemplate = includePath / "fmi2TypesPlatform.h";
auto pathModelHeaderTemplate = includePath / "model.h";
fstreamCosimulationHeaderTemplate.open(pathCosimulationHeaderTemplate, std::ios::out | std::ios::trunc);
fstreamFMI2FunctionsHeaderTemplate.open(pathFMI2FunctionsHeaderTemplate, std::ios::out | std::ios::trunc);
fstreamFMI2FunctionTypesHeaderTemplate.open(pathFMI2FunctionTypesHeaderTemplate, std::ios::out | std::ios::trunc);
fstreamFMI2TypesPlatformHeaderTemplate.open(pathFMI2TypesPlatformHeaderTemplate, std::ios::out | std::ios::trunc);
fstreamModelHeaderTemplate.open(pathModelHeaderTemplate, std::ios::out | std::ios::trunc);
fstreamCosimulationHeaderTemplate << szCosimulationHeaderTemplate;
fstreamCosimulationHeaderTemplate.close();
fstreamFMI2FunctionsHeaderTemplate << szFMI2FunctionsHeaderTemplate1;
fstreamFMI2FunctionsHeaderTemplate << szFMI2FunctionsHeaderTemplate2;
fstreamFMI2FunctionsHeaderTemplate.close();
fstreamFMI2FunctionTypesHeaderTemplate << szFMI2FunctionTypesHeaderTemplate;
fstreamFMI2FunctionTypesHeaderTemplate.close();
fstreamFMI2TypesPlatformHeaderTemplate << szFMI2TypesPlatformHeaderTemplate;
fstreamFMI2TypesPlatformHeaderTemplate.close();
fstreamModelHeaderTemplate << szModelHeaderTemplate;
fstreamModelHeaderTemplate.close();
}
std::string CSoftcarFMUGen::ReplaceKeywords(const std::string& rsStr, const CKeywordMap& rmapKeywords, char cMarker /*= '%'*/)
{
std::stringstream sstream;
size_t nPos = 0;
while (nPos < rsStr.size())
{
// Find the initial separator
size_t nSeparator = rsStr.find(cMarker, nPos);
sstream << rsStr.substr(nPos, nSeparator == std::string::npos ? nSeparator : nSeparator - nPos);
nPos = nSeparator;
if (nSeparator == std::string::npos) continue;
nPos++;
// Find the next separator.
nSeparator = rsStr.find(cMarker, nPos);
if (nSeparator == std::string::npos)
{
// Internal error: missing second separator during code building.
continue;
}
// Find the keyword in the keyword map (between the separator and the position).
CKeywordMap::const_iterator itKeyword = rmapKeywords.find(rsStr.substr(nPos, nSeparator - nPos));
if (itKeyword == rmapKeywords.end())
{
// Internal error: invalid keyword during building code.
nPos = nSeparator + 1;
continue;
}
sstream << itKeyword->second;
nPos = nSeparator + 1;
}
return sstream.str();
}
bool CSoftcarFMUGen::CreateDirectories(const std::filesystem::path& rpathRootDirectory, const std::filesystem::path& rpathSubDirectory) const
{
std::filesystem::path subFolder = (rpathRootDirectory / rpathSubDirectory).lexically_normal();
try
{
if (!std::filesystem::exists(subFolder))
{
std::filesystem::create_directories(subFolder);
}
}
catch (const std::filesystem::filesystem_error& e)
{
std::cout << "Filesystem error: " << e.what() << '\n';
}
catch (const std::exception& e)
{
std::cout << "Error: " << e.what() << '\n';
}
return std::filesystem::exists(subFolder);
}
bool CSoftcarFMUGen::CleanUpDirectory(const std::filesystem::path& rpathRootDirectory) const
{
try
{
if (!std::filesystem::exists(rpathRootDirectory))
{
std::filesystem::create_directories(rpathRootDirectory);
}
else
{
for (const auto& entry : std::filesystem::directory_iterator(rpathRootDirectory))
{
if (entry.path().filename() != "CMakeLists.txt")
{
if (std::filesystem::is_directory(entry.path()))
{
std::filesystem::remove_all(entry.path());
}
else
{
std::filesystem::remove(entry.path());
}
}
}
}
}
catch (const std::filesystem::filesystem_error& e)
{
std::cout << "Filesystem error: " << e.what() << '\n';
}
catch (const std::exception& e)
{
std::cout << "Error: " << e.what() << '\n';
}
return std::filesystem::exists(rpathRootDirectory);
}
std::string CSoftcarFMUGen::CodeDBCFileVersion(const std::string& rsVersion) const
{
if (rsVersion.empty())
{
return "";
}
CKeywordMap mapKeywords;
mapKeywords["dbc_version"] = rsVersion;
return ReplaceKeywords(R"code(DBC file version: %dbc_version%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelIdentifier(const std::string& rsModelIdentifier) const
{
if (rsModelIdentifier.empty())
{
return "BasicModelIdentifier";
}
return rsModelIdentifier;
}
std::string CSoftcarFMUGen::CodeBuildDescriptionXML(const std::string& rsModelIdentifier) const
{
CKeywordMap mapKeywords;
mapKeywords["model_Identifier"] = rsModelIdentifier;
return ReplaceKeywords(R"code(<?xml version="1.0" encoding="UTF-8"?>
<fmiBuildDescription fmiVersion="2.0">
<BuildConfiguration modelIdentifier="%model_Identifier%">
<SourceFileSet language="C++17">
<SourceFile name="fmi2Functions.c"/>
<SourceFile name="model.c"/>
<SourceFile name="cosimulation.c"/>
<PreprocessorDefinition name="FMI_VERSION" value="2"/>
</SourceFileSet>
</BuildConfiguration>
</fmiBuildDescription>
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeFMIFile_VariableList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamSignalDecl;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
sstreamSignalDecl << CodeFMIFile_SignalVariableList(rsSignal);
}
mapKeywords["sig_list"] = std::move(sstreamSignalDecl.str());
return ReplaceKeywords(R"code(%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeFMIFile_SignalVariableList(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name;
if (rSignal.isInput)
{
mapKeywords["sign_io"] = "input";
}
else
{
mapKeywords["sign_io"] = "output";
}
mapKeywords["sig_index"] = to_string(rSignal.index + 1);
mapKeywords["sig_reference"] = to_string(rSignal.index);
mapKeywords["siggn_attributes"] = rSignal.attributes;
return ReplaceKeywords(R"code( <!--Index for next variable = %sig_index% -->
<ScalarVariable name = "%sig_name%"
valueReference = "%sig_reference%"
description = " "
causality = "%sign_io%">
%siggn_attributes%
</ScalarVariable>
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeConfigH_Rx_ValueReference(const dbc::SMessageDef& rsMsg, uint32_t& rIndex,
std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamSignalDecl;
for (const dbc::SSignalDef& rsSignal : rsMsg.vecSignals)
{
sstreamSignalDecl << CodeConfigH_Rx_SignalForValueReference(rsSignal, rIndex, rsMsg.ssName, rvecSignalDefinitions);
rIndex++;
}
mapKeywords["sig_list"] = std::move(sstreamSignalDecl.str());
return ReplaceKeywords(R"code(%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeConfigH_Tx_ValueReference(const dbc::SMessageDef& rsMsg, uint32_t& rIndex,
std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamSignalDecl;
for (const dbc::SSignalDef& rsSignal : rsMsg.vecSignals)
{
sstreamSignalDecl << CodeConfigH_Tx_SignalForValueReference(rsSignal, rIndex, rsMsg.ssName, rvecSignalDefinitions);
rIndex++;
}
mapKeywords["sig_list"] = std::move(sstreamSignalDecl.str());
return ReplaceKeywords(R"code(%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeConfigH_Rx_SignalForValueReference(const dbc::SSignalDef& rsSig, const uint32_t index,
const std::string& rsMessageName, std::vector<signalDefinition>& rvecSignalDefinitions) const
{
auto prSignalTypeDef = m_rparser.GetSignalTypeDef(rsSig.ssSignalTypeDef);
const dbc::SSignalTypeBase& rsSigType = prSignalTypeDef.second ?
static_cast<const dbc::SSignalTypeBase&>(prSignalTypeDef.first) :
static_cast<const dbc::SSignalTypeBase&>(rsSig);
signalDefinition signal;
signal.index = index;
signal.message_name = rsMessageName;
signal.name = rsSig.ssName;
signal.isInput = true;
signal.uiSize = rsSigType.uiSize;
signal.isDouble = true;
signal.signalType = rsSigType.eValType;
if ((rsSigType.eValType == dbc::SSignalTypeBase::EValueType::signed_integer) || (rsSigType.eValType == dbc::SSignalTypeBase::EValueType::unsigned_integer))
{
if (rsSigType.dFactor == 1.0)
{
signal.isDouble = false;
}
}
signal.attributes = GetAttributes(rsSig, signal.isDouble);
rvecSignalDefinitions.push_back(signal);
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rsSig.ssName;
mapKeywords["sig_index"] = to_string(index);
return ReplaceKeywords(R"code(
vr_%sig_name% = %sig_index%,)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeConfigH_Tx_SignalForValueReference(const dbc::SSignalDef& rsSig, const uint32_t index,
const std::string& rsMessageName, std::vector<signalDefinition>& rvecSignalDefinitions) const
{
auto prSignalTypeDef = m_rparser.GetSignalTypeDef(rsSig.ssSignalTypeDef);
const dbc::SSignalTypeBase& rsSigType = prSignalTypeDef.second ?
static_cast<const dbc::SSignalTypeBase&>(prSignalTypeDef.first) :
static_cast<const dbc::SSignalTypeBase&>(rsSig);
signalDefinition signal;
signal.index = index;
signal.message_name = rsMessageName;
signal.name = rsSig.ssName;
signal.isInput = false;
signal.uiSize = rsSigType.uiSize;
signal.signalType = rsSigType.eValType;
signal.isDouble = true;
if ((rsSigType.eValType == dbc::SSignalTypeBase::EValueType::signed_integer) || (rsSigType.eValType == dbc::SSignalTypeBase::EValueType::unsigned_integer))
{
if (rsSigType.dFactor == 1.0)
{
signal.isDouble = false;
}
}
signal.attributes = GetAttributes(rsSig, signal.isDouble);
rvecSignalDefinitions.push_back(signal);
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rsSig.ssName;
mapKeywords["sig_index"] = to_string(index);
return ReplaceKeywords(R"code(
vr_%sig_name% = %sig_index%,)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeConfigH_ModelDataList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamSignalDecl;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
sstreamSignalDecl << CodeConfigH_SignalForModelData(rsSignal);
}
mapKeywords["sig_list"] = std::move(sstreamSignalDecl.str());
return ReplaceKeywords(R"code(%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeConfigH_SignalForModelData(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name;
if (rSignal.isDouble)
{
mapKeywords["sig_type"] = "double";
}
else
{
mapKeywords["sig_type"] = "int32_t";
}
return ReplaceKeywords(R"code(
%sig_type% %sig_name%;)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeVariableList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamSignalDecl;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
if (rsSignal.isInput)
{
sstreamSignalDecl << CodeVariableName(rsSignal);
}
}
sstreamSignalDecl << "\n";
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
if (!rsSignal.isInput)
{
sstreamSignalDecl << CodeVariableName(rsSignal);
}
}
mapKeywords["sig_list"] = std::move(sstreamSignalDecl.str());
return ReplaceKeywords(R"code(%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeVariableName(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name;
return ReplaceKeywords(R"code(%sig_name% =
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GlobalDefinitionList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
sstreamGlobalSignals << CodeModelCPP_GlobalSignalDefinition(rsSignal);
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
return ReplaceKeywords(R"code(%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GlobalSignalDefinition(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
return ReplaceKeywords(R"code(sdv::core::CSignal g_signal%sig_name%;
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GlobalRegisterList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
sstreamGlobalSignals << CodeModelCPP_GlobalRegisterSignal(rsSignal);
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
return ReplaceKeywords(R"code(%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GlobalRegisterCheckList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
uint32_t count = 0;
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
bool first = true;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
sstreamGlobalSignals << CodeModelCPP_GlobalRegisterSignalCheck(rsSignal, first);
first = false;
if(count++ > 4)
{
sstreamGlobalSignals << "\n ";
count = 0;
}
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
return ReplaceKeywords(R"code( if (%sig_list%)
{
return sdv::core::EConfigProcessResult::successful;
})code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GlobalRegisterSignal(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
mapKeywords["msg_name"] = rSignal.message_name.c_str();
if (rSignal.isInput)
{
mapKeywords["sig_type"] = "Rx";
mapKeywords["sig_default"] = "";
}
else
{
mapKeywords["sig_type"] = "Tx";
mapKeywords["sig_default"] = ",0";
}
return ReplaceKeywords(R"code( g_signal%sig_name% = dispatch.Register%sig_type%Signal("%msg_name%.%sig_name%"%sig_default%);
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GlobalRegisterSignalCheck(const signalDefinition& rSignal, bool first) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
mapKeywords["msg_name"] = rSignal.message_name.c_str();
if (first)
{
return ReplaceKeywords(R"code(g_signal%sig_name%)code", mapKeywords);
}
return ReplaceKeywords(R"code( && g_signal%sig_name%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GlobalResetList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
sstreamGlobalSignals << CodeModelCPP_GlobalResetSignal(rsSignal);
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
return ReplaceKeywords(R"code(%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GlobalResetSignal(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
return ReplaceKeywords(R"code( if (g_signal%sig_name%)
{
g_signal%sig_name%.Reset();
}
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_EventUpdateList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
if (rsSignal.isInput)
{
sstreamGlobalSignals << CodeModelCPP_EventUpdateSignalDefinitionWrite(rsSignal);
}
else
{
sstreamGlobalSignals << CodeModelCPP_EventUpdateSignalDefinitionRead(rsSignal);
}
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
return ReplaceKeywords(R"code(
if (g_pTimerSimulationStep) // in case the simulation timer was used, maybe the step size has to be adjusted
{
g_pTimerSimulationStep->SimulationStep(1000);
}
%sig_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_EventUpdateSignalDefinitionWrite(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
return ReplaceKeywords(R"code( g_signal%sig_name%.Write( M(%sig_name%));
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_EventUpdateSignalDefinitionRead(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
if (rSignal.isDouble)
{
mapKeywords["sig_type"] = "float";
}
else
{
mapKeywords["sig_type"] = "uint32_t";
}
return ReplaceKeywords(R"code( M(%sig_name%) = g_signal%sig_name%.Read().get<%sig_type%>();
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GetFloat64(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
bool empty = true;
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
if (rsSignal.isDouble)
{
empty = false;
sstreamGlobalSignals << CodeModelCPP_GetFloat64SignalDefinition(rsSignal);
}
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
if (empty)
{
return "";
}
return ReplaceKeywords(R"code(
switch (vr)
{
%sig_list%
default:
logError(comp, "Get Float64 is not allowed for value reference %u.", vr);
return Error;
}
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GetInt32(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
bool empty = true;
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
if (!rsSignal.isDouble)// && !rsSignal.isInput)
{
empty = false;
sstreamGlobalSignals << CodeModelCPP_GetInt32SignalDefinition(rsSignal);
}
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
if (empty)
{
return "";
}
return ReplaceKeywords(R"code(
switch (vr)
{
%sig_list%
default:
logError(comp, "Get Int32 is not allowed for value reference %u.", vr);
return Error;
}
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_SetFloat64(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
bool empty = true;
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
if (rsSignal.isDouble)
{
empty = false;
sstreamGlobalSignals << CodeModelCPP_SetFloat64SignalDefinition(rsSignal);
}
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
if (empty)
{
return "";
}
return ReplaceKeywords(R"code(
switch (vr)
{
%sig_list%
default:
logError(comp, "Set Float64 is not allowed for value reference %u.", vr);
return Error;
}
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_SetInt32(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
bool empty = true;
CKeywordMap mapKeywords;
std::stringstream sstreamGlobalSignals;
for (const signalDefinition& rsSignal : rvecSignalDefinitions)
{
if (!rsSignal.isDouble)
{
empty = false;
sstreamGlobalSignals << CodeModelCPP_SetInt32SignalDefinition(rsSignal);
}
}
mapKeywords["sig_list"] = std::move(sstreamGlobalSignals.str());
if (empty)
{
return "";
}
return ReplaceKeywords(R"code(
switch (vr)
{
%sig_list%
default:
logError(comp, "Set Int32 is not allowed for value reference %u.", vr);
return Error;
}
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GetFloat64SignalDefinition(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
return ReplaceKeywords(R"code( case vr_%sig_name%:
values[(*index)++] = M(%sig_name%);
break;
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_GetInt32SignalDefinition(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
return ReplaceKeywords(R"code( case vr_%sig_name%:
values[(*index)++] = M(%sig_name%);
break;
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_SetFloat64SignalDefinition(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
return ReplaceKeywords(R"code( case vr_%sig_name%:
M(%sig_name%) = values[(*index)++];
break;
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_SetInt32SignalDefinition(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
mapKeywords["sig_name"] = rSignal.name.c_str();
return ReplaceKeywords(R"code( case vr_%sig_name%:
M(%sig_name%) = values[(*index)++];
break;
)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeModelCPP_OpenAPILoadFunction() const
{
CKeywordMap mapKeywords;
return ReplaceKeywords(R"code(
//
//
// TODO: Load all configurations files
//
//
// Get the simulation task timer service if the simulation timer should be used
success &= g_appcontrol->LoadConfig("simulation_task_timer_config_file.toml") == sdv::core::EConfigProcessResult::successful;
g_pTimerSimulationStep = sdv::core::GetObject<sdv::core::ITimerSimulationStep>("SimulationTaskTimerService");
if (!g_pTimerSimulationStep)
{
SDV_LOG_WARNING("Simulation timer step not available, use normal task timer ");
success &= g_appcontrol->LoadConfig("task_timer_config_file.toml") == sdv::core::EConfigProcessResult::successful;
}
)code", mapKeywords);
}
std::string CSoftcarFMUGen::newUUID() const
{
#ifdef WIN32
UUID uuid{};
std::string s;
if (UuidCreate(&uuid) == RPC_S_OK)
{
unsigned char* str = nullptr;
if (UuidToStringA(&uuid, &str) == RPC_S_OK)
s = reinterpret_cast<char*>(str);
RpcStringFreeA(&str);
}
return s;
#else
return {};
#endif
}
std::string CSoftcarFMUGen::GetAttributes(const dbc::SSignalDef& rsSig, const bool& isDouble) const
{
auto prSignalTypeDef = m_rparser.GetSignalTypeDef(rsSig.ssSignalTypeDef);
const dbc::SSignalTypeBase& rsSigType = prSignalTypeDef.second ?
static_cast<const dbc::SSignalTypeBase&>(prSignalTypeDef.first) :
static_cast<const dbc::SSignalTypeBase&>(rsSig);
CKeywordMap mapKeywords;
mapKeywords["start_value"] = CodeGetDefaultValueOfTxSignal(rsSig);
if (isDouble)
{
mapKeywords["sig_type"] = "Real";
}
else
{
mapKeywords["sig_type"] = "Integer";
}
switch (rsSigType.eValType)
{
case dbc::SSignalDef::EValueType::signed_integer:
if (std::round(rsSigType.dFactor) != rsSigType.dFactor ||
std::round(rsSigType.dOffset) != rsSigType.dOffset)
{
mapKeywords["sig_factor"] = to_string(rsSigType.dFactor);
mapKeywords["sig_offset"] = to_string(rsSigType.dOffset);
mapKeywords["sig_min"] = to_string(rsSigType.dMinimum);
mapKeywords["sig_max"] = to_string(rsSigType.dMaximum);
if (rsSigType.uiSize > 32)
mapKeywords["sig_helper_value"] = "static_cast<double>(uValueHelper.iInt64Value)";
else
mapKeywords["sig_helper_value"] = "static_cast<double>(uValueHelper.s32.u32.iValue)";
}
else
{
mapKeywords["sig_factor"] = to_string(static_cast<int32_t>(rsSigType.dFactor));
mapKeywords["sig_offset"] = to_string(static_cast<int32_t>(rsSigType.dOffset));
mapKeywords["sig_min"] = to_string(static_cast<int32_t>(rsSigType.dMinimum));
mapKeywords["sig_max"] = to_string(static_cast<int32_t>(rsSigType.dMaximum));
if (rsSigType.uiSize > 32)
mapKeywords["sig_helper_value"] = "uValueHelper.iInt64Value";
else
mapKeywords["sig_helper_value"] = "uValueHelper.s32.u32.iValue";
}
break;
case dbc::SSignalDef::EValueType::unsigned_integer:
if (std::round(rsSigType.dFactor) != rsSigType.dFactor ||
std::round(rsSigType.dOffset) != rsSigType.dOffset)
{
mapKeywords["sig_factor"] = to_string(rsSigType.dFactor);
mapKeywords["sig_offset"] = to_string(rsSigType.dOffset);
mapKeywords["sig_min"] = to_string(rsSigType.dMinimum);
mapKeywords["sig_max"] = to_string(rsSigType.dMaximum);
if (rsSigType.uiSize > 32)
mapKeywords["sig_helper_value"] = "static_cast<double>(uValueHelper.uiUint64Value)";
else
mapKeywords["sig_helper_value"] = "static_cast<double>(uValueHelper.s32.u32.uiValue)";
}
else
{
if (rsSigType.dFactor < 0.0 || rsSigType.dOffset < 0.0)
{
mapKeywords["sig_factor"] = to_string(static_cast<int64_t>(rsSigType.dFactor)) + "ll";
mapKeywords["sig_offset"] = to_string(static_cast<int64_t>(rsSigType.dOffset)) + "ll";
mapKeywords["sig_min"] = to_string(static_cast<int64_t>(rsSigType.dMinimum)) + "ll";
mapKeywords["sig_max"] = to_string(static_cast<int64_t>(rsSigType.dMaximum)) + "ll";
if (rsSigType.uiSize > 32)
mapKeywords["sig_helper_value"] = "static_cast<int64_t>(uValueHelper.uiUint64Value)";
else
mapKeywords["sig_helper_value"] = "static_cast<int64_t>(uValueHelper.s32.u32.uiValue)";
}
else
{
mapKeywords["sig_factor"] = to_string(static_cast<uint32_t>(rsSigType.dFactor)) + "u";
mapKeywords["sig_offset"] = to_string(static_cast<uint32_t>(rsSigType.dOffset)) + "u";
mapKeywords["sig_min"] = to_string(static_cast<uint32_t>(std::max(rsSigType.dMinimum, 0.0))) + "u";
mapKeywords["sig_max"] = to_string(static_cast<uint32_t>(std::max(rsSigType.dMaximum, 0.0))) + "u";
if (rsSigType.uiSize > 32)
mapKeywords["sig_helper_value"] = "uValueHelper.uiUint64Value";
else
mapKeywords["sig_helper_value"] = "uValueHelper.s32.u32.uiValue";
}
}
break;
case dbc::SSignalDef::EValueType::ieee_float:
mapKeywords["sig_factor"] = to_string(static_cast<float>(rsSigType.dFactor));
mapKeywords["sig_offset"] = to_string(static_cast<float>(rsSigType.dOffset));
mapKeywords["sig_min"] = to_string(static_cast<float>(rsSigType.dMinimum));
mapKeywords["sig_max"] = to_string(static_cast<float>(rsSigType.dMaximum));
mapKeywords["sig_helper_value"] = "uValueHelper.s32.u32.fValue";
break;
case dbc::SSignalDef::EValueType::ieee_double:
mapKeywords["sig_factor"] = to_string(rsSigType.dFactor);
mapKeywords["sig_offset"] = to_string(rsSigType.dOffset);
mapKeywords["sig_min"] = to_string(rsSigType.dMinimum);
mapKeywords["sig_max"] = to_string(rsSigType.dMaximum);
mapKeywords["sig_helper_value"] = "uValueHelper.dValue";
break;
default:
return ReplaceKeywords(R"code(<%sig_type% />)code", mapKeywords);
break;
}
return ReplaceKeywords(R"code(<%sig_type% start = "%start_value%"
min = "%sig_min%"
max = "%sig_max%" />)code", mapKeywords);
}
std::string CSoftcarFMUGen::CodeGetDefaultValueOfTxSignal(const dbc::SSignalDef& rsSig) const
{
// Check for a start default value (attribute GeSigStartValue).
std::string ssDefValue;
std::for_each(rsSig.vecAttributes.begin(), rsSig.vecAttributes.end(), [&](const dbc::SAttributeValue& rsAttrValue)
{
if (!rsAttrValue.ptrAttrDef) return;
if (rsAttrValue.ptrAttrDef->ssName != "GenSigStartValue") return;
switch (rsAttrValue.ptrAttrDef->eType)
{
case dbc::SAttributeDef::EType::integer:
if (rsSig.dFactor == std::round(rsSig.dFactor) && rsSig.dOffset == std::round(rsSig.dOffset))
ssDefValue = to_string(rsAttrValue.iValue * static_cast<int32_t>(rsSig.dFactor) +
static_cast<int32_t>(rsSig.dOffset));
else
ssDefValue = to_string(static_cast<double>(rsAttrValue.iValue) * rsSig.dFactor + rsSig.dOffset);
break;
case dbc::SAttributeDef::EType::hex_integer:
if (rsSig.dFactor == std::round(rsSig.dFactor) && rsSig.dOffset == std::round(rsSig.dOffset))
ssDefValue = to_string(rsAttrValue.uiValue * static_cast<uint32_t>(rsSig.dFactor) +
static_cast<uint32_t>(rsSig.dOffset));
else
ssDefValue = to_string(static_cast<double>(rsAttrValue.uiValue) * rsSig.dFactor + rsSig.dOffset);
break;
case dbc::SAttributeDef::EType::floating_point:
ssDefValue = to_string(rsAttrValue.dValue * rsSig.dFactor + rsSig.dOffset);
break;
case dbc::SAttributeDef::EType::string:
ssDefValue = std::string("\"") + rsAttrValue.ssValue + "\"";
break;
case dbc::SAttributeDef::EType::enumerator:
for (size_t nIndex = 0; nIndex < rsAttrValue.ptrAttrDef->sEnumValues.vecEnumValues.size(); nIndex++)
{
if (rsAttrValue.ptrAttrDef->sEnumValues.vecEnumValues[nIndex] == rsAttrValue.ssValue)
{
ssDefValue = to_string(nIndex);
break;
}
if (rsAttrValue.ptrAttrDef->sEnumValues.vecEnumValues[nIndex] == rsAttrValue.ptrAttrDef->sEnumValues.ssDefault)
ssDefValue = to_string(nIndex);
}
break;
default:
break;
}
});
return ssDefValue.empty() ? "0" : ssDefValue;
}
std::string CSoftcarFMUGen::Code_AllSignalsIdentifierList(const std::vector<signalDefinition>& rvecSignalDefinitions) const
{
CKeywordMap mapKeywords;
std::stringstream sstreamSignals;
for (const auto& signal : rvecSignalDefinitions)
{
sstreamSignals << Code_SignalIdentifierList(signal);
}
mapKeywords["signals_list"] = std::move(sstreamSignals.str());
return ReplaceKeywords(R"code(%signals_list%)code", mapKeywords);
}
std::string CSoftcarFMUGen::Code_SignalIdentifierList(const signalDefinition& rSignal) const
{
CKeywordMap mapKeywords;
auto startWithUppercase = rSignal.name;
if (!startWithUppercase.empty())
{
startWithUppercase[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(startWithUppercase[0])));
}
mapKeywords["uppercase_signal_name"] = startWithUppercase;
mapKeywords["can_message_name"] = rSignal.message_name;
mapKeywords["can_signale_name"] = rSignal.name;
return ReplaceKeywords(R"code( static std::string ds%uppercase_signal_name% = "%can_message_name%.%can_signale_name%";
)code", mapKeywords);
}