/******************************************************************************** * 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 ********************************************************************************/ #ifndef PARAM_IMPL_INL #define PARAM_IMPL_INL #ifndef PARAM_IMPL_H #include "param_impl.h" #endif //! defined PARAM_IMPL_H #include #include /** * @brief Software Defined Vehicle framework. */ namespace sdv { template inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam) { EParamType eTypeLocal = EParamType::boolean_param; if constexpr (std::is_same_v) eTypeLocal = EParamType::boolean_param; else if constexpr (std::is_arithmetic_v) eTypeLocal = EParamType::number_param; // No number limitation else if constexpr (TypeIsString()) eTypeLocal = EParamType::string_param; // No pattern else if constexpr (std::is_enum_v) eTypeLocal = EParamType::enum_param; // No enumerator labels else static_assert(sdv::internal::always_false_v, "The parameter type is not supported!"); any_t anyDefaultValLocal; if constexpr (std::is_enum_v) { static_assert(std::is_same_v>, TDefaultType>, "The enum types of the value and the default value must be identical!"); anyDefaultValLocal = static_cast>(tDefaultVal); } else anyDefaultValLocal = tDefaultVal; uint32_t uiFlagsLocal = uiFlagsParam; if (TypeIsReadOnly()) uiFlagsLocal |= static_cast(EParamFlags::read_only); if (uiFlagsLocal & static_cast(EParamFlags::read_only)) uiFlagsLocal &= ~static_cast(EParamFlags::locked); // Readonly cannot write Init(eTypeLocal, rssName, anyDefaultValLocal, rssUnit, rssCategory, rssDescription, uiFlagsLocal); if constexpr (std::is_enum_v) uExtInfo.sEnumInfo.seqLabels = internal::GetLabelMapHelper().GetLabelMap(); } template inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const any_t& ranyLimitLow, bool bIncludeLow, const any_t& ranyLimitHigh, bool bIncludeHigh, const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam) { static_assert(std::is_arithmetic_v && !std::is_same_v, "The construct function for numeric types expects the type to be numeric."); uint32_t uiFlagsLocal = uiFlagsParam; if (TypeIsReadOnly()) uiFlagsLocal |= static_cast(EParamFlags::read_only); if (uiFlagsLocal & static_cast(EParamFlags::read_only)) uiFlagsLocal &= ~static_cast(EParamFlags::locked); // Readonly cannot write Init(EParamType::number_param, rssName, tDefaultVal, rssUnit, rssCategory, rssDescription, uiFlagsLocal); uExtInfo.sNumberInfo.anyLowerLimit = ranyLimitLow; uExtInfo.sNumberInfo.bIncludeLowerLinit = bIncludeLow; uExtInfo.sNumberInfo.anyUpperLimit = ranyLimitHigh; uExtInfo.sNumberInfo.bIncludeUpperLimit = bIncludeHigh; } template inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const std::string& rssPattern, const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam) { static_assert(TypeIsString(), "The construct function for string types expects the type to be a string."); uint32_t uiFlagsLocal = uiFlagsParam; if (TypeIsReadOnly()) uiFlagsLocal |= static_cast(EParamFlags::read_only); if (uiFlagsLocal & static_cast(EParamFlags::read_only)) uiFlagsLocal &= ~static_cast(EParamFlags::locked); // Readonly cannot write Init(EParamType::string_param, rssName, tDefaultVal, rssUnit, rssCategory, rssDescription, uiFlagsLocal); uExtInfo.sStringInfo.ssPattern = rssPattern; } template inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const sequence& rseqLabels, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam) { static_assert(std::is_enum_v || (std::is_integral_v && !std::is_same_v), "The construct function for enumerators and bitmasks expects the type to be an enum or an integral type."); uint32_t uiFlagsLocal = uiFlagsParam; if (TypeIsReadOnly()) uiFlagsLocal |= static_cast(EParamFlags::read_only); if (uiFlagsLocal & static_cast(EParamFlags::read_only)) uiFlagsLocal &= ~static_cast(EParamFlags::locked); // Readonly cannot write if constexpr (std::is_enum_v) { Init(EParamType::enum_param, rssName, static_cast>(tDefaultVal), "", rssCategory, rssDescription, uiFlagsLocal); uExtInfo.sEnumInfo.seqLabels = rseqLabels; } else { Init(EParamType::bitmask_param, rssName, tDefaultVal, "", rssCategory, rssDescription, uiFlagsLocal); uExtInfo.sBitmaskInfo.seqLabels = rseqLabels; } } inline CSdvParamInfo::CSdvParamInfo(const SParamInfo& rInfo) : SParamInfo(rInfo) {} inline CSdvParamInfo::CSdvParamInfo(SParamInfo&& rInfo) : SParamInfo(std::move(rInfo)) {} inline CSdvParamInfo& CSdvParamInfo::operator=(const SParamInfo& rsInfo) { static_cast(*this) = rsInfo; return *this; } inline CSdvParamInfo& CSdvParamInfo::operator=(SParamInfo&& rsInfo) { static_cast(*this) = std::move(rsInfo); return *this; } inline SParamInfo CSdvParamInfo::InfoStruct() const { return *this; } inline const u8string& CSdvParamInfo::Name() const { return SParamInfo::ssName; } inline u8string CSdvParamInfo::Path() const { sdv::u8string ssPath; if (!SParamInfo::ssGroup.empty()) ssPath += ssGroup + "."; ssPath += ssName; return ssPath; } inline const any_t& CSdvParamInfo::DefaultVal() const { return SParamInfo::anyDefaultVal; } inline const u8string& CSdvParamInfo::Group() const { return SParamInfo::ssGroup; } inline const u8string& CSdvParamInfo::Description() const { return SParamInfo::ssDescription; } inline const u8string& CSdvParamInfo::Unit() const { return SParamInfo::ssUnit; } inline uint32_t CSdvParamInfo::Flags() const { return uiFlags; } inline bool CSdvParamInfo::ReadOnly() const { return (uiFlags & static_cast(EParamFlags::read_only)) ? true : false; } inline bool CSdvParamInfo::Temporary() const { return (uiFlags & static_cast(EParamFlags::temporary)) ? true : false; } inline bool CSdvParamInfo::Dirty() const { return (uiFlags & static_cast(EParamFlags::dirty)) ? true : false; } inline bool CSdvParamInfo::Locked() const { return (uiFlags & static_cast(EParamFlags::locked)) ? true : false; } inline bool CSdvParamInfo::Boolean() const { return get_switch() == EParamType::boolean_param; } inline bool CSdvParamInfo::Numeric() const { return get_switch() == EParamType::number_param; } inline bool CSdvParamInfo::String() const { return get_switch() == EParamType::string_param; } inline bool CSdvParamInfo::Enum() const { return get_switch() == EParamType::enum_param; } inline bool CSdvParamInfo::Bitmask() const { return get_switch() == EParamType::bitmask_param; } inline std::pair CSdvParamInfo::NumericLimitLow() const { if (!Numeric()) return {}; return std::make_pair(uExtInfo.sNumberInfo.anyLowerLimit, uExtInfo.sNumberInfo.bIncludeLowerLinit); } inline std::pair CSdvParamInfo::NumericLimitHigh() const { if (!Numeric()) return {}; return std::make_pair(uExtInfo.sNumberInfo.anyUpperLimit, uExtInfo.sNumberInfo.bIncludeUpperLimit); } inline std::string CSdvParamInfo::StringPattern() const { if (!String()) return {}; return uExtInfo.sStringInfo.ssPattern; } inline sequence CSdvParamInfo::EnumBitmaskLabels() const { if (Enum()) return uExtInfo.sEnumInfo.seqLabels; else if (Bitmask()) return uExtInfo.sBitmaskInfo.seqLabels; else return {}; } inline void CSdvParamInfo::Init(EParamType eTypeParam, const std::string& rssName, const any_t& ranyDefaultVal, const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam) { switch_to(eTypeParam); uiFlags = uiFlagsParam; ssName = rssName; anyDefaultVal = ranyDefaultVal; ssUnit = rssUnit; ssGroup = rssCategory; ssDescription = rssDescription; } namespace internal { template inline CParamGuardian::CParamGuardian(bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct) : CSdvParamInfo(tConstruct...), m_bLockable(bLockable), m_bAutoDirty(bAutoDirty) {} inline SParamInfo CParamGuardian::InfoStruct() const { sdv::SParamInfo sInfo = CSdvParamInfo::InfoStruct(); sInfo.uiFlags = Flags(); return sInfo; } inline uint32_t CParamGuardian::Flags() const { uint32_t uiFlagsTemp = CSdvParamInfo::Flags() & ~static_cast(EParamFlags::state_mask); if (m_bLocked) uiFlagsTemp |= static_cast(EParamFlags::locked); if (m_bDirty) uiFlagsTemp |= static_cast(EParamFlags::dirty); return uiFlagsTemp; } inline bool CParamGuardian::UpdateDirty(const sdv::any_t& ranyValue) { if (!m_bAutoDirty) return false; bool bDirty = m_bDirty; m_bDirty |= ranyValue != m_anyStored; m_anyStored = ranyValue; return m_bDirty != bDirty; } inline bool CParamGuardian::SetDirty() { bool bDirty = m_bDirty; m_bDirty = true; return m_bDirty != bDirty; } inline bool CParamGuardian::ResetDirty() { bool bDirty = m_bDirty; m_bDirty = false; return m_bDirty != bDirty; } inline bool CParamGuardian::Dirty() const { return m_bDirty; } inline bool CParamGuardian::Lockable() const { return m_bLockable; } inline bool CParamGuardian::Lock() { if (!m_bLockable) return false; bool bLocked = m_bLocked; m_bLocked = true; return m_bLocked != bLocked; } inline bool CParamGuardian::Unlock() { if (!m_bLockable) return false; bool bLocked = m_bLocked; m_bLocked = false; return m_bLocked != bLocked; } inline bool CParamGuardian::Locked() const { return m_bLocked; } inline bool CParamGuardian::CheckRestrictions(const any_t& ranyValue) { if (Numeric()) { // Check for lower limit auto prLimitLow = NumericLimitLow(); if (!prLimitLow.first.empty()) { if (prLimitLow.second) { if (ranyValue < prLimitLow.first) return false; } else { if (ranyValue <= prLimitLow.first) return false; } } // Check for higher limit auto prLimitHigh = NumericLimitHigh(); if (!prLimitHigh.first.empty()) { if (prLimitHigh.second) { if (ranyValue > prLimitHigh.first) return false; } else { if (ranyValue >= prLimitHigh.first) return false; } } } else if (String()) { // Check for compatibility to the regular expression pattern. if (!StringPattern().empty()) { std::regex regexPattern(StringPattern()); if (!std::regex_match(static_cast(ranyValue), regexPattern)) return false; } } else if (Enum()) { // Check for occurrence in the label sequence auto seqLabels = EnumBitmaskLabels(); if (std::find_if(seqLabels.begin(), seqLabels.end(), [&](const auto& rsLabel) { return ranyValue == rsLabel.anyValue; }) == seqLabels.end()) return false; } else if (Bitmask()) { // Check for the occurence of each set bit in the label sequence auto seqLabels = EnumBitmaskLabels(); uint64_t uiBitmask = ranyValue; for (const auto& rsLabel : seqLabels) uiBitmask &= ~static_cast(rsLabel.anyValue); if (uiBitmask) return false; } // All okay; restrictions were checked return true; } template template inline CParamValue::CParamValue(TVar& rtVar, bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct) : CParamGuardian(bLockable, bAutoDirty, rtVar, tConstruct...), m_rtVar(rtVar) { // Assign the default value if constexpr (!CSdvParamInfo::TypeIsReadOnly()) { m_rtVar = DefaultVal().get(); UpdateDirty(DefaultVal()); ResetDirty(); } } template inline bool CParamValue::Set(const any_t& ranyValue) { // Assign the value if constexpr (!CSdvParamInfo::TypeIsReadOnly()) { if (!Locked() && !ReadOnly() && CheckRestrictions(ranyValue)) { m_rtVar = ranyValue.get(); UpdateDirty(ranyValue); return true; } } return false; } template inline any_t CParamValue::Get() const { // Remarks: in case of an enum, an explicit construction is needed. return sdv::any_t(m_rtVar); } } // namespace internal inline std::shared_ptr CSdvParamMap::FindParamObject(const sdv::u8string& rssPath) { // Iterate through the registrations for (const SParamRegistration& rsRegistration : m_vecParamMapRegistration) { // Member parameter map entry if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) { auto ptrParameter = rsRegistration.MemberMap()->Access()->FindParamObject(rssPath); if (ptrParameter) return ptrParameter; } // Parameter entry if (rsRegistration.Parameter() && rsRegistration.Parameter()->Path() == rssPath) return rsRegistration.Parameter(); } return {}; } inline std::shared_ptr CSdvParamMap::FindParamObject(const sdv::u8string& rssPath) const { // Iterate through the registrations for (const SParamRegistration& rsRegistration : m_vecParamMapRegistration) { // Member parameter map entry if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) { auto ptrParameter = rsRegistration.MemberMap()->Access()->FindParamObject(rssPath); if (ptrParameter) return ptrParameter; } // Parameter entry if (rsRegistration.Parameter() && rsRegistration.Parameter()->Path() == rssPath) return rsRegistration.Parameter(); } return {}; } inline sequence CSdvParamMap::GetParamPaths() const { // Split path function (splits the group from the parameter names) auto fnSplitPath = [](const sdv::u8string& rssPath) -> std::pair { size_t nPos = rssPath.find_last_of('.'); if (nPos == sdv::u8string::npos) return std::make_pair("", rssPath); return std::make_pair(rssPath.substr(0, nPos), rssPath.substr(nPos + 1)); }; std::set setOneTime; sequence seqPaths; for (const auto& rsRegistration : m_vecParamMapRegistration) { // Add a path to the sequence. auto fnAddPath = [&](const u8string& rssPath) { // Check if already present in the list if (setOneTime.find(rssPath) != setOneTime.end()) return; setOneTime.insert(rssPath); // Sort the paths per group, so all paths belonging to one group is returned together. // Find thee first location where the group is larger than the one of this path. auto prPath = fnSplitPath(rssPath); auto itPos = seqPaths.begin(); while (itPos != seqPaths.end()) { auto prStoredPath = fnSplitPath(*itPos); if (prPath.first.compare(prStoredPath.first) < 0) break; ++itPos; } // Insert into the sequence seqPaths.insert(itPos, rssPath); }; // Add the chained member to the list if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) { auto seqNamesMemberMap = rsRegistration.MemberMap()->Access()->GetParamPaths(); for (const auto& rssPath : seqNamesMemberMap) fnAddPath(rssPath); } // Add the parameter to the list if (rsRegistration.Parameter()) fnAddPath(rsRegistration.Parameter()->Path()); } return seqPaths; } inline any_t CSdvParamMap::GetParam(const u8string& rssPath) const { auto ptrParam = FindParamObject(rssPath); if (!ptrParam) return {}; return ptrParam->Get(); } inline bool CSdvParamMap::SetParam(const u8string& rssPath, any_t ranyValue) { auto ptrParam = FindParamObject(rssPath); if (!ptrParam) return false; return ptrParam->Set(ranyValue); } inline SParamInfo CSdvParamMap::GetParamInfo(const u8string& rssPath) const { auto ptrParamInfo = FindParamObject(rssPath); if (!ptrParamInfo) return {}; return ptrParamInfo.get()->InfoStruct(); } inline bool CSdvParamMap::IsParamDirty(const u8string& rssPath) const { auto ptrParam = FindParamObject(rssPath); if (!ptrParam) return false; return ptrParam->Dirty(); } inline void CSdvParamMap::SetParamDirtyFlag(const std::string& rssPath) { auto ptrParam = FindParamObject(rssPath); if (!ptrParam) return; ptrParam->SetDirty(); } inline void CSdvParamMap::ResetParamDirtyFlag(const u8string& rssPath) { auto ptrParam = FindParamObject(rssPath); if (!ptrParam) return; ptrParam->ResetDirty(); } inline bool CSdvParamMap::IsParamMapDirty() const { std::set setOneTime; for (const auto& rsRegistration : m_vecParamMapRegistration) { // Member parameter map entry if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) { if (rsRegistration.MemberMap()->Access()->IsParamMapDirty()) return true; continue; } // Parameter entry if (rsRegistration.Parameter()) { // Check if already present in the list if (setOneTime.find(rsRegistration.Parameter()->Name()) != setOneTime.end()) continue; setOneTime.insert(rsRegistration.Parameter()->Name()); // Check for dirtiness if (rsRegistration.Parameter()->Dirty()) return true; } } return false; } inline void CSdvParamMap::ResetParamMapDirtyFlags() { std::set setOneTime; for (const auto& rsRegistration : m_vecParamMapRegistration) { // Member parameter map entry if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) { rsRegistration.MemberMap()->Access()->ResetParamMapDirtyFlags(); continue; } // Parameter entry if (rsRegistration.Parameter()) { // Check if already present in the list if (setOneTime.find(rsRegistration.Parameter()->Name()) != setOneTime.end()) continue; setOneTime.insert(rsRegistration.Parameter()->Name()); // Reset dirtiness rsRegistration.Parameter()->ResetDirty(); } } } inline void CSdvParamMap::LockParamMap() { for (const auto& rsRegistration : m_vecParamMapRegistration) { // Member parameter map entry if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) { rsRegistration.MemberMap()->Access()->LockParamMap(); continue; } // Parameter entry if (rsRegistration.Parameter()) { // Lock rsRegistration.Parameter()->Lock(); } } } inline void CSdvParamMap::UnlockParamMap() { for (const auto& rsRegistration : m_vecParamMapRegistration) { // Member parameter map entry if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) { rsRegistration.MemberMap()->Access()->UnlockParamMap(); continue; } // Parameter entry if (rsRegistration.Parameter()) { // Lock rsRegistration.Parameter()->Unlock(); } } } template inline std::shared_ptr CSdvParamMap::RegisterParameter(TVar& rtVar, bool bLockable, bool bAutoDirty, TConstruct... tConstruct) { auto ptrParam = std::make_shared>(rtVar, bLockable, bAutoDirty, tConstruct...); m_vecParamMapRegistration.push_back(SParamRegistration(ptrParam)); return ptrParam; } inline void CSdvParamMap::RegisterMemberMap(const std::shared_ptr& rptrMember) { m_vecParamMapRegistration.push_back(SParamRegistration(rptrMember)); } inline void CSdvParamMap::InitParamMap() { // Nothing to do here. } inline bool CSdvParamMap::BuildNecessary() const { return m_vecParamMapRegistration.empty(); } template constexpr bool CSdvParamInfo::TypeIsString() { using TType2 = std::remove_cv_t; using TRoot = std::remove_cv_t>>; constexpr bool bArray = std::is_array_v; constexpr bool bPointer = std::is_pointer_v; constexpr bool bCharacter = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; constexpr bool bStdString = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; constexpr bool bSdvString = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; constexpr bool bStdPath = std::is_same_v; return bStdString || bSdvString || (bArray && bCharacter) || (bPointer && bCharacter) || bStdPath; } template constexpr bool CSdvParamInfo::TypeIsReadOnly() { using TType2 = std::remove_cv_t; constexpr bool bArray = std::is_array_v; constexpr bool bPointer = std::is_pointer_v; return bArray || bPointer || std::is_const_v; } }; // namespace sdv #endif // !defined PARAM_IMPL_INL