/******************************************************************************** * 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: * Erik Verhoeven - initial API and implementation ********************************************************************************/ #include "can_dl.h" #include #include #include #include void CAN_Extract_Sample() { struct SSignal { uint32_t uiStartBit; uint32_t uiSize; }; // Motorola format: // The signal occupies the byte where the start bit determines the MSB and any number of following bytes that start with // the most significant bit. For example: // Signal 1: start=7 length = 4 = 0xA // Signal 2: start=3 length = 8 = 0x5D // Signal 3: start=11 length = 6 = 0x31 // Signal 4: start=21 length = 6 = 0x2D // Signal 5: start=31 length = 8 = 0x96 // Signal 6: start=39 length = 32 = 0x33CC55AA // 7 6 5 4 3 2 1 0 // 0 | Signal 1 | Signal 2 MSB | b10100101 0xA5 // 1 | signal 2 LSB | Signal 3 MSB | b11011100 0xDC // 2 | Sig 3 | Signal 4 | b01101101 0x6D // 3 | Signal 5 | b10010110 0x96 // 4 | Signal 6 MSB | b00110011 0x33 // 5 | | b11001100 0xCC // 6 | | b01010101 0x55 // 7 | Signal 6 LSB | b10101010 0xAA SSignal rgMotorolaSigs[] = { {7, 4}, {3, 8}, {11, 6}, {21, 6}, {31, 8}, {39, 32} }; uint8_t pMotorolaData[8] = { 0xA5, 0xDC, 0x6D, 0x96, 0x33, 0xCC, 0x55, 0xAA }; for (SSignal& rsSig : rgMotorolaSigs) { auto fnInverseBitPos = [](uint32_t uiPos) -> uint32_t { uint32_t uiInverseStartBit = (uiPos >> 3) << 3; uint32_t uiInverseStartBitInByte = (8 - ((uiPos + 1) & 7)) & 7; return uiInverseStartBit + uiInverseStartBitInByte; }; auto fnFirstByte = [&]() { return rsSig.uiStartBit >> 3; }; auto fnLastByte = [&]() { return ((fnInverseBitPos(rsSig.uiStartBit) + rsSig.uiSize - 1)) >> 3; }; auto fnFirstByteMask = [&]() { return (1 << ((rsSig.uiStartBit & 0x7) + 1)) - 1; }; auto fnShiftRight = [&]() { return (fnInverseBitPos(fnInverseBitPos(rsSig.uiStartBit) + rsSig.uiSize) + 1) & 0x7; }; auto fnValue = [&]() { uint64_t uiValue = 0; for (size_t nSrcIndex = fnFirstByte(); nSrcIndex <= fnLastByte(); nSrcIndex++) { uint8_t uiByte = pMotorolaData[nSrcIndex]; if (nSrcIndex == fnFirstByte()) uiByte &= fnFirstByteMask(); uiValue = uiValue << 8 | uiByte; if (nSrcIndex == fnLastByte()) uiValue >>= fnShiftRight(); } return uiValue; }; std::cout << "Start-bit=" << rsSig.uiStartBit << " Length=" << rsSig.uiSize << " First-byte=" << fnFirstByte() << " Last-byte=" << fnLastByte() << " First-byte-mask=" << fnFirstByteMask() << " Shift-right=" << fnShiftRight() << " Value=0x" << std::hex << fnValue() << std::dec << std::endl; } // Intel format: // The signal occupies the byte where the start bit determines the MSB and any number of following bytes that start with // the most significant bit. For example: // Signal 1: start=0 length = 4 = 0xA // Signal 2: start=4 length = 8 = 0x5D // Signal 3: start=12 length = 6 = 0x31 // Signal 4: start=18 length = 6 = 0x2D // Signal 5: start=24 length = 8 = 0x96 // Signal 6: start=32 length = 32 = 0x33CC55AA // 7 6 5 4 3 2 1 0 // 0 | Signal 2 LSB | Signal 1 | b11011010 0xDA // 1 | signal 3 LSB | Signal 2 MSB | b00010101 0x15 // 2 | Signal 4 | Sig 3 | b10110111 0xB7 // 3 | Signal 5 | b10010110 0x96 // 4 | Signal 6 MSB | b00110011 0xAA // 5 | | b11001100 0x55 // 6 | | b01010101 0xCC // 7 | Signal 6 LSB | b10101010 0x33 const SSignal rgIntelSigs[] = { {0, 4}, {4, 8}, {12, 6}, {18, 6}, {24, 8}, {32, 32} }; uint8_t pIntelData[8] = { 0xDA, 0x15, 0xB7, 0x96, 0xAA, 0x55, 0xCC, 0x33 }; for (const SSignal& rsSig : rgIntelSigs) { auto fnFirstByte = [&]() { return rsSig.uiStartBit >> 3; }; auto fnLastByte = [&]() { return (rsSig.uiStartBit + rsSig.uiSize - 1) >> 3; }; auto fnLastByteMask = [&]() { return (1 << (((rsSig.uiStartBit + rsSig.uiSize - 1) & 0x7) + 1)) - 1; }; auto fnShiftRight = [&]() { return rsSig.uiStartBit & 0x7; }; auto fnValue = [&]() { uint64_t uiValue = 0; for (size_t nSrcIndex = fnLastByte(); nSrcIndex >= fnFirstByte() && nSrcIndex <= fnLastByte(); nSrcIndex--) { uint8_t uiByte = pIntelData[nSrcIndex]; if (nSrcIndex == fnLastByte()) uiByte &= fnLastByteMask(); uiValue = uiValue << 8 | uiByte; if (nSrcIndex == fnFirstByte()) uiValue >>= fnShiftRight(); } return uiValue; }; std::cout << "Start-bit=" << rsSig.uiStartBit << " Length=" << rsSig.uiSize << " First-byte=" << fnFirstByte() << " Last-byte=" << fnLastByte() << " Last-byte-mask=" << fnLastByteMask() << " Shift-right=" << fnShiftRight() << " Value=0x" << std::hex << fnValue() << std::dec << std::endl; } } /** * @brief Header file template. Code chunks are inserted at the keywords surrounded by %%. */ const char szHdrTemplate[] = R"code(/** * @file %hdr_path% * @date %date% * This file defines the data link object between CAN and the V-API devices. * This file was generated by the DBC utility from: * %dbc_sources% * %dbc_version% */ #ifndef %safeguard% #define %safeguard% #include #include #include #include /** * @brief Data link class. */ class CDataLink : public sdv::CSdvObject, public sdv::can::IReceive { public: /** * @brief Constructor */ CDataLink(); /** * @brief Destructor */ ~CDataLink(); // Interface map BEGIN_SDV_INTERFACE_MAP() SDV_INTERFACE_ENTRY(sdv::can::IReceive) END_SDV_INTERFACE_MAP() // Declarations DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus) DECLARE_OBJECT_CLASS_NAME("CAN_data_link") DECLARE_DEFAULT_OBJECT_NAME("DataLink") DECLARE_OBJECT_SINGLETON() /** * @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize. * @return Returns 'true' when the initialization was successful, 'false' when not. */ virtual bool OnInitialize() override; /** * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ virtual void OnShutdown() override; /** * @brief Process a receive a CAN message. Overload of sdv::can::IReceive::Receive. * @param[in] sMsg Message that was received. * @param[in] uiIfcIndex Interface index of the received message. */ virtual void Receive(/*in*/ const sdv::can::SMessage& sMsg, /*in*/ uint32_t uiIfcIndex) override; /** * @brief Process an error frame. Overload of sdv::can::IReceive::Error. * @param[in] sError Error frame that was received. * @param[in] uiIfcIndex Interface index of the received message. */ virtual void Error(/*in*/ const sdv::can::SErrorFrame& sError, /*in*/ uint32_t uiIfcIndex) override; private: /** * @brief Union containing all the compound values needed to convert between the DBC defined types. */ union UValueHelper { uint64_t uiUint64Value; ///< The 64-bit unsingned value. int64_t iInt64Value; ///< The 64-bit signed value. /** * @brief The structure mapping the 32-bit value types into the 64-bit. */ struct S32 { #if (!defined(_MSC_VER) || defined(__clang__)) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) uint32_t uiPadding; ///< Padding for big endian byte ordering (MSB) #endif /** * @brief The 32-bit union containing the possible 32-bit values. */ union U32Value { int32_t iValue; ///< 32-bit signed integer. uint32_t uiValue; ///< 32-bit unsigned integer. float fValue; ///< 32-bit floating point number. } u32; ///< 32-bit union instance. #if (defined(_MSC_VER) && !defined(__clang__)) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) uint32_t uiPadding; ///< Padding for little endian byte ordering (MSB) #endif } s32; double dValue; ///< 64-bit double precision floating point number. }; %message_def% size_t m_nIfcIndex = %ifc_index%; ///< CAN Interface index. sdv::can::IRegisterReceiver* m_pRegister = nullptr; ///< CAN receiver registration interface. sdv::can::ISend* m_pSend = nullptr; ///< CAN sender interface. sdv::core::CDispatchService m_dispatch; ///< Dispatch service }; DEFINE_SDV_OBJECT(CDataLink) #endif // !defined %safeguard% )code"; /** * @brief Cpp file template. Code chunks are inserted at the keywords surrounded by %%. */ const char szCppTemplate[] = R"code(/** * @file %cpp_path% * @date %date% * This file implements the data link object between CAN and the V-API devices. * This file was generated by the DBC utility from: * %dbc_sources% * %dbc_version% */ #include "%hdr_path%" #include #include #ifdef _MSC_VER #ifdef min #undef min #endif #ifdef max #undef max #endif #endif CDataLink::CDataLink()%init_var% {} CDataLink::~CDataLink() { Shutdown(); // Just in case } bool CDataLink::OnInitialize() { // Get the CAN communication object. sdv::TInterfaceAccessPtr ptrCANObject = sdv::core::GetObject("CAN_Communication_Object"); if (!ptrCANObject) { SDV_LOG_ERROR("CDataLink::Initialize() failure, 'CAN_Communication_Object' not found"); return false; } %init_ifc_index%// Get the CAN receiver registration interface. m_pRegister = ptrCANObject.GetInterface(); if (!m_pRegister) { SDV_LOG_ERROR("CDataLink::Initialize() failure, 'sdv::can::IRegisterReceiver' interface not found"); return false; } m_pRegister->RegisterReceiver(static_cast(this)); // Get the CAN transmit interface m_pSend = ptrCANObject.GetInterface(); if (!m_pSend) { SDV_LOG_ERROR("CDataLink::Initialize() failure, 'sdv::can::ISend' interface not found"); return false; } // Initialize messages bool bSuccess = true;%init_msg% return bSuccess; } void CDataLink::OnShutdown() { // Unregister receiver interface. if (m_pRegister) m_pRegister->UnregisterReceiver(static_cast(this)); m_pRegister = nullptr; m_pSend = nullptr; // Terminate messages%term_msg% } void CDataLink::Receive(/*in*/ [[maybe_unused]] const sdv::can::SMessage& sMsg, /*in*/ uint32_t uiIfcIndex) { if (static_cast(uiIfcIndex) != m_nIfcIndex) return; %receive_switch_begin%%receive_switch%%receive_switch_end% } void CDataLink::Error(/*in*/ [[maybe_unused]] const sdv::can::SErrorFrame& sError, /*in*/ uint32_t uiIfcIndex) { if (static_cast(uiIfcIndex) != m_nIfcIndex) return; // TODO: Currently no error frame handling... } %msg_impl% )code"; CCanDataLinkGen::CCanDataLinkGen(const std::filesystem::path& rpathOutputDir, const dbc::CDbcParser& rparser, const std::string& rsVersion, const std::string& rssIfcName, size_t nIfcIdx, const std::vector& rvecNodes) : m_rparser(rparser) { // Create project directory if (!rpathOutputDir.empty()) m_pathProject = rpathOutputDir; m_pathProject /= "can_dl"; for (size_t nCnt = 0; nCnt < 5; nCnt++) { if (!std::filesystem::exists(m_pathProject)) std::filesystem::create_directories(m_pathProject); else break; std::this_thread::sleep_for(std::chrono::milliseconds(250)); } // Open the streams m_pathHeader = m_pathProject / "datalink.h"; m_pathCpp = m_pathProject / "datalink.cpp"; m_fstreamHeader.open(m_pathHeader, std::ios::out | std::ios::trunc); m_fstreamCpp.open(m_pathCpp, std::ios::out | std::ios::trunc); CKeywordMap mapKeywords; mapKeywords["hdr_path"] = m_pathHeader.filename().generic_u8string(); mapKeywords["cpp_path"] = m_pathCpp.filename().generic_u8string(); 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 = 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["dbc_version"] = CodeDBCFileVersion(rsVersion); // Safeguard // Safeguards start with "__IDL_GENERATED__", add the file name, add the date and time and end with "__" std::stringstream sstreamSafeguard; sstreamSafeguard << "__DBC_GENERATED__DATALINK_H__"; sstreamSafeguard << std::put_time(std::localtime(&in_time_t), "%Y%m%d_%H%M%S") << "_"; sstreamSafeguard << std::chrono::duration_cast(now.time_since_epoch()).count() % 1000; sstreamSafeguard << "__"; mapKeywords["safeguard"] = sstreamSafeguard.str(); // Interface index mapKeywords["ifc_index"] = to_string(nIfcIdx); mapKeywords["init_ifc_index"] = CodeInitInterfaceIndex(rssIfcName); // Generate message structures, signal definitions, message switch std::stringstream sstreamReceiveSwitchBegin; std::stringstream sstreamReceiveSwitch; std::stringstream sstreamReceiveSwitchEnd; std::stringstream sstreamMessageDef; std::stringstream sstreamInitMsg; std::stringstream sstreamTermMsg; std::stringstream sstreamMsgImpl; std::stringstream sstreamInitVarImpl; auto vecMsgIDs = rparser.GetMessageIDs(); for (uint32_t uiRawMsgID : vecMsgIDs) { auto prMessage = 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; if (sstreamInitVarImpl.str().empty()) sstreamInitVarImpl << R"code( : )code"; else sstreamInitVarImpl << R"code(, )code"; // Create message definitions if (bPartOfReceiveNode) { sstreamMessageDef << CodeRxMessageDefinition(prMessage.first); sstreamInitMsg << CodeInitRxMessage(prMessage.first); sstreamTermMsg << CodeTermRxMessage(prMessage.first); sstreamMsgImpl << CodeRxMessageFunctions(prMessage.first); sstreamInitVarImpl << CodeInitVarRxMessage(prMessage.first); sstreamReceiveSwitch << R"code( case )code" << rparser.ExtractMsgId(uiRawMsgID).first << R"code(: // )code" << prMessage.first.ssName << R"code( m_sRxMsg)code" << prMessage.first.ssName << R"code(.Process(sMsg.seqData); break;)code"; } if (bPartOfTransmitNode) { if (bPartOfReceiveNode) sstreamInitVarImpl << R"code(, )code"; sstreamMessageDef << CodeTxMessageDefinition(prMessage.first); sstreamInitMsg << CodeInitTxMessage(prMessage.first); sstreamTermMsg << CodeTermTxMessage(prMessage.first); sstreamMsgImpl << CodeTxMessageFunctions(prMessage.first); sstreamInitVarImpl << CodeInitVarTxMessage(prMessage.first); } } if (!sstreamReceiveSwitch.str().empty()) { sstreamReceiveSwitchBegin << R"code( switch (sMsg.uiID) {)code"; sstreamReceiveSwitchEnd << R"code( default: break; })code"; } mapKeywords["message_def"] = sstreamMessageDef.str(); mapKeywords["receive_switch_begin"] = sstreamReceiveSwitchBegin.str(); mapKeywords["receive_switch"] = sstreamReceiveSwitch.str(); mapKeywords["receive_switch_end"] = sstreamReceiveSwitchEnd.str(); mapKeywords["msg_impl"] = sstreamMsgImpl.str(); mapKeywords["init_msg"] = sstreamInitMsg.str(); mapKeywords["term_msg"] = sstreamTermMsg.str(); mapKeywords["init_var"] = sstreamInitVarImpl.str(); // Replace keywords and stream the code files. m_fstreamHeader << ReplaceKeywords(szHdrTemplate, mapKeywords); m_fstreamCpp << ReplaceKeywords(szCppTemplate, mapKeywords); // Generate DBC-signal registration // Add CAN input processing function // Add CAN output timer triggers } std::string CCanDataLinkGen::ReplaceKeywords(const std::string& rssStr, const CKeywordMap& rmapKeywords, char cMarker /*= '%'*/) { std::stringstream sstream; size_t nPos = 0; while (nPos < rssStr.size()) { // Find the initial separator size_t nSeparator = rssStr.find(cMarker, nPos); sstream << rssStr.substr(nPos, nSeparator == std::string::npos ? nSeparator : nSeparator - nPos); nPos = nSeparator; if (nSeparator == std::string::npos) continue; nPos++; // Find the next separator. nSeparator = rssStr.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(rssStr.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(); } std::string CCanDataLinkGen::CodeInitInterfaceIndex(const std::string& rssIfcName) { if (rssIfcName.empty()) return std::string(); CKeywordMap mapKeywords; mapKeywords["ifc_name"] = rssIfcName; return ReplaceKeywords(R"code(sdv::can::IInformation* pInfo = ptrCANObject.GetInterface(); if (!pInfo) { // CAN information interface not found. return false; } sdv::sequence seqInterfaces = pInfo->GetInterfaces(); size_t nIndex = 0; for (; nIndex < seqInterfaces.size(); nIndex++) { if (seqInterfaces[nIndex] == "%ifc_name%") { m_nIfcIndex = nIndex; break; } } if (nIndex >= seqInterfaces.size()) { // Interface with supplied name not found. return false; } return true; )code", mapKeywords); } std::string CCanDataLinkGen::CodeRxMessageDefinition(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; mapKeywords["msg_id"] = to_string(dbc::CDbcParser::ExtractMsgId(rsMsg.uiId).first); std::stringstream sstreamSignalDecl; for (const dbc::SSignalDef& rsSignal : rsMsg.vecSignals) sstreamSignalDecl << CodeSignalDecl(rsSignal); mapKeywords["sig_decl"] = std::move(sstreamSignalDecl.str()); return ReplaceKeywords(R"code( /** * @brief RX CAN message definition of: %msg_name% [id=%msg_id%] */ struct SRxMsg_%msg_name% { /** * @brief Constructor * @param[in] rdispatch Reference to the dispatch service. */ SRxMsg_%msg_name%(sdv::core::CDispatchService& rdispatch); /** * @brief Initialize the message by registering all signals. */ bool Init(); /** * @brief Terminate the message by unregistering all signals. */ void Term(); /** * @brief Process received data. * @param[in] rseqData Reference to the message data to process. */ void Process(const sdv::sequence& rseqData); sdv::core::CDispatchService& m_rdispatch; ///< Reference to the dispatch service. %sig_decl% } m_sRxMsg%msg_name%; )code", mapKeywords); } std::string CCanDataLinkGen::CodeTxMessageDefinition(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; mapKeywords["msg_id"] = to_string(dbc::CDbcParser::ExtractMsgId(rsMsg.uiId).first); std::stringstream sstreamSignalDecl; for (const dbc::SSignalDef& rsSignal : rsMsg.vecSignals) sstreamSignalDecl << CodeSignalDecl(rsSignal); mapKeywords["sig_decl"] = std::move(sstreamSignalDecl.str()); return ReplaceKeywords(R"code( /** * @brief TX CAN message definition of: %msg_name% [id=%msg_id%] */ struct STxMsg_%msg_name% { /** * @brief Constructor * @param[in] rdispatch Reference to the dispatch service. */ STxMsg_%msg_name%(sdv::core::CDispatchService& rdispatch); /** * @brief Initialize the message by registering all signals. * @param[in] pSend The send-interface of the CAN. * @param[in] nIfcIndex CAN Interface index. */ bool Init(sdv::can::ISend* pSend, size_t nIfcIndex); /** * @brief Terminate the message by unregistering all signals. */ void Term(); /** * @brief Transmit data. * @param[in] pCanSend Pointer to the CAN send interface. */ void Transmit(); sdv::core::CDispatchService& m_rdispatch; ///< Reference to the dispatch service. sdv::core::CTrigger m_trigger; ///< Message trigger being called by the dispatch service. sdv::can::ISend* m_pSend = nullptr; ///< Message sending interface. size_t m_nIfcIndex = 0; ///< CAN Interface index. %sig_decl% } m_sTxMsg%msg_name%; )code", mapKeywords); } std::string CCanDataLinkGen::CodeSignalDecl(const dbc::SSignalDef& rsSig) { CKeywordMap mapKeywords; mapKeywords["sig_name"] = rsSig.ssName; mapKeywords["sig_unit"] = rsSig.ssUnit; return ReplaceKeywords(R"code( /// Signal %sig_name% with unit %sig_unit% sdv::core::CSignal m_sig%sig_name%;)code", mapKeywords); } std::string CCanDataLinkGen::CodeInitVarRxMessage(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; return ReplaceKeywords(R"code(m_sRxMsg%msg_name%(m_dispatch))code", mapKeywords); } std::string CCanDataLinkGen::CodeInitVarTxMessage(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; return ReplaceKeywords(R"code(m_sTxMsg%msg_name%(m_dispatch))code", mapKeywords); } std::string CCanDataLinkGen::CodeInitRxMessage(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; return ReplaceKeywords(R"code( bSuccess &= m_sRxMsg%msg_name%.Init();)code", mapKeywords); } std::string CCanDataLinkGen::CodeInitTxMessage(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; return ReplaceKeywords(R"code( bSuccess &= m_sTxMsg%msg_name%.Init(m_pSend, m_nIfcIndex);)code", mapKeywords); } std::string CCanDataLinkGen::CodeTermRxMessage(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; return ReplaceKeywords(R"code( m_sRxMsg%msg_name%.Term();)code", mapKeywords); } std::string CCanDataLinkGen::CodeTermTxMessage(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; return ReplaceKeywords(R"code( m_sTxMsg%msg_name%.Term();)code", mapKeywords); } std::string CCanDataLinkGen::CodeRxMessageFunctions(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; std::stringstream sstreamSignalRegister; std::stringstream sstreamSignalUnregister; std::stringstream sstreamSignalProcessing; for (const dbc::SSignalDef& rsSignal : rsMsg.vecSignals) { sstreamSignalRegister << CodeRegisterRxSignal(rsMsg, rsSignal); sstreamSignalUnregister << CodeUnregisterSignal(rsMsg, rsSignal); sstreamSignalProcessing << CodeProcessRxSignal(rsMsg, rsSignal); } mapKeywords["sig_register"] = std::move(sstreamSignalRegister.str()); mapKeywords["sig_unregister"] = std::move(sstreamSignalUnregister.str()); mapKeywords["msg_len"] = to_string(rsMsg.uiSize); mapKeywords["process_signals"] = std::move(sstreamSignalProcessing.str()); return ReplaceKeywords(R"code( CDataLink::SRxMsg_%msg_name%::SRxMsg_%msg_name%(sdv::core::CDispatchService& rdispatch) : m_rdispatch(rdispatch) {} bool CDataLink::SRxMsg_%msg_name%::Init() { // Register signals [[maybe_unused]] bool bSuccess = true;%sig_register% return bSuccess; } void CDataLink::SRxMsg_%msg_name%::Term() { // Unregister signals%sig_unregister% } void CDataLink::SRxMsg_%msg_name%::Process(const sdv::sequence& rseqData) { // Check for the correct size. if (rseqData.size() != %msg_len%) { // TODO: Error. Delivered data has different size as compared to the specification. return; } // Helper variable [[maybe_unused]] UValueHelper uValueHelper; // Start a transaction sdv::core::CTransaction transaction = m_rdispatch.CreateTransaction();%process_signals% // Finalize the transaction transaction.Finish(); } )code", mapKeywords); } std::string CCanDataLinkGen::CodeTxMessageFunctions(const dbc::SMessageDef& rsMsg) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; auto prCanId = dbc::CDbcParser::ExtractMsgId(rsMsg.uiId); mapKeywords["msg_id"] = to_string(prCanId.first); mapKeywords["msg_id_ext"] = prCanId.second ? "true" : "false"; mapKeywords["msg_len"] = to_string(rsMsg.uiSize); std::stringstream sstreamSignalRegister; std::stringstream sstreamSignalUnregister; std::stringstream sstreamSignalComposition; std::stringstream sstreamTriggerInit; for (const dbc::SSignalDef& rsSignal : rsMsg.vecSignals) { sstreamSignalRegister << CodeRegisterTxSignal(rsMsg, rsSignal); sstreamSignalUnregister << CodeUnregisterSignal(rsMsg, rsSignal); sstreamSignalComposition << CodeComposeTxSignal(rsMsg, rsSignal); sstreamTriggerInit << InitTrigger(rsMsg, rsSignal); } mapKeywords["sig_register"] = std::move(sstreamSignalRegister.str()); mapKeywords["sig_unregister"] = std::move(sstreamSignalUnregister.str()); mapKeywords["compose_signals"] = std::move(sstreamSignalComposition.str()); // DBC files are quite messy keeping track of capital letters. Convert all strings to lower-case. auto fnLower = [](const std::string& rss) { std::string ssRet = rss; for (char& rc : ssRet) rc = static_cast(std::tolower(rc)); return ssRet; }; // Determine the trigger attributes bool bCyclic = false; uint32_t uiCycleTime = 100; bool bSpontaneous = false; uint32_t uiDelayTime = 0; bool bOnlyIfActive = false; for (const dbc::SAttributeValue& rsAttribute : rsMsg.vecAttributes) { if (!rsAttribute.ptrAttrDef) continue; // No interpretation possible. if (rsAttribute.ptrAttrDef->ssName == "GenMsgDelayTime") { if (rsAttribute.ptrAttrDef->eType == dbc::SAttributeDef::EType::integer) uiDelayTime = static_cast(rsAttribute.iValue); if (rsAttribute.ptrAttrDef->eType == dbc::SAttributeDef::EType::hex_integer) uiDelayTime = rsAttribute.uiValue; if (rsAttribute.ptrAttrDef->eType == dbc::SAttributeDef::EType::floating_point) uiDelayTime = static_cast(rsAttribute.dValue); } if (rsAttribute.ptrAttrDef->ssName == "GenMsgCycleTime") { if (rsAttribute.ptrAttrDef->eType == dbc::SAttributeDef::EType::integer) uiCycleTime = static_cast(rsAttribute.iValue); if (rsAttribute.ptrAttrDef->eType == dbc::SAttributeDef::EType::hex_integer) uiCycleTime = rsAttribute.uiValue; if (rsAttribute.ptrAttrDef->eType == dbc::SAttributeDef::EType::floating_point) uiCycleTime = static_cast(rsAttribute.dValue); } if (rsAttribute.ptrAttrDef->ssName == "GenMsgSendType") { size_t nValue = 1; // Representing 'spontaneous'. switch (rsAttribute.ptrAttrDef->eType) { case dbc::SAttributeDef::EType::string: if (fnLower(rsAttribute.ssValue) == "cyclic" || fnLower(rsAttribute.ssValue) == "zyklisch") nValue = 0; else if (fnLower(rsAttribute.ssValue) == "triggered" || fnLower(rsAttribute.ssValue) == "ereignisgesteuert") nValue = 1; else if (fnLower(rsAttribute.ssValue) == "cyclicifactive") nValue = 2; else if (fnLower(rsAttribute.ssValue) == "cyclicandtriggered" || fnLower(rsAttribute.ssValue) == "zyklischimmer") nValue = 3; else if (fnLower(rsAttribute.ssValue) == "cyclicifactiveandtriggered") nValue = 4; else nValue = 5; // None break; case dbc::SAttributeDef::EType::enumerator: for (size_t nIndex = 0; nIndex < rsAttribute.ptrAttrDef->sEnumValues.vecEnumValues.size(); nIndex++) if (fnLower(rsAttribute.ssValue) == fnLower(rsAttribute.ptrAttrDef->sEnumValues.vecEnumValues[nIndex])) nValue = nIndex; break; case dbc::SAttributeDef::EType::integer: nValue = static_cast(rsAttribute.iValue); break; case dbc::SAttributeDef::EType::hex_integer: nValue = static_cast(rsAttribute.uiValue); break; default: break; } // Deal with the value switch (nValue) { case 0: // cyclic bCyclic = true; break; case 2: // cyclicIfActive bCyclic = true; bOnlyIfActive = true; break; case 3: // cyclicAndTriggered bCyclic = true; bSpontaneous = true; break; case 4: // cyclicIfActiveAndTriggered bCyclic = true; bSpontaneous = true; bOnlyIfActive = true; break; case 1: // triggered default: // all others bSpontaneous = true; break; } } } // Prevent a situation that the object will never be transmitted. if (!bCyclic && !bSpontaneous) bSpontaneous = true; // CreateTxTrigger definition std::stringstream sstreamCreateTrigger; sstreamCreateTrigger << "CreateTxTrigger([&] { Transmit(); }, " << (bSpontaneous ? "true, " : "false, ") << uiDelayTime << ", " << ((bCyclic && uiCycleTime) ? uiCycleTime : 0ul) << ", " << ((bCyclic && bOnlyIfActive) ? "true" : "false") << ")"; mapKeywords["create_trigger"] = std::move(sstreamCreateTrigger.str()); mapKeywords["init_trigger"] = std::move(sstreamTriggerInit.str()); return ReplaceKeywords(R"code( CDataLink::STxMsg_%msg_name%::STxMsg_%msg_name%(sdv::core::CDispatchService& rdispatch) : m_rdispatch(rdispatch) {} bool CDataLink::STxMsg_%msg_name%::Init(sdv::can::ISend* pSend, size_t nIfcIndex) { if (!pSend) { return false; } m_pSend = pSend; m_nIfcIndex = nIfcIndex; // Register signals bool bSuccess = true;%sig_register% // Initialize the trigger m_trigger = m_rdispatch.%create_trigger%;%init_trigger% bSuccess &= m_trigger; return bSuccess; } void CDataLink::STxMsg_%msg_name%::Term() { // Reset the trigger m_trigger.Reset(); // Unregister signals%sig_unregister% } void CDataLink::STxMsg_%msg_name%::Transmit() { if (!m_pSend) return; // Compose CAN message sdv::can::SMessage msg; msg.uiID = %msg_id%; msg.bExtended = %msg_id_ext%; msg.bCanFd = false; // TODO: Currently not supported msg.seqData.resize(%msg_len%); std::fill(msg.seqData.begin(), msg.seqData.end(), static_cast(0)); // Helper variable [[maybe_unused]] UValueHelper uValueHelper; // Start a transaction sdv::core::CTransaction transaction = m_rdispatch.CreateTransaction();%compose_signals% // Finalize the transaction transaction.Finish(); // Transmit the message m_pSend->Send(msg, static_cast(m_nIfcIndex)); } )code", mapKeywords); } std::string CCanDataLinkGen::CodeRegisterRxSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; mapKeywords["sig_name"] = rsSig.ssName; return ReplaceKeywords(R"code( m_sig%sig_name% = m_rdispatch.RegisterRxSignal("%msg_name%.%sig_name%"); bSuccess &= m_sig%sig_name% ? true : false;)code", mapKeywords); } std::string CCanDataLinkGen::CodeRegisterTxSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; mapKeywords["sig_name"] = rsSig.ssName; // 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; } }); mapKeywords["def_value"] = ssDefValue.empty() ? "sdv::any_t()" : ssDefValue; return ReplaceKeywords(R"code( m_sig%sig_name% = m_rdispatch.RegisterTxSignal("%msg_name%.%sig_name%", %def_value%); bSuccess &= m_sig%sig_name% ? true : false;)code", mapKeywords); } std::string CCanDataLinkGen::CodeUnregisterSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; mapKeywords["sig_name"] = rsSig.ssName; return ReplaceKeywords(R"code( m_sig%sig_name%.Reset();)code", mapKeywords); } std::string CCanDataLinkGen::CodeProcessRxSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; mapKeywords["sig_name"] = rsSig.ssName; auto prSignalTypeDef = m_rparser.GetSignalTypeDef(rsSig.ssSignalTypeDef); const dbc::SSignalTypeBase& rsSigType = prSignalTypeDef.second ? static_cast(prSignalTypeDef.first) : static_cast(rsSig); // Signals with zero length do not get processed if (!rsSigType.uiSize) return std::string(); mapKeywords["sig_size"] = to_string(rsSigType.uiSize); // Check for correct size and store the default value switch (rsSigType.eValType) { case dbc::SSignalDef::EValueType::signed_integer: if (rsSigType.uiSize > 64) return std::string(); // Invalid size if (rsSigType.uiSize <= 1) return std::string(); // Invalid size mapKeywords["default_value"] = to_string(prSignalTypeDef.second ? static_cast(prSignalTypeDef.first.dDefaultValue) : 0l); mapKeywords["sign_bit"] = to_string(rsSigType.uiSize - 1); break; case dbc::SSignalDef::EValueType::unsigned_integer: if (rsSigType.uiSize > 64) return std::string(); // Invalid size mapKeywords["default_value"] = to_string(prSignalTypeDef.second ? static_cast(prSignalTypeDef.first.dDefaultValue) : 0ul) + "u"; break; case dbc::SSignalDef::EValueType::ieee_float: if (rsSigType.uiSize != 32) return std::string(); // Invalid size mapKeywords["default_value"] = to_string(prSignalTypeDef.second ? static_cast(prSignalTypeDef.first.dDefaultValue) : 0.0f); break; case dbc::SSignalDef::EValueType::ieee_double: if (rsSigType.uiSize != 64) return std::string(); // Invalid size mapKeywords["default_value"] = to_string(prSignalTypeDef.second ? prSignalTypeDef.first.dDefaultValue : 0.0); break; default: return std::string(); // Invalid type } // Create the algorithm stream std::stringstream sstreamAlgorithm; sstreamAlgorithm << R"code( // Process %msg_name%.%sig_name%)code"; // Dependable on the endianness, the signal conversion differs. if (rsSig.eByteOrder == dbc::SSignalDef::EByteOrder::big_endian) { // Helper function to inverse the bit position (which is mirrored for the Motorola format). auto fnInverseBitPos = [](uint32_t uiPos) -> uint32_t { uint32_t uiInverseStartBit = (uiPos >> 3) << 3; uint32_t uiInverseStartBitInByte = (8 - ((uiPos + 1) & 7)) & 7; return uiInverseStartBit + uiInverseStartBitInByte; }; // Helper function to determine the first byte that contains the value. auto fnFirstByte = [&]() { return rsSig.uiStartBit >> 3; }; mapKeywords["first_byte"] = to_string(fnFirstByte()); // Helper function to determine the last byte that contains the value. auto fnLastByte = [&]() { return ((fnInverseBitPos(rsSig.uiStartBit) + rsSigType.uiSize - 1)) >> 3; }; mapKeywords["last_byte"] = to_string(fnLastByte()); // Helper function to determine the mask identifying the start of the value. auto fnFirstByteMask = [&]() { return (1 << ((rsSig.uiStartBit & 0x7) + 1)) - 1; }; mapKeywords["first_byte_mask"] = to_string(fnFirstByteMask()); // Helper function to determine the amount of bits the value needs to shift to the right to reach into focus. auto fnShiftRight = [&]() { return (fnInverseBitPos(fnInverseBitPos(rsSig.uiStartBit) + rsSigType.uiSize) + 1) & 0x7; }; mapKeywords["shift_right"] = to_string(fnShiftRight()); // Add all the bytes for (size_t nSrcIndex = fnFirstByte(); nSrcIndex <= fnLastByte(); nSrcIndex++) { if (nSrcIndex == fnFirstByte()) { sstreamAlgorithm << R"code( uValueHelper.uiUint64Value = rseqData[%first_byte%])code"; if (fnFirstByteMask() != 0xff) sstreamAlgorithm << R"code( & %first_byte_mask%)code"; sstreamAlgorithm << ";"; } else sstreamAlgorithm << R"code( uValueHelper.uiUint64Value = uValueHelper.uiUint64Value << 8 | rseqData[)code" << nSrcIndex << R"code(];)code"; if (nSrcIndex == fnLastByte() && fnShiftRight()) sstreamAlgorithm << R"code( uValueHelper.uiUint64Value >>= %shift_right%;)code"; } } else { // Helper function to determine the first byte that contains the value. auto fnFirstByte = [&]() { return rsSig.uiStartBit >> 3; }; mapKeywords["first_byte"] = to_string(fnFirstByte()); // Helper function to determine the last byte that contains the value. auto fnLastByte = [&]() { return (rsSig.uiStartBit + rsSigType.uiSize - 1) >> 3; }; mapKeywords["last_byte"] = to_string(fnLastByte()); // Helper function to determine the mask identifying the end of the value. auto fnLastByteMask = [&]() { return (1 << (((rsSig.uiStartBit + rsSigType.uiSize - 1) & 0x7) + 1)) - 1; }; mapKeywords["last_byte_mask"] = to_string(fnLastByteMask()); // Helper function to determine the amount of bits the value needs to shift to the right to reach into focus. auto fnShiftRight = [&]() { return rsSig.uiStartBit & 0x7; }; mapKeywords["shift_right"] = to_string(fnShiftRight()); // Add all the bytes for (size_t nSrcIndex = fnLastByte(); nSrcIndex >= fnFirstByte() && nSrcIndex <= fnLastByte(); nSrcIndex--) { if (nSrcIndex == fnLastByte()) { sstreamAlgorithm << R"code( uValueHelper.uiUint64Value = rseqData[%last_byte%])code"; if (fnLastByteMask() != 0xff) sstreamAlgorithm << R"code( & %last_byte_mask%)code"; sstreamAlgorithm << ";"; } else sstreamAlgorithm << R"code( uValueHelper.uiUint64Value = uValueHelper.uiUint64Value << 8 | rseqData[)code" << nSrcIndex << R"code(];)code"; if (nSrcIndex == fnFirstByte() && fnShiftRight()) sstreamAlgorithm << R"code( uValueHelper.uiUint64Value >>= %shift_right%;)code"; } } // Value to hex-string (64-bit) auto fnToHexString = [](uint64_t uiValue) -> std::string { std::stringstream sstream; sstream << "0x" << std::setfill('0') << std::setw(16) << std::hex << uiValue << "ull"; return sstream.str(); }; switch (rsSigType.eValType) { case dbc::SSignalDef::EValueType::signed_integer: // MSB bit determines the sign. if (rsSigType.uiSize != 64) { // If the provided bit is '1', fill the more significant bits with 1 as well. mapKeywords["msb_sign"] = fnToHexString(((1ull << (64ull - rsSigType.uiSize)) - 1) << rsSigType.uiSize); sstreamAlgorithm << R"code( if (uValueHelper.uiUint64Value & (0x1ull << %sign_bit%ull)) // Extend sign to 64 bit. uValueHelper.uiUint64Value |= %msb_sign%;)code"; } 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); if (rsSigType.uiSize > 32) { mapKeywords["sig_helper_value"] = "static_cast(uValueHelper.uiUint64Value)"; mapKeywords["sig_max"] = to_string(rsSigType.dMaximum); } else { mapKeywords["sig_helper_value"] = "static_cast(uValueHelper.s32.u32.uiValue)"; mapKeywords["sig_max"] = StringMustBeFloatValue(to_string(rsSigType.dMaximum)); } } 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: if (rsSigType.uiSize != 32) return std::string(); // Invalid size sstreamAlgorithm << R"code( if ((uValueHelper.s32.u32.uiValue & 0x7f800000) == 0x7f800000) // NaN uValueHelper.s32.u32.fValue = %default_value%;)code"; 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: if (rsSigType.uiSize != 64) return std::string(); // Invalid size sstreamAlgorithm << R"code( if ((uValueHelper.uiUint64Value & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) // NaN uValueHelper.dValue = %default_value%;)code"; 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 std::string(); break; } // Generate the code for the conversion from logical to physical and assign the value to the signal. sstreamAlgorithm << R"code( m_sig%sig_name%.Write()code"; if (rsSigType.dMinimum != rsSigType.dMaximum) sstreamAlgorithm << "std::min(std::max("; sstreamAlgorithm << "%sig_helper_value%"; if (rsSigType.dFactor != 1.0) sstreamAlgorithm << " * %sig_factor%"; if (rsSigType.dOffset != 0.0) sstreamAlgorithm << " + %sig_offset%"; if (rsSigType.dMinimum != rsSigType.dMaximum) sstreamAlgorithm << ", %sig_min%), %sig_max%)"; sstreamAlgorithm << ", transaction);"; return ReplaceKeywords(sstreamAlgorithm.str(), mapKeywords); } std::string CCanDataLinkGen::InitTrigger(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; mapKeywords["sig_name"] = rsSig.ssName; return ReplaceKeywords(R"code( m_trigger.AddSignal(m_sig%sig_name%);)code", mapKeywords); } std::string CCanDataLinkGen::CodeComposeTxSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig) { CKeywordMap mapKeywords; mapKeywords["msg_name"] = rsMsg.ssName; mapKeywords["sig_name"] = rsSig.ssName; auto prSignalTypeDef = m_rparser.GetSignalTypeDef(rsSig.ssSignalTypeDef); const dbc::SSignalTypeBase& rsSigType = prSignalTypeDef.second ? static_cast(prSignalTypeDef.first) : static_cast(rsSig); // Signals with zero length do not get processed if (!rsSigType.uiSize) return std::string(); mapKeywords["sig_size"] = to_string(rsSigType.uiSize); // Check for correct size and store the default value switch (rsSigType.eValType) { case dbc::SSignalDef::EValueType::signed_integer: if (rsSigType.uiSize > 64) return std::string(); // Invalid size if (rsSigType.uiSize <= 1) return std::string(); // Invalid size mapKeywords["default_value"] = to_string(prSignalTypeDef.second ? static_cast(prSignalTypeDef.first.dDefaultValue) : 0l); mapKeywords["sign_bit"] = to_string(rsSigType.uiSize - 1); break; case dbc::SSignalDef::EValueType::unsigned_integer: if (rsSigType.uiSize > 64) return std::string(); // Invalid size mapKeywords["default_value"] = to_string(prSignalTypeDef.second ? static_cast(prSignalTypeDef.first.dDefaultValue) : 0ul) + "u"; break; case dbc::SSignalDef::EValueType::ieee_float: if (rsSigType.uiSize != 32) return std::string(); // Invalid size mapKeywords["default_value"] = to_string(prSignalTypeDef.second ? static_cast(prSignalTypeDef.first.dDefaultValue) : 0.0f); break; case dbc::SSignalDef::EValueType::ieee_double: if (rsSigType.uiSize != 64) return std::string(); // Invalid size mapKeywords["default_value"] = to_string(prSignalTypeDef.second ? prSignalTypeDef.first.dDefaultValue : 0.0); break; default: return std::string(); // Invalid type } // Create the algorithm stream std::stringstream sstreamAlgorithm; sstreamAlgorithm << R"code( // Compose %msg_name%.%sig_name%)code"; bool bHelperCast = false; bool bRound = false; 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); mapKeywords["sig_physical_cast"] = "double"; if (rsSigType.uiSize > 32) { mapKeywords["sig_helper_value"] = "uValueHelper.iInt64Value"; mapKeywords["sig_helper_cast"] = "int64_t"; } else { mapKeywords["sig_helper_value"] = "uValueHelper.s32.u32.iValue"; mapKeywords["sig_helper_cast"] = "int32_t"; } bHelperCast = true; bRound = true; } else { 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"; mapKeywords["sig_physical_cast"] = "long long int"; if (rsSigType.uiSize > 32) { mapKeywords["sig_helper_value"] = "uValueHelper.iInt64Value"; mapKeywords["sig_helper_cast"] = "int64_t"; } else { mapKeywords["sig_helper_value"] = "uValueHelper.s32.u32.iValue"; mapKeywords["sig_helper_cast"] = "int32_t"; } bHelperCast = true; } 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); mapKeywords["sig_physical_cast"] = "double"; if (rsSigType.uiSize > 32) { mapKeywords["sig_helper_value"] = "uValueHelper.uiUint64Value"; mapKeywords["sig_helper_cast"] = "uint64_t"; } else { mapKeywords["sig_helper_value"] = "uValueHelper.s32.u32.uiValue"; mapKeywords["sig_helper_cast"] = "uint32_t"; } bHelperCast = true; bRound = true; } else { 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"; mapKeywords["sig_physical_cast"] = "long long int"; if (rsSigType.uiSize > 32) { mapKeywords["sig_helper_value"] = "uValueHelper.uiUint64Value"; mapKeywords["sig_helper_cast"] = "uint64_t"; } else { mapKeywords["sig_helper_value"] = "uValueHelper.s32.u32.uiValue"; mapKeywords["sig_helper_cast"] = "uint32_t"; } bHelperCast = true; } break; case dbc::SSignalDef::EValueType::ieee_float: if (rsSigType.uiSize != 32) return std::string(); // Invalid size 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_physical_cast"] = "float"; mapKeywords["sig_helper_value"] = "uValueHelper.s32.u32.fValue"; break; case dbc::SSignalDef::EValueType::ieee_double: if (rsSigType.uiSize != 64) return std::string(); // Invalid size 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_physical_cast"] = "double"; mapKeywords["sig_helper_value"] = "uValueHelper.dValue"; break; default: return std::string(); break; } // Generate the code for the conversion from physical to logical. sstreamAlgorithm << R"code( %sig_helper_value% = )code"; if (bHelperCast) sstreamAlgorithm << "static_cast<%sig_helper_cast%>("; if (bRound) sstreamAlgorithm << "std::round("; if (rsSigType.dOffset != 0.0) sstreamAlgorithm << "("; if (rsSigType.dMinimum != rsSigType.dMaximum) sstreamAlgorithm << "std::min(std::max("; sstreamAlgorithm << "static_cast<%sig_physical_cast%>(m_sig%sig_name%.Read(transaction))"; if (rsSigType.dMinimum != rsSigType.dMaximum) sstreamAlgorithm << ", %sig_min%), %sig_max%)"; if (rsSigType.dOffset != 0.0) sstreamAlgorithm << " - %sig_offset%)"; if (rsSigType.dFactor != 1.0) sstreamAlgorithm << " / %sig_factor%"; if (bRound) sstreamAlgorithm << ")"; if (bHelperCast) sstreamAlgorithm << ")"; sstreamAlgorithm << ";"; // Dependable on the endianness, the signal conversion differs. if (rsSig.eByteOrder == dbc::SSignalDef::EByteOrder::big_endian) { // Helper function to inverse the bit position (which is mirrored for the Motorola format). auto fnInverseBitPos = [](uint32_t uiPos) -> uint32_t { uint32_t uiInverseStartBit = (uiPos >> 3) << 3; uint32_t uiInverseStartBitInByte = (8 - ((uiPos + 1) & 7)) & 7; return uiInverseStartBit + uiInverseStartBitInByte; }; // Helper function to determine the first byte that contains the value. auto fnFirstByte = [&]() { return rsSig.uiStartBit >> 3; }; mapKeywords["first_byte"] = to_string(fnFirstByte()); // Helper function to determine the last byte that contains the value. auto fnLastByte = [&]() { return ((fnInverseBitPos(rsSig.uiStartBit) + rsSigType.uiSize - 1)) >> 3; }; mapKeywords["last_byte"] = to_string(fnLastByte()); // Helper function to determine the mask identifying the start of the value. auto fnFirstByteMask = [&]() { return (1 << ((rsSig.uiStartBit & 0x7) + 1)) - 1; }; mapKeywords["first_byte_mask"] = to_string(fnFirstByteMask()); // Helper function to determine the amount of bits the value needs to shift to the left to reach the right bit-position. auto fnShiftLeft = [&]() { return (fnInverseBitPos(fnInverseBitPos(rsSig.uiStartBit) + rsSigType.uiSize) + 1) & 0x7; }; mapKeywords["shift_left"] = to_string(fnShiftLeft()); // Add all the bytes for (size_t nSrcIndex = fnLastByte(); nSrcIndex >= fnFirstByte() && nSrcIndex <= fnLastByte(); nSrcIndex--) { if (nSrcIndex == fnLastByte() && fnShiftLeft()) sstreamAlgorithm << R"code( uValueHelper.uiUint64Value <<= %shift_left%;)code"; if (nSrcIndex == fnFirstByte()) { sstreamAlgorithm << R"code( msg.seqData[%first_byte%] |= uValueHelper.uiUint64Value & )code"; if (fnFirstByteMask() != 0xff) sstreamAlgorithm << "%first_byte_mask%"; else sstreamAlgorithm << "0xff"; sstreamAlgorithm << ";"; } else sstreamAlgorithm << R"code( msg.seqData[)code" << nSrcIndex << R"code(] |= uValueHelper.uiUint64Value & 0xff; uValueHelper.uiUint64Value >>= 8;)code"; } } else { // Helper function to determine the first byte that contains the value. auto fnFirstByte = [&]() { return rsSig.uiStartBit >> 3; }; mapKeywords["first_byte"] = to_string(fnFirstByte()); // Helper function to determine the last byte that contains the value. auto fnLastByte = [&]() { return (rsSig.uiStartBit + rsSigType.uiSize - 1) >> 3; }; mapKeywords["last_byte"] = to_string(fnLastByte()); // Helper function to determine the mask identifying the end of the value. auto fnLastByteMask = [&]() { return (1 << (((rsSig.uiStartBit + rsSigType.uiSize - 1) & 0x7) + 1)) - 1; }; mapKeywords["last_byte_mask"] = to_string(fnLastByteMask()); // Helper function to determine the amount of bits the value needs to shift to the left to reach the right bit-position. auto fnShiftLeft = [&]() { return rsSig.uiStartBit & 0x7; }; mapKeywords["shift_left"] = to_string(fnShiftLeft()); // Add all the bytes for (size_t nSrcIndex = fnFirstByte(); nSrcIndex <= fnLastByte(); nSrcIndex++) { if (nSrcIndex == fnFirstByte() && fnShiftLeft()) sstreamAlgorithm << R"code( uValueHelper.uiUint64Value <<= %shift_left%;)code"; if (nSrcIndex == fnLastByte()) { sstreamAlgorithm << R"code( msg.seqData[%last_byte%] |= uValueHelper.uiUint64Value & )code"; if (fnLastByteMask() != 0xff) sstreamAlgorithm << "%last_byte_mask%"; else sstreamAlgorithm << "0xff"; sstreamAlgorithm << ";"; } else sstreamAlgorithm << R"code( msg.seqData[)code" << nSrcIndex << R"code(] |= uValueHelper.uiUint64Value & 0xff; uValueHelper.uiUint64Value >>= 8;)code"; } } return ReplaceKeywords(sstreamAlgorithm.str(), mapKeywords); } std::string CCanDataLinkGen::CodeDBCFileVersion(const std::string& rsVersion) { if (rsVersion.empty()) { return ""; } CKeywordMap mapKeywords; mapKeywords["dbc_version"] = rsVersion; return ReplaceKeywords(R"code(DBC file version: %dbc_version%)code", mapKeywords); } std::string CCanDataLinkGen::StringMustBeFloatValue(const std::string& rssInput) { if (rssInput.find_first_of(".") != std::string::npos) { return rssInput; } return rssInput + ".0"; }