From 6dfeac9df1098f66d9347d8ead1a82fc67ff9ab7 Mon Sep 17 00:00:00 2001 From: Evgueni Driouk Date: Tue, 29 Jul 2025 15:30:42 +0200 Subject: [PATCH 1/2] Add methods to get devices and boards (#1277) * Added GetDeviceList and GetDeviceData methods and corresponding structures * Unit Tests and corrections * Use id string * Board data * Tests and fixes * Update `csolution-rpc` version * add #include --------- Co-authored-by: Daniel Brondani --- libs/rtemodel/include/RteDevice.h | 11 +- libs/rtemodel/include/RteItem.h | 26 +- libs/rtemodel/include/RteTarget.h | 1 - libs/rtemodel/src/RteDevice.cpp | 58 ++-- libs/rtemodel/src/RteItem.cpp | 38 ++- libs/rtemodel/src/RteTarget.cpp | 19 +- libs/rtemodel/test/src/RteModelTest.cpp | 4 +- libs/rteutils/include/RteConstants.h | 1 + tools/projmgr/CMakeLists.txt | 4 +- tools/projmgr/include/ProjMgrRpcServerData.h | 19 +- tools/projmgr/include/ProjMgrRunDebug.h | 3 +- tools/projmgr/src/ProjMgrRpcServer.cpp | 146 ++++++--- tools/projmgr/src/ProjMgrRpcServerData.cpp | 176 ++++++++++- tools/projmgr/src/ProjMgrRunDebug.cpp | 18 +- tools/projmgr/test/src/ProjMgrRpcTests.cpp | 303 ++++++++++++++++++- 15 files changed, 682 insertions(+), 145 deletions(-) diff --git a/libs/rtemodel/include/RteDevice.h b/libs/rtemodel/include/RteDevice.h index 3f453a0fe..8e256e0d5 100644 --- a/libs/rtemodel/include/RteDevice.h +++ b/libs/rtemodel/include/RteDevice.h @@ -1054,7 +1054,7 @@ class RteDeviceItem : public RteDeviceElement * @brief get list of all effective properties for given tag and processor name * @param tag property tag * @param pName processor name - * @return list of RteDeviceProperty pointers + * @return reference to the list of RteDeviceProperty pointers */ const std::list& GetEffectiveProperties(const std::string& tag, const std::string& pName); @@ -1066,6 +1066,13 @@ class RteDeviceItem : public RteDeviceElement */ RteDeviceProperty* GetSingleEffectiveProperty(const std::string& tag, const std::string& pName); + /** + * @brief return list of all effective memory properties for all processors + * @param tag property tag + * @return list of RteDeviceProperty* pointers + */ + std::list GetAllEffectiveProperties(const std::string& tag); + /** * @brief get list of immediate RteDeviceItem children * @return list to RteDeviceItem pointers @@ -1429,7 +1436,7 @@ class RteDeviceItemAggregate bool IsDeprecated() const { return m_bDeprecated; } private: - static std::string GetMemorySizeString(unsigned int size); + static std::string GetMemorySizeString(unsigned long long size); static std::string GetScaledClockFrequency(const std::string& dclock); std::string m_name; diff --git a/libs/rtemodel/include/RteItem.h b/libs/rtemodel/include/RteItem.h index c246b6f9c..32bb4e16f 100644 --- a/libs/rtemodel/include/RteItem.h +++ b/libs/rtemodel/include/RteItem.h @@ -750,41 +750,53 @@ class RteItem : public XmlTreeItem * @return value of memory attribute "access" */ virtual const std::string& GetAccess() const { return GetAttribute("access"); } + + /** + * @brief return a string of memory access permissions + * @return value of memory attribute "access" or construct it + */ + virtual std::string GetAccessPermissions() const; + + /** + * @brief return a string of memory access attributes permissions + * @return pair of strings with access permissions (rwx) and attributes (psnc) + */ + virtual std::pair GetAccessAttributes() const; /** * @brief check for memory read access * @return true if read access specified */ - virtual bool IsReadAccess(); + virtual bool IsReadAccess() const; /** * @brief check for memory write access * @return true if write access specified */ - virtual bool IsWriteAccess(); + virtual bool IsWriteAccess() const; /** * @brief check for memory executable access * @return true if executable access specified */ - virtual bool IsExecuteAccess(); + virtual bool IsExecuteAccess() const; /** * @brief check for memory secure access * @return true if secure access specified */ - virtual bool IsSecureAccess(); + virtual bool IsSecureAccess() const; /** * @brief check for memory non-secure access * @return true if non-secure access specified */ - virtual bool IsNonSecureAccess(); + virtual bool IsNonSecureAccess() const; /** * @brief check for memory callable access * @return true if callable access specified */ - virtual bool IsCallableAccess(); + virtual bool IsCallableAccess() const; /** * @brief check for memory peripheral area * @return true if memory peripheral area specified */ - virtual bool IsPeripheralAccess(); + virtual bool IsPeripheralAccess() const; /** * @brief get RteCondition associated with the item diff --git a/libs/rtemodel/include/RteTarget.h b/libs/rtemodel/include/RteTarget.h index 654bd86e6..4a8ec13e4 100644 --- a/libs/rtemodel/include/RteTarget.h +++ b/libs/rtemodel/include/RteTarget.h @@ -596,7 +596,6 @@ class RteTarget : public RteItem void FilterComponents(); std::string GenerateRegionsHeaderContent() const; std::string GenerateMemoryRegionContent(const std::vector memVec, const std::string& id, const std::string& dfp) const; - std::pair GetAccessAttributes(RteItem* mem) const; bool GenerateRTEComponentsH(); bool GenerateRteHeaderFile(const std::string& headerName, const std::string& content, bool bRegionsHeader = false, const std::string& directory = EMPTY_STRING); diff --git a/libs/rtemodel/src/RteDevice.cpp b/libs/rtemodel/src/RteDevice.cpp index 4c3b76d65..607576c27 100644 --- a/libs/rtemodel/src/RteDevice.cpp +++ b/libs/rtemodel/src/RteDevice.cpp @@ -19,6 +19,8 @@ #include "XMLTree.h" +#include + using namespace std; static const list EMPTY_PROPERTY_LIST; @@ -813,6 +815,23 @@ RteDeviceProperty* RteDeviceItem::GetSingleEffectiveProperty(const string& tag, return nullptr; } +std::list RteDeviceItem::GetAllEffectiveProperties(const std::string& tag) +{ + // Make a copy, simpler to merge with potential processor specific memories + list properties = GetEffectiveProperties(tag, RteUtils::EMPTY_STRING); + // Iterate over processors + for(auto [pname, _] : GetProcessors()) { + // Collect processor - unique memories + const list& procProps = GetEffectiveProperties(tag, pname); + for(auto p : procProps) { + if(std::find(properties.begin(), properties.end(), p) == properties.end()) { + properties.push_back(p); + } + } + } + return properties; +} + void RteDeviceItem::GetEffectiveFilterAttributes(const string& pName, XmlItem& attributes) { @@ -1175,9 +1194,6 @@ string RteDeviceItemAggregate::GetSummaryString() const list processors; item->GetEffectiveProcessors(processors); - // Make a copy, simpler to merge with potential processor specific memories - list mems = item->GetEffectiveProperties("memory", RteUtils::EMPTY_STRING); - // Iterate over processors for (auto it = processors.begin(); it != processors.end(); ++it) { procProperties = *it; @@ -1205,39 +1221,21 @@ string RteDeviceItemAggregate::GetSummaryString() const } summary += GetScaledClockFrequency(dclock); } - - // Collect unique memory attributes - const list& procMems = item->GetEffectiveProperties("memory", procProperties->GetAttribute("Pname")); - list addMems; - for (auto procMemIt = procMems.begin(); procMemIt != procMems.end(); ++procMemIt) { - auto memsIt = mems.begin(); - for (; memsIt != mems.end(); ++memsIt) { - if ((*memsIt) == (*procMemIt)) { - break; - } - } - if (memsIt == mems.end()) { - addMems.push_back(*procMemIt); - } - } - - if (!addMems.empty()) { - mems.insert(mems.end(), addMems.begin(), addMems.end()); - } } // Memory (RAM/ROM) - unsigned int ramSize = 0, romSize = 0; - + unsigned long long ramSize = 0, romSize = 0; + // Get all memory properties + list mems = item->GetAllEffectiveProperties("memory"); for (auto memsIt = mems.begin(); memsIt != mems.end(); ++memsIt) { RteDeviceMemory* mem = dynamic_cast(*memsIt); if (!mem) { continue; } if (mem->IsWriteAccess()) { - ramSize += mem->GetAttributeAsUnsigned("size"); + ramSize += mem->GetAttributeAsULL("size"); } else if (mem->IsReadAccess()) { - romSize += mem->GetAttributeAsUnsigned("size"); + romSize += mem->GetAttributeAsULL("size"); } } @@ -1258,24 +1256,24 @@ string RteDeviceItemAggregate::GetSummaryString() const return summary; } -string RteDeviceItemAggregate::GetMemorySizeString(unsigned int size) +string RteDeviceItemAggregate::GetMemorySizeString(unsigned long long size) { if (size == 0) { return RteUtils::EMPTY_STRING; } if (size < 1024) { - return (to_string((unsigned long long)size) + " Byte"); + return (to_string(size) + " Byte"); } size >>= 10; // Scale to kByte if (size < 1024 || size % 1024) { // Less than a MByte or division with rest => show kByte - return (to_string((unsigned long long)size) + " kB"); + return (to_string(size) + " KiB"); } size >>= 10; // Scale to MByte - return (to_string((unsigned long long)size) + " MB"); + return (to_string(size) + " MiB"); } string RteDeviceItemAggregate::GetScaledClockFrequency(const string& dclock) diff --git a/libs/rtemodel/src/RteItem.cpp b/libs/rtemodel/src/RteItem.cpp index 2dff57d2c..c1fc2fd88 100644 --- a/libs/rtemodel/src/RteItem.cpp +++ b/libs/rtemodel/src/RteItem.cpp @@ -762,7 +762,31 @@ string RteItem::GetDocFile() const return EMPTY_STRING; } -bool RteItem::IsReadAccess() + +string RteItem::GetAccessPermissions() const +{ + string access = GetAccess(); + if (access.empty()) { + access = string(IsReadAccess() ? "r" : "") + (IsWriteAccess() ? "w" : "") + (IsExecuteAccess() ? "x" : ""); + } + return access; +} + +std::pair RteItem::GetAccessAttributes() const +{ + return { + string(IsReadAccess() ? "r" : "") + + (IsWriteAccess() ? "w" : "") + + (IsExecuteAccess() ? "x" : ""), + string(IsPeripheralAccess() ? "p" : "") + + (IsSecureAccess() ? "s" : "") + + (IsNonSecureAccess() ? "n" : "") + + (IsCallableAccess() ? "c" : "") + }; +} + + +bool RteItem::IsReadAccess() const { if (HasAttribute("id")) { return true; @@ -771,7 +795,7 @@ bool RteItem::IsReadAccess() return access.empty() || access.find('r') != string::npos; } -bool RteItem::IsWriteAccess() +bool RteItem::IsWriteAccess() const { const string& id = GetAttribute("id"); if (!id.empty()) { @@ -781,7 +805,7 @@ bool RteItem::IsWriteAccess() return access.find('w') != string::npos; } -bool RteItem::IsExecuteAccess() +bool RteItem::IsExecuteAccess() const { const string& id = GetAttribute("id"); if (!id.empty()) { @@ -791,7 +815,7 @@ bool RteItem::IsExecuteAccess() return access.find('x') != string::npos; } -bool RteItem::IsSecureAccess() +bool RteItem::IsSecureAccess() const { if (HasAttribute("id")) { return true; @@ -799,7 +823,7 @@ bool RteItem::IsSecureAccess() const string& access = GetAccess(); return access.find('s') != string::npos && access.find('n') == string::npos; } -bool RteItem::IsNonSecureAccess() +bool RteItem::IsNonSecureAccess() const { if (HasAttribute("id")) { return false; @@ -808,7 +832,7 @@ bool RteItem::IsNonSecureAccess() return access.find('n') != string::npos && access.find('s') == string::npos; } -bool RteItem::IsCallableAccess() +bool RteItem::IsCallableAccess() const { if (HasAttribute("id")) { return false; @@ -817,7 +841,7 @@ bool RteItem::IsCallableAccess() return access.find('c') != string::npos; } -bool RteItem::IsPeripheralAccess() +bool RteItem::IsPeripheralAccess() const { if (HasAttribute("id")) { return false; diff --git a/libs/rtemodel/src/RteTarget.cpp b/libs/rtemodel/src/RteTarget.cpp index a9cf8a2a2..11a8fba4e 100644 --- a/libs/rtemodel/src/RteTarget.cpp +++ b/libs/rtemodel/src/RteTarget.cpp @@ -1750,26 +1750,13 @@ std::string RteTarget::GetRegionsHeader() const return GetDeviceFolder() + "/regions_" + filename + ".h"; } -std::pair RteTarget::GetAccessAttributes(RteItem* mem) const -{ - return { - string(mem->IsReadAccess() ? "r" : "") + - (mem->IsWriteAccess() ? "w" : "") + - (mem->IsExecuteAccess() ? "x" : ""), - string(mem->IsPeripheralAccess() ? "p" : "") + - (mem->IsSecureAccess() ? "s" : "") + - (mem->IsNonSecureAccess() ? "n" : "") + - (mem->IsCallableAccess() ? "c" : "") - }; -} - std::string RteTarget::GenerateMemoryRegionContent(const std::vector memVec, const std::string& id, const std::string& dfp) const { string pack, access, name, start, size; bool unused = memVec.empty(); if (!unused) { pack = memVec.front()->GetPackageID() == dfp ? "DFP" : "BSP"; - access = GetAccessAttributes(memVec.front()).first; + access = memVec.front()->GetAccessPermissions(); for (const auto& mem : memVec) { name += (mem == memVec.front() ? "" : "+") + mem->GetName(); } @@ -1860,7 +1847,7 @@ std::string RteTarget::GenerateRegionsHeaderContent() const } else { // search contiguous memory region (same pack, same access) if ((mem->GetPackageID() == alloc.back()->GetPackageID()) && - GetAccessAttributes(mem) == GetAccessAttributes(alloc.back()) && + mem->GetAccessAttributes() == alloc.back()->GetAccessAttributes() && stoul(mem->GetAttribute("start"), nullptr, 16) == stoul(alloc.back()->GetAttribute("start"), nullptr, 16) + stoul(alloc.back()->GetAttribute("size"), nullptr, 16)) { alloc.push_back(mem); @@ -1924,7 +1911,7 @@ std::string RteTarget::GenerateRegionsHeaderContent() const } for (const auto& mem : notAllocated) { oss << "// "; - oss << left << setw(10) << GetAccessAttributes(mem).first + (mem->IsWriteAccess() ? " RAM:" : " ROM:"); + oss << left << setw(10) << mem->GetAccessPermissions() + (mem->IsWriteAccess() ? " RAM:" : " ROM:"); oss << left << setw(maxNameLength + 12) << mem->GetName() + " from" + (mem->GetPackageID() == device->GetPackageID() ? " DFP:" : " BSP:"); oss << "BASE: " << mem->GetAttribute("start") << " SIZE: " << mem->GetAttribute("size"); oss << (mem->GetProcessorName().empty() ? "" : " Pname: " + mem->GetProcessorName()) << RteUtils::LF_STRING; diff --git a/libs/rtemodel/test/src/RteModelTest.cpp b/libs/rtemodel/test/src/RteModelTest.cpp index 77c832d6b..15424aca9 100644 --- a/libs/rtemodel/test/src/RteModelTest.cpp +++ b/libs/rtemodel/test/src/RteModelTest.cpp @@ -150,13 +150,13 @@ TEST(RteModelTest, LoadPacks) { ASSERT_NE(da, nullptr); // test deprecated memory attributes: IROM and IRAM string summary = da->GetSummaryString(); - EXPECT_EQ(summary, "ARM Cortex-M3, 10 MHz, 128 kB RAM, 256 kB ROM"); + EXPECT_EQ(summary, "ARM Cortex-M3, 10 MHz, 128 KiB RAM, 256 KiB ROM"); da = rteModel->GetDeviceAggregate("RteTest_ARMCM4", "ARM:82"); ASSERT_NE(da, nullptr); // test recommended memory attributes: name and access summary = da->GetSummaryString(); - EXPECT_EQ(summary, "ARM Cortex-M4, 10 MHz, 128 kB RAM, 256 kB ROM"); + EXPECT_EQ(summary, "ARM Cortex-M4, 10 MHz, 128 KiB RAM, 256 KiB ROM"); RteBoard* board = rteModel->FindBoard("RteTest board listing (Rev.C)"); ASSERT_NE(board, nullptr); diff --git a/libs/rteutils/include/RteConstants.h b/libs/rteutils/include/RteConstants.h index a63f387df..6d1c68a5b 100644 --- a/libs/rteutils/include/RteConstants.h +++ b/libs/rteutils/include/RteConstants.h @@ -189,6 +189,7 @@ class RteConstants static constexpr const char* RTE_SECURE_ONLY = "Secure-only"; static constexpr const char* RTE_NON_SECURE = "Non-secure"; static constexpr const char* RTE_TZ_DISABLED = "TZ-disabled"; + static constexpr const char* RTE_TZ = "TZ"; static constexpr const char* RTE_NO_TZ = "NO_TZ"; static constexpr const char* RTE_BTI = "BTI"; static constexpr const char* RTE_BTI_SIGNRET = "BTI_SIGNRET"; diff --git a/tools/projmgr/CMakeLists.txt b/tools/projmgr/CMakeLists.txt index d659de728..57f6d99e9 100644 --- a/tools/projmgr/CMakeLists.txt +++ b/tools/projmgr/CMakeLists.txt @@ -17,8 +17,8 @@ include(FetchContent) FetchContent_Declare( rpc-interface DOWNLOAD_EXTRACT_TIMESTAMP ON - URL https://github.com/Open-CMSIS-Pack/csolution-rpc/releases/download/v0.0.2/csolution-rpc.zip - URL_HASH SHA256=bc00342a240d2fded19981524bb286c91541504d4271e936f68c646a8b62908f + URL https://github.com/Open-CMSIS-Pack/csolution-rpc/releases/download/v0.0.3/csolution-rpc.zip + URL_HASH SHA256=edc762373b3b7dad5b966ecb271f03866b12841675e0967a809c10c9883f29f4 ) FetchContent_MakeAvailable(rpc-interface) diff --git a/tools/projmgr/include/ProjMgrRpcServerData.h b/tools/projmgr/include/ProjMgrRpcServerData.h index 584cc2ef9..ecda26d46 100644 --- a/tools/projmgr/include/ProjMgrRpcServerData.h +++ b/tools/projmgr/include/ProjMgrRpcServerData.h @@ -9,6 +9,7 @@ #include "RpcInterface.h" #include +#include using namespace std; @@ -18,16 +19,28 @@ class RteComponent; class RteComponentInstance; class RteComponentAggregate; class RteComponentGroup; +class RteDevice; +class RteBoard; class RteItem; +class RteModel; class RpcDataCollector { public: - RpcDataCollector(RteTarget* t); + RpcDataCollector(RteTarget* t, RteModel* m = nullptr); RteTarget* GetTarget() const { return m_target; } void CollectCtClasses(RpcArgs::CtRoot& ctRoot) const; void CollectUsedItems(RpcArgs::UsedItems& usedItems) const; + void CollectDeviceList(RpcArgs::DeviceList& deviceList, const std::string& namePattern, const std::string& vendor) const; + void CollectDeviceInfo(RpcArgs::DeviceInfo& deviceInfo, const std::string& id) const; + + void CollectBoardList(RpcArgs::BoardList& boardList, const std::string& namePattern, const std::string& vendor) const; + void CollectBoardInfo(RpcArgs::BoardInfo& boardInfo, const std::string& id) const; + + RpcArgs::Device FromRteDevice(RteDevice* rteDevice, bool bIncludeProperties) const; + RpcArgs::Board FromRteBoard(RteBoard* rteBoard, bool bIncludeProperties) const; + RpcArgs::Component FromRteComponent(const RteComponent* rteComponent) const; RpcArgs::ComponentInstance FromComponentInstance(const RteComponentInstance* rteCi) const; RteItem* GetTaxonomyItem(const RteComponentGroup* rteGroup) const; @@ -36,13 +49,15 @@ class RpcDataCollector { std::string ResultStringFromRteItem(const RteItem* item) const; protected: + void CollectBoardDevices(vector& boardDevices, RteBoard* rteBoard, bool bInstalled, std::list& processedDevices) const; void CollectCtBundles(RpcArgs::CtClass& ctClass, RteComponentGroup* rteClass) const; void CollectCtChildren(RpcArgs::CtTreeItem& parent, RteComponentGroup* rteGroup, const string& bundleName) const; void CollectCtAggregates(RpcArgs::CtTreeItem& parent, RteComponentGroup* rteGroup, const string& bundleName) const; void CollectCtVariants(RpcArgs::CtAggregate& ctAggregate, RteComponentAggregate* rteAggregate) const; private: - RteTarget* m_target; + RteTarget* m_target; // target for context-specific data + RteModel* m_model; // RTE model for global data }; #endif // PROJMGRRPCSERVERDATA_ diff --git a/tools/projmgr/include/ProjMgrRunDebug.h b/tools/projmgr/include/ProjMgrRunDebug.h index d597ddb88..68ec244d5 100644 --- a/tools/projmgr/include/ProjMgrRunDebug.h +++ b/tools/projmgr/include/ProjMgrRunDebug.h @@ -56,7 +56,7 @@ struct FilesType { std::string info; std::string type; std::string load; - std::string offset; + std::string offset; std::string pname; }; @@ -217,7 +217,6 @@ class ProjMgrRunDebug { const RteItem* item, const std::string pname); void AddGeneratedImage(const ContextItem* context, const std::string& filename, const std::string& type, const std::string& load); void AddGeneratedImages(const ContextItem* context); - const std::string GetAccessAttributes(const RteItem* mem); void SetAccessPorts(std::vector& parent, const std::map>& childrenMap); void SetProtNodes(const RteDeviceProperty* item, AccessPortType& ap); diff --git a/tools/projmgr/src/ProjMgrRpcServer.cpp b/tools/projmgr/src/ProjMgrRpcServer.cpp index 311b91d23..87b1bdf3b 100644 --- a/tools/projmgr/src/ProjMgrRpcServer.cpp +++ b/tools/projmgr/src/ProjMgrRpcServer.cpp @@ -84,6 +84,10 @@ class RpcHandler : public RpcMethods { RpcArgs::SuccessResult LoadSolution(const string& solution) override; RpcArgs::UsedItems GetUsedItems(const string& context) override; RpcArgs::PacksInfo GetPacksInfo(const string& context) override; + RpcArgs::DeviceList GetDeviceList(const string& context, const string& namePattern, const string& vendor) override; + RpcArgs::DeviceInfo GetDeviceInfo(const string& id) override; + RpcArgs::BoardList GetBoardList(const string& context, const string& namePattern, const string& vendor) override; + RpcArgs::BoardInfo GetBoardInfo(const string& id) override; RpcArgs::CtRoot GetComponentsTree(const string& context, const bool& all) override; RpcArgs::SuccessResult SelectComponent(const string& context, const string& id, const int& count, const RpcArgs::Options& options) override; RpcArgs::SuccessResult SelectVariant(const string& context, const string& aggregateId, const string& variantName) override; @@ -109,7 +113,6 @@ class RpcHandler : public RpcMethods { ProjMgrRpcServer& m_server; ProjMgr& m_manager; ProjMgrWorker& m_worker; - ContextItem m_globalContext; bool m_packsLoaded = false; bool m_solutionLoaded = false; bool m_bUseAllPacks = false; @@ -226,12 +229,38 @@ RpcArgs::SuccessResult RpcHandler::Resolve(const string& context) { return result; } -RpcArgs::UsedItems RpcHandler::GetUsedItems(const string& context) { - RpcArgs::UsedItems usedItems; - usedItems.success = true; - RpcDataCollector dc(GetActiveTarget(context)); - dc.CollectUsedItems(usedItems); - return usedItems; +RpcArgs::PacksInfo RpcHandler::GetPacksInfo(const string& context) { + const auto contextItem = GetContext(context); + map> packRefs; + for (const auto& packItem : contextItem.packRequirements) { + if (!packItem.origin.empty()) { + const auto packId = RtePackage::ComposePackageID(packItem.pack.vendor, packItem.pack.name, packItem.pack.version); + CollectionUtils::PushBackUniquely(packRefs[packId], packItem.origin); + } + } + RpcArgs::PacksInfo packsInfo; + for (auto& [pack, packItem] : contextItem.rteActiveTarget->GetFilteredModel()->GetPackages()) { + RpcArgs::Pack p; + p.id = packItem->GetPackageID(true); + const auto& description = packItem->GetDescription(); + if (!description.empty()) { + p.description = description; + } + string overview = packItem->GetChildAttribute("description", "overview"); + if (!overview.empty()) { + RteFsUtils::NormalizePath(overview, packItem->GetAbsolutePackagePath()); + p.overview = overview; + } + if (contextItem.packages.find(p.id) != contextItem.packages.end()) { + p.used = true; + if (packRefs.find(p.id) != packRefs.end()) { + p.references = packRefs.at(p.id); + } + } + packsInfo.packs.push_back(p); + } + packsInfo.success = true; + return packsInfo; } RpcArgs::SuccessResult RpcHandler::LoadPacks(void) { @@ -267,42 +296,6 @@ RpcArgs::SuccessResult RpcHandler::LoadSolution(const string& solution) { return result; } -RpcArgs::PacksInfo RpcHandler::GetPacksInfo(const string& context) { - const auto contextItem = GetContext(context); - - map> packRefs; - for (const auto& packItem : contextItem.packRequirements) { - if (!packItem.origin.empty()) { - const auto packId = RtePackage::ComposePackageID(packItem.pack.vendor, packItem.pack.name, packItem.pack.version); - CollectionUtils::PushBackUniquely(packRefs[packId], packItem.origin); - } - } - - RpcArgs::PacksInfo packsInfo; - for (auto& [pack, packItem] : contextItem.rteActiveTarget->GetFilteredModel()->GetPackages()) { - RpcArgs::Pack p; - p.id = packItem->GetPackageID(true); - const auto& description = packItem->GetDescription(); - if (!description.empty()) { - p.description = description; - } - string overview = packItem->GetChildAttribute("description", "overview"); - if (!overview.empty()) { - RteFsUtils::NormalizePath(overview, packItem->GetAbsolutePackagePath()); - p.overview = overview; - } - if (contextItem.packages.find(p.id) != contextItem.packages.end()) { - p.used = true; - if (packRefs.find(p.id) != packRefs.end()) { - p.references = packRefs.at(p.id); - } - } - packsInfo.packs.push_back(p); - } - packsInfo.success = true; - return packsInfo; -} - void RpcHandler::StoreSelectedComponents(RteTarget* rteTarget, map& selectedComponents) { auto& selectedAggregates = rteTarget->CollectSelectedComponentAggregates(); @@ -356,6 +349,72 @@ void RpcHandler::UpdateFilter(const string& context, RteTarget* rteTarget, bool } } +RpcArgs::UsedItems RpcHandler::GetUsedItems(const string& context) { + RpcArgs::UsedItems usedItems; + usedItems.success = true; + RpcDataCollector dc(GetActiveTarget(context)); + dc.CollectUsedItems(usedItems); + return usedItems; +} + +RpcArgs::DeviceList RpcHandler::GetDeviceList(const string& context, const string& namePattern, const string& vendor) +{ + RpcArgs::DeviceList deviceList{{false}}; + if (!m_packsLoaded) { + deviceList.message = "Packs must be loaded before accessing device info"; + } else { + RteTarget* rteTarget = context.empty() ? nullptr : GetActiveTarget(context); + RteModel* rteModel = rteTarget ? rteTarget->GetFilteredModel() : ProjMgrKernel::Get()->GetGlobalModel(); + RpcDataCollector dc(rteTarget, rteModel); + dc.CollectDeviceList(deviceList, namePattern, vendor); + deviceList.success = true; + } + return deviceList; +} + +RpcArgs::DeviceInfo RpcHandler::GetDeviceInfo(const string& id) +{ + RpcArgs::DeviceInfo deviceInfo{{false}}; + if (!m_packsLoaded) { + deviceInfo.message = "Packs must be loaded before accessing device info"; + } else { + RpcDataCollector dc(nullptr, ProjMgrKernel::Get()->GetGlobalModel()); + dc.CollectDeviceInfo(deviceInfo, id); + } + return deviceInfo; +} + +RpcArgs::BoardList RpcHandler::GetBoardList(const string& context, const string& namePattern, const string& vendor) +{ + RpcArgs::BoardList boardList{{false}}; + if (!m_packsLoaded) { + boardList.message = "Packs must be loaded before accessing board info"; + } else { + RteTarget* rteTarget = context.empty() ? nullptr : GetActiveTarget(context); + RteModel* rteModel = rteTarget ? rteTarget->GetFilteredModel() : ProjMgrKernel::Get()->GetGlobalModel(); + RpcDataCollector dc(rteTarget, rteModel); + dc.CollectBoardList(boardList, namePattern, vendor); + boardList.success = true; + } + return boardList; + +} + +RpcArgs::BoardInfo RpcHandler::GetBoardInfo(const string& id) +{ + RpcArgs::BoardInfo boardInfo{{false}}; + if (!m_packsLoaded) { + boardInfo.message = "Packs must be loaded before accessing board info"; + } else { + RpcDataCollector dc(nullptr, ProjMgrKernel::Get()->GetGlobalModel()); + dc.CollectBoardInfo(boardInfo, id); + } + return boardInfo; +} + + + + RpcArgs::CtRoot RpcHandler::GetComponentsTree(const string& context, const bool& all) { RteTarget* rteTarget = GetActiveTarget(context); UpdateFilter(context, rteTarget, all); @@ -366,6 +425,7 @@ RpcArgs::CtRoot RpcHandler::GetComponentsTree(const string& context, const bool& ctRoot.success = true; return ctRoot; } + RpcArgs::SuccessResult RpcHandler::SelectComponent(const string& context, const string& id, const int& count, const RpcArgs::Options& options) { // first try full component ID RteTarget* activeTarget = GetActiveTarget(context); diff --git a/tools/projmgr/src/ProjMgrRpcServerData.cpp b/tools/projmgr/src/ProjMgrRpcServerData.cpp index 98b6b2387..ab67e5861 100644 --- a/tools/projmgr/src/ProjMgrRpcServerData.cpp +++ b/tools/projmgr/src/ProjMgrRpcServerData.cpp @@ -12,9 +12,13 @@ #include "RteProject.h" #include "RteTarget.h" #include "RteModel.h" +#include "RteConstants.h" #include "CollectionUtils.h" +#include +using namespace std; + using namespace RpcArgs; template @@ -32,8 +36,9 @@ T FromRteItem(const string& id, RteItem* rteItem) { return e; } -RpcDataCollector::RpcDataCollector(RteTarget* t) : - m_target(t) +RpcDataCollector::RpcDataCollector(RteTarget* t, RteModel* m) : + m_target(t), + m_model(t? t->GetFilteredModel() : m) { } @@ -150,6 +155,173 @@ void RpcDataCollector::CollectUsedItems(RpcArgs::UsedItems& usedItems) const { } } +RpcArgs::Device RpcDataCollector::FromRteDevice( RteDevice* rteDevice, bool bIncludeProperties) const { + RpcArgs::Device d; + d.id = rteDevice->GetVendorName() + RteConstants::SUFFIX_CVENDOR + rteDevice->GetName(); + d.family = rteDevice->GetEffectiveAttribute("Dfamily"); + d.subFamily = rteDevice->GetEffectiveAttribute("DsubFamily"); + d.pack = rteDevice->GetPackageID(); + if(bIncludeProperties) { + string description; + auto descr = rteDevice->GetAllEffectiveProperties("description"); + for(auto descr : rteDevice->GetAllEffectiveProperties("description")) { + description += descr->GetText() + "\n"; + } + if(!description.empty()) { + d.description = description; + } + vector processors; + for(auto [pname, rteProc] : rteDevice->GetProcessors()) { + RpcArgs::Processor p; + p.name = pname; + p.core = rteProc->GetAttribute("Dcore"); + p.attributes = rteProc->GetAttributes(); + processors.push_back(p); + } + d.processors = processors; + auto mems = rteDevice->GetAllEffectiveProperties("memory"); + vector memories; + for(auto rteMem : rteDevice->GetAllEffectiveProperties("memory")) { + RpcArgs::Memory m; + m.name = rteMem->GetName(); + m.size = rteMem->GetAttribute("size"); + m.access = rteMem->GetAccessPermissions(); + memories.push_back(m); + } + d.memories = memories; + } + return d; +} + +RpcArgs::Board RpcDataCollector::FromRteBoard( RteBoard* rteBoard, bool bIncludeProperties) const { + RpcArgs::Board b; + b.id = rteBoard->GetVendorString() + "::" + rteBoard->GetName(); + const string& rev = rteBoard->GetRevision(); + if(!rev.empty()) { + b.id += ":" + rev; + } + b.pack = rteBoard->GetPackageID(); + if(bIncludeProperties) { + b.description = rteBoard->GetDescription(); + auto imageItem = rteBoard->GetFirstChild("image"); + if(imageItem) { + auto imageFile = imageItem->GetAttribute("small"); + if(imageFile.empty()) { + imageFile = imageItem->GetAttribute("large"); + } + if(!imageFile.empty()) { + b.image = rteBoard->GetOriginalAbsolutePath(imageFile); + } + } + Collection mems; + vector memories; + for(auto rteMem : rteBoard->GetMemories(mems)) { + RpcArgs::Memory m; + m.name = rteMem->GetName(); + m.size = rteMem->GetAttribute("size"); + m.access = rteMem->GetAccessPermissions(); + memories.push_back(m); + } + if(!memories.empty()) { + b.memories = memories; + } + vector devices; + list processedDevices; + CollectBoardDevices(devices, rteBoard, true, processedDevices); + CollectBoardDevices(devices, rteBoard, false, processedDevices); + if(!devices.empty()) { + b.devices = devices; + } + } + return b; +} + +void RpcDataCollector::CollectBoardDevices(vector& boardDevices, RteBoard* rteBoard, + bool bInstalled, list& processedDevices) const { + list refDevices; + rteBoard->GetDevices(refDevices, !bInstalled, bInstalled); + for(auto rteItem : refDevices) { + RteDevice* rteDevice = m_model->GetDevice(rteItem->GetDeviceName(), rteItem->GetDeviceVendor()); + if(rteDevice && find(processedDevices.begin(), processedDevices.end(), rteDevice) == processedDevices.end()) { + processedDevices.push_back(rteDevice); + boardDevices.push_back(FromRteDevice(rteDevice, bInstalled)); + } + } +} + + +void RpcDataCollector::CollectDeviceList(RpcArgs::DeviceList& deviceList, const std::string& namePattern, const std::string& vendor) const { + list devices; + if(m_model) { + m_model->GetDevices(devices, namePattern, vendor); + } + for(auto rteDevice : devices) { + deviceList.devices.push_back(FromRteDevice(rteDevice, false)); + } +} + +void RpcDataCollector::CollectDeviceInfo(RpcArgs::DeviceInfo& deviceInfo, const std::string& id) const { + string name = RteUtils::StripPrefix(id, RteConstants::SUFFIX_CVENDOR); + string vendor = RteUtils::ExtractPrefix(id, RteConstants::SUFFIX_CVENDOR); + if(name.empty()) { + deviceInfo.success = false; + deviceInfo.message = "Invalid device ID: '" + id + "'"; + return; + } + + RteDevice* rteDevice = m_model? m_model->GetDevice(name, vendor) : nullptr; + if(rteDevice) { + deviceInfo.device = FromRteDevice(rteDevice, true); + deviceInfo.success = true; + } else { + deviceInfo.success = false; + deviceInfo.message = "Device '" + id + "' not found"; + return; + } + deviceInfo.device = FromRteDevice(rteDevice, true); +} + +void RpcDataCollector::CollectBoardList(RpcArgs::BoardList& boardList, const std::string& namePattern, const std::string& vendor) const { + if(!m_model) { + return; + } + auto& boards = m_model->GetBoards(); + for(auto [name, rteBoard] : boards) { + if(vendor.empty() || rteBoard->GetVendorString() == vendor) { + if(namePattern.empty() || WildCards::MatchToPattern(name, namePattern)) { + boardList.boards.push_back(FromRteBoard(rteBoard, false)); + } + } + } +} + +void RpcDataCollector::CollectBoardInfo(RpcArgs::BoardInfo& boardInfo, const std::string& id) const { + string displayName = RteUtils::StripPrefix(id, RteConstants::SUFFIX_CVENDOR); + string vendor = RteUtils::ExtractPrefix(id, RteConstants::SUFFIX_CVENDOR); + string name = RteUtils::GetPrefix(displayName, ':'); + string revision = RteUtils::GetSuffix(displayName, ':'); + if(!revision.empty()) { + displayName = name + " (" + revision + ")"; + } + + if(name.empty()) { + boardInfo.success = false; + boardInfo.message = "Invalid board ID: '" + id + "'"; + return; + } + + RteBoard* rteBoard = m_model? m_model->FindBoard(displayName) : nullptr; + if(rteBoard) { + boardInfo.board = FromRteBoard(rteBoard, true); + boardInfo.success = true; + } else { + boardInfo.success = false; + boardInfo.message = "Board '" + id + "' not found"; + return; + } + boardInfo.board = FromRteBoard(rteBoard, true); +} + void RpcDataCollector::CollectCtClasses(RpcArgs::CtRoot& root) const { diff --git a/tools/projmgr/src/ProjMgrRunDebug.cpp b/tools/projmgr/src/ProjMgrRunDebug.cpp index 7b27be58e..d1124d2b3 100644 --- a/tools/projmgr/src/ProjMgrRunDebug.cpp +++ b/tools/projmgr/src/ProjMgrRunDebug.cpp @@ -96,14 +96,14 @@ bool ProjMgrRunDebug::CollectSettings(const vector& contexts, cons // if not found, use ramstart/size from other algorithm in DFP RamType defaultRam; for (const auto& [memory, _] : memories) { - if (memory->GetAttributeAsBool("default") && GetAccessAttributes(memory).find("rwx") == 0) { + if (memory->GetAttributeAsBool("default") && memory->GetAccessPermissions().find("rwx") == 0) { defaultRam.start = memory->GetAttributeAsULL("start"); defaultRam.size = memory->GetAttributeAsULL("size"); defaultRam.pname = memory->GetProcessorName(); break; } } - if (defaultRam.size == 0) { + if (defaultRam.size == 0) { for (const auto& [algorithm, _] : algorithms) { if (algorithm->HasAttribute("RAMsize")) { defaultRam.start = algorithm->GetAttributeAsULL("RAMstart"); @@ -150,7 +150,7 @@ bool ProjMgrRunDebug::CollectSettings(const vector& contexts, cons if (style != "Keil" && style != "CMSIS") { continue; } - } + } AlgorithmType item; item.algorithm = algorithm->GetOriginalAbsolutePath(); item.start = algorithm->GetAttributeAsULL("start"); @@ -171,7 +171,7 @@ bool ProjMgrRunDebug::CollectSettings(const vector& contexts, cons for (const auto& [memory, _] : memories) { MemoryType item; item.name = memory->GetName(); - item.access = GetAccessAttributes(memory); + item.access = memory->GetAccessPermissions(); item.alias = memory->GetAlias(); item.start = memory->GetAttributeAsULL("start"); item.size = memory->GetAttributeAsULL("size"); @@ -617,16 +617,6 @@ void ProjMgrRunDebug::PushBackUniquely(vectorGetAccess(); - if (access.empty()) { - RteItem m = *mem; - access = string(m.IsReadAccess() ? "r" : "") + (m.IsWriteAccess() ? "w" : "") + (m.IsExecuteAccess() ? "x" : ""); - } - return access; -} - bool ProjMgrRunDebug::GetDebugAdapter(const string& name, const DebugAdaptersItem& adapters, DebugAdapterItem& match) { for (const auto& adapter : adapters) { auto aliases = adapter.alias; diff --git a/tools/projmgr/test/src/ProjMgrRpcTests.cpp b/tools/projmgr/test/src/ProjMgrRpcTests.cpp index 99ae37fc0..a5c4014e2 100644 --- a/tools/projmgr/test/src/ProjMgrRpcTests.cpp +++ b/tools/projmgr/test/src/ProjMgrRpcTests.cpp @@ -43,22 +43,26 @@ string ProjMgrRpcTests::FormatRequest(const int id, const string& method, const string ProjMgrRpcTests::CreateLoadRequests(const string& solution, const vector& contextList) { - auto csolutionPath = testinput_folder + solution; - if(!contextList.empty()) { - YAML::Node cbuildset; - cbuildset["cbuild-set"]["generated-by"] = "ProjMrgUnitTests"; - for(const auto& context : contextList) { - cbuildset["cbuild-set"]["contexts"].push_back(map{ { "context", context } }); + string loadSolutionRequest; + if(!solution.empty()) { + auto csolutionPath = testinput_folder + solution; + loadSolutionRequest = FormatRequest(2, "LoadSolution", json({{ "solution", csolutionPath }})); + if(!contextList.empty()) { + YAML::Node cbuildset; + cbuildset["cbuild-set"]["generated-by"] = "ProjMrgUnitTests"; + for(const auto& context : contextList) { + cbuildset["cbuild-set"]["contexts"].push_back(map{ { "context", context } }); + } + auto cbuildsetPath = csolutionPath; + RteUtils::ReplaceAll(cbuildsetPath, ".csolution.yml", ".cbuild-set.yml"); + + ofstream cbuildsetFile; + cbuildsetFile.open(cbuildsetPath, fstream::trunc); + cbuildsetFile << cbuildset << std::endl; + cbuildsetFile.close(); } - auto cbuildsetPath = csolutionPath; - RteUtils::ReplaceAll(cbuildsetPath, ".csolution.yml", ".cbuild-set.yml"); - - ofstream cbuildsetFile; - cbuildsetFile.open(cbuildsetPath, fstream::trunc); - cbuildsetFile << cbuildset << std::endl; - cbuildsetFile.close(); } - return FormatRequest(1, "LoadPacks") + FormatRequest(2, "LoadSolution", json({ { "solution", csolutionPath } })); + return FormatRequest(1, "LoadPacks") + loadSolutionRequest; } vector ProjMgrRpcTests::RunRpcMethods(const string& strIn) { @@ -131,7 +135,7 @@ TEST_F(ProjMgrRpcTests, RpcLoadUndefinedSolution) { } TEST_F(ProjMgrRpcTests, RpcLoadNotSolution) { - const auto& requests = CreateLoadRequests("/TestRpc/undefined.yml"); + const auto& requests = CreateLoadRequests("/TestRpc/undefined.yml"); const auto& responses = RunRpcMethods(requests); EXPECT_TRUE(responses[0]["result"]["success"]); EXPECT_FALSE(responses[1]["result"]["success"]); @@ -148,6 +152,275 @@ TEST_F(ProjMgrRpcTests, RpcLoadSolutionNoPacks) { EXPECT_EQ(msg, "Packs must be loaded before loading solution"); } +TEST_F(ProjMgrRpcTests, RpcDeviceListNoPacks) { + const auto requests = FormatRequest(1, "GetDeviceList", json({{"context", ""},{ "namePattern", ""}, {"vendor", ""}})) + + FormatRequest(2, "GetDeviceInfo", json({{ "id", "ARM::RteTest_ARMCM0"}})); + const auto& responses = RunRpcMethods(requests); + EXPECT_FALSE(responses[0]["result"]["success"]); + string msg = responses[0]["result"]["message"]; + EXPECT_EQ(msg, "Packs must be loaded before accessing device info"); + EXPECT_FALSE(responses[1]["result"]["success"]); + msg = responses[1]["result"]["message"]; + EXPECT_EQ(msg, "Packs must be loaded before accessing device info"); +} + +TEST_F(ProjMgrRpcTests, RpcDeviceListNoContext) { + auto requests = CreateLoadRequests(""); // no solution, no context + // all devices + requests += FormatRequest(2, "GetDeviceList", json({{"context", ""},{ "namePattern", ""}, {"vendor", ""}})); + // filtered devices + requests += FormatRequest(3, "GetDeviceList", json({{"context", ""},{ "namePattern", "*CM0"}, {"vendor", "ARM"}})); + // filtered devices wrong vendor + requests += FormatRequest(4, "GetDeviceList", json({{"context", ""},{ "namePattern", ""}, {"vendor", "foo"}})); + + const auto responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]["success"]); + + EXPECT_TRUE(responses[1]["result"]["success"]); + auto deviceList = responses[1]["result"]["devices"]; + EXPECT_EQ(deviceList.size(), 7); + auto d0 = deviceList[0]; + EXPECT_EQ(d0["id"], "ARM::RteTest_ARMCM0"); + EXPECT_EQ(d0["family"], "RteTest ARM Cortex M"); + EXPECT_EQ(d0["subFamily"], "RteTest ARM Cortex M0"); + EXPECT_EQ(d0["pack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_FALSE(d0.contains("description")); + EXPECT_FALSE(d0.contains("processors")); + EXPECT_FALSE(d0.contains("memories")); + + EXPECT_TRUE(responses[2]["result"]["success"]); + deviceList = responses[2]["result"]["devices"]; + EXPECT_EQ(deviceList.size(), 2); + auto d1 = deviceList[1]; + EXPECT_EQ(d1["id"], "ARM::RteTestGen_ARMCM0"); + EXPECT_EQ(d1["family"], "RteTestGen ARM Cortex M"); + EXPECT_EQ(d1["subFamily"], "RteTestGen ARM Cortex M0"); + EXPECT_EQ(d1["pack"], "ARM::RteTestGenerator@0.1.0"); + + EXPECT_TRUE(responses[3]["result"]["success"]); + deviceList = responses[3]["result"]["devices"]; + EXPECT_EQ(deviceList.size(), 0); +} + +TEST_F(ProjMgrRpcTests, RpcDeviceListContext) { + string context = "selectable+CM0"; + vector contextList = { + context + }; + auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", contextList); + // all devices + requests += FormatRequest(3, "GetDeviceList", json({{"context", "selectable+CM0"},{ "namePattern", ""}, {"vendor", ""}})); + requests += FormatRequest(4, "GetDeviceList", json({{"context", "selectable+CM0"},{ "namePattern", "*Dual*"}, {"vendor", ""}})); + const auto responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_TRUE(responses[1]["result"]["success"]); + + EXPECT_TRUE(responses[2]["result"]["success"]); + auto deviceList = responses[2]["result"]["devices"]; + EXPECT_EQ(deviceList.size(), 6); + auto d0 = deviceList[0]; + EXPECT_EQ(d0["id"], "ARM::RteTest_ARMCM0"); + + EXPECT_TRUE(responses[3]["result"]["success"]); + deviceList = responses[3]["result"]["devices"]; + EXPECT_EQ(deviceList.size(), 1); + d0 = deviceList[0]; + EXPECT_EQ(d0["id"], "ARM::RteTest_ARMCM0_Dual"); +} + +TEST_F(ProjMgrRpcTests, RpcDeviceInfo) { + auto requests = CreateLoadRequests(""); // no solution, no context + // device with name and vendor + requests += FormatRequest(2, "GetDeviceInfo", json({{ "id", "ARM::RteTest_ARMCM0_Dual"}})); + // device with name, no vendor + requests += FormatRequest(3, "GetDeviceInfo", json({{ "id", "RteTest_ARMCM0_Dual"}})); + // device with name, wrong vendor + requests += FormatRequest(4, "GetDeviceInfo", json({{ "id", "foo::RteTest_ARMCM0"}})); + // device with wrong name, no vendor + requests += FormatRequest(5, "GetDeviceInfo", json({{ "id", "RteTest_Unknown"}})); + // empty id + requests += FormatRequest(6, "GetDeviceInfo", json({{ "id", ""}})); + // only vendor + requests += FormatRequest(7, "GetDeviceInfo", json({{ "id", "ARM::"}})); + + const auto responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_TRUE(responses[1]["result"]["success"]); + EXPECT_TRUE(responses[2]["result"]["success"]); + EXPECT_FALSE(responses[3]["result"]["success"]); + EXPECT_EQ("Device 'foo::RteTest_ARMCM0' not found", responses[3]["result"]["message"]); + EXPECT_FALSE(responses[4]["result"]["success"]); + EXPECT_EQ("Device 'RteTest_Unknown' not found", responses[4]["result"]["message"]); + EXPECT_FALSE(responses[5]["result"]["success"]); + EXPECT_EQ("Invalid device ID: ''", responses[5]["result"]["message"]); + EXPECT_FALSE(responses[6]["result"]["success"]); + EXPECT_EQ("Invalid device ID: 'ARM::'", responses[6]["result"]["message"]); + + auto d1 = responses[1]["result"]["device"]; + EXPECT_EQ(d1["id"], "ARM::RteTest_ARMCM0_Dual"); + EXPECT_EQ(d1["family"], "RteTest ARM Cortex M"); + EXPECT_EQ(d1["subFamily"], "RteTest ARM Cortex M0"); + EXPECT_EQ(d1["pack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_TRUE(d1.contains("description")); + EXPECT_FALSE(d1["description"].empty()); + EXPECT_EQ(d1["processors"].size(), 2); + auto p0 = d1["processors"][0]; + EXPECT_EQ(p0["name"], "cm0_core0"); + EXPECT_EQ(p0["core"], "Cortex-M0"); + RpcArgs::Processor proc; + from_json(p0, proc); + EXPECT_TRUE(proc.attributes.has_value()); + XmlItem attributes(proc.attributes.value()); + EXPECT_EQ(attributes.GetAttributesString(), + "Dclock=10000000 Dcore=Cortex-M0 DcoreVersion=r0p0 Dendian=Configurable Dfpu=NO_FPU Dmpu=NO_MPU Pname=cm0_core0"); + + EXPECT_EQ(d1["memories"].size(), 4); + auto m0 = d1["memories"][0]; + EXPECT_EQ(m0["name"], "FLASH_DUAL"); + EXPECT_EQ(m0["size"], "0x00080000"); + EXPECT_EQ(m0["access"], "rx"); + + d1 = responses[2]["result"]["device"]; + EXPECT_EQ(d1["id"], "ARM::RteTest_ARMCM0_Dual"); +} + +TEST_F(ProjMgrRpcTests, RpcBoardListNoPacks) { + const auto requests = FormatRequest(1, "GetBoardList", json({{"context", ""},{ "namePattern", ""}, {"vendor", ""}})) + + FormatRequest(2, "GetBoardInfo", json({{ "id", "ARM::RteTest_ARMCM0"}})); + const auto& responses = RunRpcMethods(requests); + EXPECT_FALSE(responses[0]["result"]["success"]); + string msg = responses[0]["result"]["message"]; + EXPECT_EQ(msg, "Packs must be loaded before accessing board info"); + EXPECT_FALSE(responses[1]["result"]["success"]); + msg = responses[1]["result"]["message"]; + EXPECT_EQ(msg, "Packs must be loaded before accessing board info"); +} + +TEST_F(ProjMgrRpcTests, RpcBoardListNoContext) { + auto requests = CreateLoadRequests(""); // no solution, no context + // all boards + requests += FormatRequest(2, "GetBoardList", json({{"context", ""},{ "namePattern", ""}, {"vendor", ""}})); + // filtered boards + requests += FormatRequest(3, "GetBoardList", json({{"context", ""},{ "namePattern", "*CM4*"}, {"vendor", "Keil"}})); + // filtered boards wrong vendor + requests += FormatRequest(4, "GetBoardList", json({{"context", ""},{ "namePattern", ""}, {"vendor", "foo"}})); + + const auto responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]["success"]); + + EXPECT_TRUE(responses[1]["result"]["success"]); + auto boardList = responses[1]["result"]["boards"]; + EXPECT_EQ(boardList.size(), 14); + auto b0 = boardList[0]; + EXPECT_EQ(b0["id"], "Keil::RteTest board listing:Rev.C"); + EXPECT_EQ(b0["pack"], "ARM::RteTestBoard@0.1.0"); + EXPECT_FALSE(b0.contains("description")); + EXPECT_FALSE(b0.contains("devices")); + EXPECT_FALSE(b0.contains("memories")); + + EXPECT_TRUE(responses[2]["result"]["success"]); + boardList = responses[2]["result"]["boards"]; + EXPECT_EQ(boardList.size(), 1); + b0 = boardList[0]; + EXPECT_EQ(b0["id"], "Keil::RteTest CM4 board:Rev.C"); + EXPECT_EQ(b0["pack"], "ARM::RteTestBoard@0.1.0"); + + EXPECT_TRUE(responses[3]["result"]["success"]); + boardList = responses[3]["result"]["boards"]; + EXPECT_EQ(boardList.size(), 0); +} + +TEST_F(ProjMgrRpcTests, RpcBoardListContext) { + + string context = "selectable+CM0"; + vector contextList = { + context + }; + auto requests = CreateLoadRequests("/Validation/dependencies.csolution.yml", contextList); + + // all boards + requests += FormatRequest(2, "GetBoardList", json({{"context", "selectable+CM0"},{ "namePattern", ""}, {"vendor", ""}})); + + const auto responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_TRUE(responses[1]["result"]["success"]); + + EXPECT_TRUE(responses[2]["result"]["success"]); + auto boardList = responses[2]["result"]["boards"]; + EXPECT_EQ(boardList.size(), 11); + auto b0 = boardList[0]; + EXPECT_EQ(b0["id"], "Keil::RteTest board test revision:Rev1"); + EXPECT_EQ(b0["pack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_FALSE(b0.contains("description")); + EXPECT_FALSE(b0.contains("devices")); + EXPECT_FALSE(b0.contains("memories")); +} + +TEST_F(ProjMgrRpcTests, RpcBoardInfo) { + auto requests = CreateLoadRequests(""); // no solution, no context + // board with name, vendor and revision + requests += FormatRequest(2, "GetBoardInfo", json({{ "id", "Keil::RteTest Test board:1.1.1"}})); + // board with name, no vendor + requests += FormatRequest(3, "GetBoardInfo", json({{ "id", "RteTest Test board:1.1.1"}})); + // board with name, no revision + requests += FormatRequest(4, "GetBoardInfo", json({{ "id", "Keil::RteTest Test board"}})); + // board with memory + requests += FormatRequest(5, "GetBoardInfo", json({{ "id", "RteTest CM4 board:Rev.C" }})); + // no device + requests += FormatRequest(6, "GetBoardInfo", json({{ "id", "RteTest NoMCU board"}})); + // only vendor => no ID + requests += FormatRequest(7, "GetBoardInfo", json({{ "id", "Keil::"}})); + + const auto responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]["success"]); + EXPECT_TRUE(responses[1]["result"]["success"]); + + auto b1 = responses[1]["result"]["board"]; + EXPECT_EQ(b1["id"], "Keil::RteTest Test board:1.1.1"); + EXPECT_EQ(b1["pack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_EQ(b1["description"], "uVision Simulator"); + auto devices = b1["devices"]; + EXPECT_EQ(devices.size(), 4); + EXPECT_FALSE(b1.contains("memories")); + auto d1 = devices[1]; + EXPECT_EQ(d1["id"], "ARM::RteTest_ARMCM0_Dual"); + EXPECT_EQ(d1["processors"].size(), 2); + EXPECT_EQ(d1["processors"][0]["name"], "cm0_core0"); + + auto d2 = devices[2]; + EXPECT_EQ(d2["id"], "ARM::RteTest_ARMCM4_NOFP"); + EXPECT_FALSE(d2.contains("processors")); + + auto b2 = responses[2]["result"]["board"]; + EXPECT_EQ(b2["id"], "Keil::RteTest Test board:1.1.1"); + EXPECT_EQ(b2["pack"], "ARM::RteTest_DFP@0.2.0"); + EXPECT_EQ(b2["description"], "uVision Simulator"); + + EXPECT_FALSE(responses[3]["result"]["success"]); + EXPECT_EQ(responses[3]["result"]["message"], "Board 'Keil::RteTest Test board' not found"); + + auto b4 = responses[4]["result"]["board"]; + EXPECT_EQ(b4["id"], "Keil::RteTest CM4 board:Rev.C"); + EXPECT_EQ(b4["pack"], "ARM::RteTestBoard@0.1.0"); + EXPECT_EQ(b4["description"], "uVision Simulator"); + EXPECT_TRUE(b4.contains("memories")); + EXPECT_EQ(b4["memories"][0]["name"], "BoardFLASH"); + + auto b5 = responses[5]["result"]["board"]; + EXPECT_EQ(b5["id"], "Keil::RteTest NoMCU board"); + EXPECT_EQ(b5["pack"], "ARM::RteTestBoard@0.1.0"); + EXPECT_EQ(b5["description"], "No device board"); + EXPECT_TRUE(b5.contains("memories")); + EXPECT_EQ(b5["memories"][1]["name"], "BoardRAM"); + EXPECT_FALSE(b5.contains("devices")); + + EXPECT_FALSE(responses[6]["result"]["success"]); + EXPECT_EQ(responses[6]["result"]["message"], "Invalid board ID: 'Keil::'"); +} + +///////// +// components +///////// TEST_F(ProjMgrRpcTests, RpcLoadSolution_UnknownComponent) { const string& csolution = testinput_folder + "/TestRpc/unknown-component.csolution.yml"; auto requests = CreateLoadRequests("/TestRpc/unknown-component.csolution.yml") + From 3c513031a8435ba1d428c72a9d52f484171bb4d3 Mon Sep 17 00:00:00 2001 From: Evgueni Driouk Date: Tue, 29 Jul 2025 16:10:20 +0200 Subject: [PATCH 2/2] Update ProjMgrRpcServerData.cpp Remove redudant line --- tools/projmgr/src/ProjMgrRpcServerData.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/projmgr/src/ProjMgrRpcServerData.cpp b/tools/projmgr/src/ProjMgrRpcServerData.cpp index ab67e5861..ed940088f 100644 --- a/tools/projmgr/src/ProjMgrRpcServerData.cpp +++ b/tools/projmgr/src/ProjMgrRpcServerData.cpp @@ -163,7 +163,6 @@ RpcArgs::Device RpcDataCollector::FromRteDevice( RteDevice* rteDevice, bool bInc d.pack = rteDevice->GetPackageID(); if(bIncludeProperties) { string description; - auto descr = rteDevice->GetAllEffectiveProperties("description"); for(auto descr : rteDevice->GetAllEffectiveProperties("description")) { description += descr->GetText() + "\n"; }