diff --git a/build/scripts/Targets/TargetDescriptionFiles/Services/AtdfService.php b/build/scripts/Targets/TargetDescriptionFiles/Services/AtdfService.php index 55899907..4813dcb5 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Services/AtdfService.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Services/AtdfService.php @@ -1046,7 +1046,8 @@ class AtdfService strtolower($attributes['ordercode'] ?? $attributes['name'] ?? '') ), $attributes['ordercode'] ?? $attributes['name'] ?? null, - isset($attributes['pinout']) ? strtolower($attributes['pinout']) : null + isset($attributes['pinout']) ? strtolower($attributes['pinout']) : null, + [] ); } } diff --git a/build/scripts/Targets/TargetDescriptionFiles/Services/ValidationService.php b/build/scripts/Targets/TargetDescriptionFiles/Services/ValidationService.php index bf9711a5..840e184a 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Services/ValidationService.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Services/ValidationService.php @@ -1039,6 +1039,17 @@ class ValidationService . '" - check pinout key'; } + $processedPropertyGroupKeys = []; + foreach ($variant->propertyGroups as $propertyGroup) { + $failures = array_merge($failures, $this->validatePropertyGroup($propertyGroup)); + + if ($propertyGroup->key !== null && in_array($propertyGroup->key, $processedPropertyGroupKeys)) { + $failures[] = 'Duplicate property group key ("' . $propertyGroup->key . '") detected'; + } + + $processedPropertyGroupKeys[] = $propertyGroup->key; + } + return array_map( fn (string $failure): string => 'Variant "' . $variant->name . '" validation failure: ' . $failure, $failures diff --git a/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/FromXmlService.php b/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/FromXmlService.php index 9bf0052e..7a57ab7c 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/FromXmlService.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/FromXmlService.php @@ -450,10 +450,32 @@ class FromXmlService { $attributes = $this->getNodeAttributesByName($element); - return new Variant( + $output = new Variant( $attributes['key'] ?? null, $attributes['name'] ?? null, - $attributes['pinout-key'] ?? null + $attributes['pinout-key'] ?? null, + [] ); + + $propertyGroupsElements = $element->getElementsByTagName('property-groups'); + if ( + $propertyGroupsElements->count() === 1 + && ($propertyGroupsElement = $propertyGroupsElements->item(0)) instanceof DOMElement + ) { + foreach ($propertyGroupsElement->childNodes as $childNode) { + if (!$childNode instanceof DOMElement) { + continue; + } + + if ($childNode->nodeName === 'property-group') { + $output->propertyGroups[] = $this->propertyGroupFromElement($childNode); + } + } + + } elseif ($propertyGroupsElements->count() > 1) { + throw new XmlParsingException('Unexpected number of "property-groups" elements'); + } + + return $output; } } diff --git a/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/ToXmlService.php b/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/ToXmlService.php index f22ed0a0..b21018ec 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/ToXmlService.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/ToXmlService.php @@ -406,6 +406,15 @@ class ToXmlService $element->setAttribute('name', $variant->name); $element->setAttribute('pinout-key', $variant->pinoutKey); + if (!empty($variant->propertyGroups)) { + $propertyGroupsElement = $document->createElement('signals'); + foreach ($variant->propertyGroups as $propertyGroup) { + $propertyGroupsElement->append($this->propertyGroupToXml($propertyGroup, $document)); + } + + $element->append($propertyGroupsElement); + } + return $element; } } diff --git a/build/scripts/Targets/TargetDescriptionFiles/Variant.php b/build/scripts/Targets/TargetDescriptionFiles/Variant.php index e3e3ff78..413375cd 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Variant.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Variant.php @@ -1,16 +1,52 @@ key = $key; $this->name = $name; $this->pinoutKey = $pinoutKey; + $this->propertyGroups = $propertyGroups; + } + + public function getPropertyGroup(array|string $keys): ?PropertyGroup + { + if (is_string($keys)) { + $keys = explode('.', $keys); + } + + $firstLevelGroupKey = array_shift($keys); + foreach ($this->propertyGroups as $propertyGroup) { + if ($propertyGroup->key === $firstLevelGroupKey) { + return !empty($keys) ? $propertyGroup->getSubgroup($keys) : $propertyGroup; + } + } + + return null; + } + + public function getProperty(array|string $propertyGroupKeys, $propertyKey): ?Property + { + return ($propertyGroup = $this->getPropertyGroup($propertyGroupKeys)) instanceof PropertyGroup + ? $propertyGroup->getProperty($propertyKey) + : null; + } + + public function getPropertyValue(array|string $propertyGroupKeys, $propertyKey): ?string + { + return ($property = $this->getProperty($propertyGroupKeys, $propertyKey)) instanceof Property + ? $property->value + : null; } } diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index 787f6cdd..3b434308 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -926,11 +926,23 @@ namespace Targets::TargetDescription } Variant TargetDescriptionFile::variantFromXml(const QDomElement& xmlElement) { - return { + auto output = Variant{ TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), - TargetDescriptionFile::getAttribute(xmlElement, "pinout-key") + TargetDescriptionFile::getAttribute(xmlElement, "pinout-key"), + {} }; + + for ( + auto element = xmlElement.firstChildElement("property-groups").firstChildElement("property-group"); + !element.isNull(); + element = element.nextSiblingElement("property-group") + ) { + auto propertyGroup = TargetDescriptionFile::propertyGroupFromXml(element); + output.propertyGroupsByKey.emplace(propertyGroup.key, std::move(propertyGroup)); + } + + return output; } TargetAddressSpaceDescriptor TargetDescriptionFile::targetAddressSpaceDescriptorFromAddressSpace( diff --git a/src/Targets/TargetDescription/Variant.hpp b/src/Targets/TargetDescription/Variant.hpp index 268aba7a..256c3822 100644 --- a/src/Targets/TargetDescription/Variant.hpp +++ b/src/Targets/TargetDescription/Variant.hpp @@ -1,6 +1,13 @@ #pragma once #include +#include +#include + +#include "PropertyGroup.hpp" + +#include "src/Services/StringService.hpp" +#include "Exceptions/InvalidTargetDescriptionDataException.hpp" namespace Targets::TargetDescription { @@ -10,14 +17,67 @@ namespace Targets::TargetDescription std::string name; std::string pinoutKey; + std::map> propertyGroupsByKey; + Variant( const std::string& key, const std::string& name, - const std::string& pinoutKey + const std::string& pinoutKey, + const std::map>& propertyGroupsByKey ) : key(key) , name(name) , pinoutKey(pinoutKey) + , propertyGroupsByKey(propertyGroupsByKey) {} + + std::optional> tryGetPropertyGroup(std::string_view keyStr) const { + const auto keys = Services::StringService::split(keyStr, '.'); + + const auto firstSubgroupIt = this->propertyGroupsByKey.find(*keys.begin()); + return firstSubgroupIt != this->propertyGroupsByKey.end() + ? keys.size() > 1 + ? firstSubgroupIt->second.tryGetSubgroup(keys | std::ranges::views::drop(1)) + : std::optional{std::cref(firstSubgroupIt->second)} + : std::nullopt; + } + + const PropertyGroup& getPropertyGroup(std::string_view keyStr) const { + const auto propertyGroup = this->tryGetPropertyGroup(keyStr); + + if (!propertyGroup.has_value()) { + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get property group \"" + std::string{keyStr} + "\" from TDF - property group not found" + }; + } + + return propertyGroup->get(); + } + + std::optional> tryGetProperty( + std::string_view groupKey, + std::string_view propertyKey + ) const { + const auto propertyGroup = this->tryGetPropertyGroup(groupKey); + + if (!propertyGroup.has_value()) { + return std::nullopt; + } + + return propertyGroup->get().tryGetProperty(propertyKey); + } + + const Property& getProperty(std::string_view groupKey, std::string_view propertyKey) const { + const auto property = this->tryGetProperty(groupKey, propertyKey); + + if (!property.has_value()) { + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get property \"" + std::string{propertyKey} + "\" from group \"" + std::string{groupKey} + + "\", from TDF - property/group not found" + }; + } + + return property->get(); + } }; }