/******************************************************************************** * Copyright (c) 2025-2026 ZF Friedrichshafen AG * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at * https://www.apache.org/licenses/LICENSE-2.0 * * SPDX-License-Identifier: Apache-2.0 * * Contributors: * Thomas Pfleiderer - initial API and implementation ********************************************************************************/ #include "fmu_templates.h" #include "fmu_fmi_templates.h" #include "fmu.h" #include #include #include #include CSoftcarFMUGen::CSoftcarFMUGen(const std::filesystem::path& rpathOutputDir, const dbc::CDbcParser& rparser, const std::string& rsModelIdentifier, const std::string& rsVersion, const std::vector& 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& 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(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(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 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( )code", mapKeywords); } std::string CSoftcarFMUGen::CodeFMIFile_VariableList(const std::vector& 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( %siggn_attributes% )code", mapKeywords); } std::string CSoftcarFMUGen::CodeConfigH_Rx_ValueReference(const dbc::SMessageDef& rsMsg, uint32_t& rIndex, std::vector& 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& 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& rvecSignalDefinitions) const { auto prSignalTypeDef = m_rparser.GetSignalTypeDef(rsSig.ssSignalTypeDef); const dbc::SSignalTypeBase& rsSigType = prSignalTypeDef.second ? static_cast(prSignalTypeDef.first) : static_cast(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& rvecSignalDefinitions) const { auto prSignalTypeDef = m_rparser.GetSignalTypeDef(rsSig.ssSignalTypeDef); const dbc::SSignalTypeBase& rsSigType = prSignalTypeDef.second ? static_cast(prSignalTypeDef.first) : static_cast(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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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("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(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(prSignalTypeDef.first) : static_cast(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(uValueHelper.iInt64Value)"; else mapKeywords["sig_helper_value"] = "static_cast(uValueHelper.s32.u32.iValue)"; } else { mapKeywords["sig_factor"] = to_string(static_cast(rsSigType.dFactor)); mapKeywords["sig_offset"] = to_string(static_cast(rsSigType.dOffset)); mapKeywords["sig_min"] = to_string(static_cast(rsSigType.dMinimum)); mapKeywords["sig_max"] = to_string(static_cast(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(uValueHelper.uiUint64Value)"; else mapKeywords["sig_helper_value"] = "static_cast(uValueHelper.s32.u32.uiValue)"; } else { if (rsSigType.dFactor < 0.0 || rsSigType.dOffset < 0.0) { mapKeywords["sig_factor"] = to_string(static_cast(rsSigType.dFactor)) + "ll"; mapKeywords["sig_offset"] = to_string(static_cast(rsSigType.dOffset)) + "ll"; mapKeywords["sig_min"] = to_string(static_cast(rsSigType.dMinimum)) + "ll"; mapKeywords["sig_max"] = to_string(static_cast(rsSigType.dMaximum)) + "ll"; if (rsSigType.uiSize > 32) mapKeywords["sig_helper_value"] = "static_cast(uValueHelper.uiUint64Value)"; else mapKeywords["sig_helper_value"] = "static_cast(uValueHelper.s32.u32.uiValue)"; } else { mapKeywords["sig_factor"] = to_string(static_cast(rsSigType.dFactor)) + "u"; mapKeywords["sig_offset"] = to_string(static_cast(rsSigType.dOffset)) + "u"; mapKeywords["sig_min"] = to_string(static_cast(std::max(rsSigType.dMinimum, 0.0))) + "u"; mapKeywords["sig_max"] = to_string(static_cast(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(rsSigType.dFactor)); mapKeywords["sig_offset"] = to_string(static_cast(rsSigType.dOffset)); mapKeywords["sig_min"] = to_string(static_cast(rsSigType.dMinimum)); mapKeywords["sig_max"] = to_string(static_cast(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(rsSig.dFactor) + static_cast(rsSig.dOffset)); else ssDefValue = to_string(static_cast(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(rsSig.dFactor) + static_cast(rsSig.dOffset)); else ssDefValue = to_string(static_cast(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& 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(std::toupper(static_cast(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); }