PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
gsdml.cpp
Go to the documentation of this file.
1
30
31#include "profinet/gsdml.h"
32
33#include <libxml/parser.h>
34#include <libxml/tree.h>
35#include <libxml/xpath.h>
36#include <libxml/xpathInternals.h>
37
38#include <algorithm>
39#include <cctype>
40#include <map>
41#include <ranges>
42#include <sstream>
43#include <stdexcept>
44#include <string_view>
45
46namespace profinet::gsdml
47{
48
49namespace
50{
51
52// =============================================================================
53// libxml2 RAII wrappers
54// =============================================================================
55
56struct XmlDocGuard
57{
58 xmlDocPtr doc = nullptr;
59 ~XmlDocGuard()
60 {
61 if (doc != nullptr)
62 {
63 xmlFreeDoc(doc);
64 }
65 }
66};
67
68struct XPathContextGuard
69{
70 xmlXPathContextPtr ctx = nullptr;
71 ~XPathContextGuard()
72 {
73 if (ctx != nullptr)
74 {
75 xmlXPathFreeContext(ctx);
76 }
77 }
78};
79
80struct XPathObjectGuard
81{
82 xmlXPathObjectPtr obj = nullptr;
83 ~XPathObjectGuard()
84 {
85 if (obj != nullptr)
86 {
87 xmlXPathFreeObject(obj);
88 }
89 }
90};
91
92// =============================================================================
93// Namespace-agnostic query helpers (GSDML files declare a default
94// namespace that varies by vendor/profile version, so we match on
95// local-name() throughout.
96// =============================================================================
97
98std::string EscapeXPathLiteral(const std::string& s)
99{
100 // XPath 1.0 has no literal-escaping, so build via concat() if the
101 // string itself contains a quote. GSDML attribute values here (IDs,
102 // TextIds) never contain quotes in practice, so this is a defensive
103 // fallback rather than a load-bearing path.
104 if (s.find('\'') == std::string::npos)
105 {
106 return "'" + s + "'";
107 }
108 std::ostringstream oss;
109 oss << "concat(";
110 bool first = true;
111 std::string cur;
112 for (char c : s)
113 {
114 if (c == '\'')
115 {
116 if (!first)
117 {
118 oss << ", ";
119 }
120 oss << "'" << cur << "'";
121 cur.clear();
122 oss << ", \"'\"";
123 first = false;
124 }
125 else
126 {
127 cur.push_back(c);
128 }
129 }
130 if (!cur.empty())
131 {
132 if (!first)
133 {
134 oss << ", ";
135 }
136 oss << "'" << cur << "'";
137 }
138 oss << ")";
139 return oss.str();
140}
141
144std::vector<xmlNodePtr> XPathFind(xmlXPathContextPtr ctx, xmlNodePtr node, const std::string& expr)
145{
146 xmlXPathContextPtr currentCtx = ctx;
147 xmlNodePtr savedNode = currentCtx->node;
148 if (node != nullptr)
149 {
150 currentCtx->node = node;
151 }
152
153 XPathObjectGuard guard;
154 guard.obj = xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(expr.c_str()), currentCtx);
155 currentCtx->node = savedNode;
156
157 std::vector<xmlNodePtr> result;
158 if ((guard.obj == nullptr) || (guard.obj->nodesetval == nullptr))
159 {
160 return result;
161 }
162 for (int i = 0; i < guard.obj->nodesetval->nodeNr; ++i)
163 {
164 result.push_back(guard.obj->nodesetval->nodeTab[i]);
165 }
166 return result;
167}
168
170xmlNodePtr FindFirst(xmlXPathContextPtr ctx, xmlNodePtr node, const std::string& tag)
171{
172 auto results = XPathFind(ctx, node, ".//*[local-name()='" + tag + "']");
173 return results.empty() ? nullptr : results.front();
174}
175
177std::vector<xmlNodePtr> FindAll(xmlXPathContextPtr ctx, xmlNodePtr node, const std::string& tag)
178{
179 return XPathFind(ctx, node, ".//*[local-name()='" + tag + "']");
180}
181
183std::vector<xmlNodePtr> FindChildren(xmlXPathContextPtr ctx, xmlNodePtr node, const std::string& tag)
184{
185 return XPathFind(ctx, node, "./*[local-name()='" + tag + "']");
186}
187
188std::string GetAttr(xmlNodePtr node, const std::string& name, const std::string& Default = "")
189{
190 if (node == nullptr)
191 {
192 return Default;
193 }
194 xmlChar* val = xmlGetProp(node, reinterpret_cast<const xmlChar*>(name.c_str()));
195 if (val == nullptr)
196 {
197 return Default;
198 }
199 std::string result(reinterpret_cast<const char*>(val));
200 xmlFree(val);
201 return result;
202}
203
204std::optional<std::string> GetAttrOpt(xmlNodePtr node, const std::string& name)
205{
206 if (node == nullptr)
207 {
208 return std::nullopt;
209 }
210 xmlChar* val = xmlGetProp(node, reinterpret_cast<const xmlChar*>(name.c_str()));
211 if (val == nullptr)
212 {
213 return std::nullopt;
214 }
215 std::string result(reinterpret_cast<const char*>(val));
216 xmlFree(val);
217 return result;
218}
219
220// =============================================================================
221// Value parsing helpers
222// =============================================================================
225bool ParseBool(const std::optional<std::string>& spec, const bool defaultValue = false)
226{
227 if (!spec)
228 {
229 return defaultValue;
230 }
231 std::string str = *spec;
232
233 // Trim left whitespace
234 str.erase(str.begin(), std::ranges::find_if(str, [](unsigned char ch)
235 {
236 return !std::isspace(ch);
237 }));
238
239 // Trim right whitespace
240 str.erase(std::ranges::find_if(std::views::reverse(str), [](unsigned char ch)
241 {
242 return !std::isspace(ch);
243 }).base(),
244 str.end());
245
246 if (str.empty())
247 {
248 return defaultValue;
249 }
250
251 if (str == "1")
252 {
253 return true;
254 }
255 if (str == "0")
256 {
257 return false;
258 }
259
260 // Lowercase conversion for case-insensitive comparison
261 std::ranges::transform(str, str.begin(), [](unsigned char c)
262 {
263 return static_cast<char>(std::tolower(c));
264 });
265
266 if (str == "true")
267 {
268 return true;
269 }
270 if (str == "false")
271 {
272 return false;
273 }
274
275 return defaultValue;
276}
279std::uint32_t ParseInt(const std::optional<std::string>& spec)
280{
281 if (!spec)
282 {
283 return 0;
284 }
285 const std::string& s = *spec;
286 if (s.empty())
287 {
288 return 0;
289 }
290
291 if (s.size() > 3 && s[0] == '#' && s[1] == '1' && s[2] == '6' && s[3] == '#')
292 {
293 return static_cast<std::uint32_t>(std::stoul(s.substr(4), nullptr, 16));
294 }
295 if (s.size() > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
296 {
297 return static_cast<std::uint32_t>(std::stoul(s.substr(2), nullptr, 16));
298 }
299 try
300 {
301 return static_cast<std::uint32_t>(std::stoul(s, nullptr, 10));
302 }
303 catch (const std::exception&)
304 {
305 return 0;
306 }
307}
308
316void ParseBitDataItems(xmlXPathContextPtr ctx, xmlNodePtr dataItemNode, GSDMLDataItem& dataItem, const std::map<std::string, std::string>& textTable)
317{
318 dataItem.useAsBits = ParseBool(GetAttrOpt(dataItemNode, "UseAsBits"));
319
320 for (auto* bitNode : FindChildren(ctx, dataItemNode, "BitDataItem"))
321 {
322 GSDMLBitDataItem bit;
323
324 const auto bitOffset = GetAttrOpt(bitNode, "BitOffset");
325 if (!bitOffset.has_value())
326 {
327 throw std::invalid_argument("GSDML BitDataItem is missing BitOffset");
328 }
329
330 bit.bitOffset = static_cast<std::size_t>(ParseInt(bitOffset));
331 bit.textId = GetAttr(bitNode, "TextId");
332
333 const auto bitCount = static_cast<std::size_t>(dataItem.length) * 8U;
334
335 if (bit.bitOffset >= bitCount)
336 {
337 throw std::invalid_argument(
338 "GSDML BitDataItem offset is outside its DataItem");
339 }
340 const auto textIt = textTable.find(bit.textId);
341
342 bit.name = (textIt != textTable.end())
343 ? textIt->second
344 : bit.textId;
345
346 dataItem.bitDataItems.push_back(std::move(bit));
347 }
348
349 // Be tolerant of a vendor GSDML that contains children without
350 // explicitly setting UseAsBits=true.
351 if (!dataItem.bitDataItems.empty())
352 {
353 dataItem.useAsBits = true;
354 }
355 if (!dataItem.textId.empty())
356 {
357 const auto textIt = textTable.find(dataItem.textId);
358
359 if (textIt != textTable.end())
360 {
361 dataItem.name = textIt->second;
362 }
363 else
364 {
365 dataItem.name = dataItem.textId;
366 }
367 }
368}
369
371std::vector<int> ParseSlotSpec(const std::optional<std::string>& spec)
372{
373 std::vector<int> result;
374 if (!spec || spec->empty())
375 {
376 return result;
377 }
378
379 std::string s = *spec;
380 std::ranges::replace(s, ',', ' ');
381 std::istringstream iss(s);
382 std::string part;
383 while (iss >> part)
384 {
385 auto dotdot = part.find("..");
386 if (dotdot != std::string::npos)
387 {
388 int start = std::stoi(part.substr(0, dotdot));
389 int end = std::stoi(part.substr(dotdot + 2));
390 for (int i = start; i <= end; ++i)
391 {
392 result.push_back(i);
393 }
394 }
395 else
396 {
397 result.push_back(std::stoi(part));
398 }
399 }
400 return result;
401}
402
405const std::map<std::string, int>& TypeSizeTable()
406{
407 static const std::map<std::string, int> table = {
408 {"Bit", 1},
409 {"BitArea", 1},
410 {"Boolean", 1},
411 {"Integer8", 1},
412 {"Unsigned8", 1},
413 {"Integer16", 2},
414 {"Unsigned16", 2},
415 {"Integer32", 4},
416 {"Unsigned32", 4},
417 {"Integer64", 8},
418 {"Unsigned64", 8},
419 {"Float32", 4},
420 {"Float64", 8},
421 {"Date", 2},
422 {"Time", 4},
423 {"TimeOfDay", 4},
424 {"TimeDifference", 4},
425 {"F_MessageTrailer4Byte", 4},
426 {"F_MessageTrailer5Byte", 5},
427 };
428 return table;
429}
430
431int DataItemLength(const std::string& dataType, const std::optional<std::string>& lengthAttr)
432{
433 if (dataType == "OctetString" || dataType == "VisibleString")
434 {
435 return lengthAttr ? static_cast<int>(ParseInt(lengthAttr)) : 0;
436 }
437 auto it = TypeSizeTable().find(dataType);
438 return it != TypeSizeTable().end() ? it->second : 1;
439}
440
441// =============================================================================
442// ExternalTextList resolution
443// =============================================================================
444
446std::map<std::string, std::string> BuildTextTable(xmlXPathContextPtr ctx, xmlNodePtr root)
447{
448 std::map<std::string, std::string> table;
449 for (auto* textNode : FindAll(ctx, root, "Text"))
450 {
451 auto id = GetAttrOpt(textNode, "TextId");
452 auto value = GetAttrOpt(textNode, "Value");
453 if (id && value)
454 {
455 table[*id] = *value;
456 }
457 }
458 return table;
459}
460
462std::string ResolveName(xmlXPathContextPtr ctx, xmlNodePtr parent, const std::map<std::string, std::string>& textTable,
463 const std::string& fallback)
464{
465 xmlNodePtr nameNode = FindFirst(ctx, parent, "Name");
466 if (nameNode == nullptr)
467 {
468 return fallback;
469 }
470
471 if (auto textId = GetAttrOpt(nameNode, "TextId"))
472 {
473 auto it = textTable.find(*textId);
474 if (it != textTable.end())
475 {
476 return it->second;
477 }
478 }
479 if (auto value = GetAttrOpt(nameNode, "Value"))
480 {
481 return *value;
482 }
483 return fallback;
484}
485
487std::string ResolveInfoText(xmlXPathContextPtr ctx, xmlNodePtr parent, const std::map<std::string, std::string>& textTable,
488 const std::string& fallback)
489{
490 xmlNodePtr infoTextNode = FindFirst(ctx, parent, "InfoText");
491 if (infoTextNode == nullptr)
492 {
493 return fallback;
494 }
495
496 if (auto textId = GetAttrOpt(infoTextNode, "TextId"))
497 {
498 auto it = textTable.find(*textId);
499 if (it != textTable.end())
500 {
501 return it->second;
502 }
503 }
504 if (auto value = GetAttrOpt(infoTextNode, "Value"))
505 {
506 return *value;
507 }
508 return fallback;
509}
510
512std::string ResolveVendorName(xmlXPathContextPtr ctx, xmlNodePtr parent, const std::map<std::string, std::string>& textTable,
513 const std::string& fallback)
514{
515 xmlNodePtr vendorNameNode = FindFirst(ctx, parent, "VendorName");
516 if (vendorNameNode == nullptr)
517 {
518 return fallback;
519 }
520
521 if (auto textId = GetAttrOpt(vendorNameNode, "TextId"))
522 {
523 auto it = textTable.find(*textId);
524 if (it != textTable.end())
525 {
526 return it->second;
527 }
528 }
529 if (auto value = GetAttrOpt(vendorNameNode, "Value"))
530 {
531 return *value;
532 }
533 return fallback;
534}
535
536rpc::StartupMode ParseStartupMode(const std::optional<std::string>& mode, char delimiter = ';')
537{
538 if (mode.has_value() && mode->find("Advanced") != std::string::npos)
539 {
541 }
542
544}
545
546std::vector<std::uint16_t> ParseStringRanges(std::optional<std::string_view> input)
547{
548 std::vector<std::uint16_t> result;
549 if (!input.has_value())
550 {
551 return result;
552 }
553
554 for (auto word : input.value() | std::ranges::views::split(' '))
555 {
556 if (word.empty())
557 {
558 continue; // Skip multiple adjacent spaces
559 }
560
561 std::uint16_t val{};
562 std::string_view sv{word.begin(), word.end()};
563
564 auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), val);
565 if (ec == std::errc{})
566 {
567 result.push_back(val);
568 }
569 }
570
571 return result;
572}
573// =============================================================================
574// Submodule / module parsing
575// =============================================================================
576
577GSDMLSubmodule ParseSubmoduleNode(xmlXPathContextPtr ctx, xmlNodePtr submoduleNode, const std::map<std::string, std::string>& textTable)
578{
579 GSDMLSubmodule sm;
580 sm.id = GetAttr(submoduleNode, "ID");
581 sm.submoduleIdentNumber = ParseInt(GetAttrOpt(submoduleNode, "SubmoduleIdentNumber"));
582 sm.name = ResolveName(ctx, submoduleNode, textTable, sm.id);
583 sm.allowedSubslots = ParseSlotSpec(GetAttrOpt(submoduleNode, "AllowedInSubslots"));
584 sm.fixedSubslots = ParseSlotSpec(GetAttrOpt(submoduleNode, "FixedInSubslots"));
585 sm.usedSubslots = ParseSlotSpec(GetAttrOpt(submoduleNode, "UsedInSubslots"));
586
587 xmlNodePtr ioData = FindFirst(ctx, submoduleNode, "IOData");
588 if (ioData != nullptr)
589 {
590 for (auto* input : FindChildren(ctx, ioData, "Input"))
591 {
592 for (auto* item : FindChildren(ctx, input, "DataItem"))
593 {
594 GSDMLDataItem di;
595 di.dataType = GetAttr(item, "DataType");
596 di.textId = GetAttr(item, "TextId");
597 di.length = DataItemLength(di.dataType, GetAttrOpt(item, "Length"));
598 di.byteOffset = sm.inputLength;
599 ParseBitDataItems(ctx, item, di, textTable);
600
601 sm.inputLength += di.length;
602 sm.inputItems.push_back(di);
603 }
604 }
605 for (auto* output : FindChildren(ctx, ioData, "Output"))
606 {
607 for (auto* item : FindChildren(ctx, output, "DataItem"))
608 {
609 GSDMLDataItem di;
610 di.dataType = GetAttr(item, "DataType");
611 di.textId = GetAttr(item, "TextId");
612 di.length = DataItemLength(di.dataType, GetAttrOpt(item, "Length"));
613 di.byteOffset = sm.inputLength;
614 ParseBitDataItems(ctx, item, di, textTable);
615
616 sm.outputLength += di.length;
617 sm.outputItems.push_back(di);
618 }
619 }
620 }
621
622 return sm;
623}
624
625} // namespace
626
627// =============================================================================
628// GSDMLDevice
629// =============================================================================
630
631const GSDMLModule* GSDMLDevice::FindModule(const std::string& moduleId) const
632{
633 auto it = modules.find(moduleId);
634 return it != modules.end() ? &it->second : nullptr;
635}
636const GSDMLSubmodule* GSDMLDevice::FindSubmodule(const std::string& submoduleId) const
637{
638 const auto it = submodules.find(submoduleId);
639
640 return it != submodules.end()
641 ? &it->second
642 : nullptr;
643}
644/*
645std::vector<rpc::IOSlot> GSDMLDevice::BuildIoSlots(
646 const std::vector<std::tuple<int, std::string, int, std::string>>& plugged) const
647{
648 std::vector<rpc::IOSlot> slots;
649
650 // System-defined submodules (interface, port) always occupy their
651 // configured subslots on the DAP's slot (slot 0 by convention).
652 for (const auto& sys : systemSubmodules)
653 {
654 rpc::IOSlot slot;
655 slot.slot = 0;
656 slot.subslot = sys.subslot;
657 slot.inputLength = 0;
658 slot.outputLength = 0;
659 slot.moduleIdent = dapModuleIdentNumber;
660 slot.submoduleIdent = sys.submoduleIdentNumber;
661 slots.push_back(slot);
662 }
663
664 auto addModuleSubmodule = [&](int slot, const std::string& moduleId, int subslot,
665 const std::string& submoduleId)
666 {
667 const GSDMLModule* module = FindModule(moduleId);
668 if (!module)
669 {
670 throw std::invalid_argument("Unknown module ID: " + moduleId);
671 }
672
673 auto allowedIt = allowedSlots.find(moduleId);
674 if (allowedIt != allowedSlots.end() && !allowedIt->second.empty())
675 {
676 if (std::find(allowedIt->second.begin(), allowedIt->second.end(), slot) ==
677 allowedIt->second.end())
678 {
679 throw std::invalid_argument("Module '" + moduleId + "' is not allowed in slot " +
680 std::to_string(slot));
681 }
682 }
683
684 auto subIt = module->submodules.find(submoduleId);
685 if (subIt == module->submodules.end())
686 {
687 throw std::invalid_argument("Unknown submodule ID '" + submoduleId + "' in module '" + moduleId + "'");
688 }
689 const GSDMLSubmodule& sm = subIt->second;
690
691 rpc::IOSlot ioSlot;
692 ioSlot.slot = static_cast<std::uint16_t>(slot);
693 ioSlot.subslot = static_cast<std::uint16_t>(subslot);
694 ioSlot.inputLength = static_cast<std::uint16_t>(sm.inputLength);
695 ioSlot.outputLength = static_cast<std::uint16_t>(sm.outputLength);
696 ioSlot.moduleIdent = module->moduleIdentNumber;
697 ioSlot.submoduleIdent = sm.submoduleIdentNumber;
698 slots.push_back(ioSlot);
699 };
700
701 if (!plugged.empty())
702 {
703 for (const auto& [Slot, ModuleId, Subslot, SubmoduleId] : plugged)
704 {
705 addModuleSubmodule(Slot, ModuleId, Subslot, SubmoduleId);
706 }
707 }
708 else
709 {
710 // No explicit plan given -- fall back to whatever the GSDML marks
711 // as fixed (pre-plugged) modules, using each module's first
712 // declared submodule at subslot 1.
713 for (const auto& [slot, moduleId] : fixedModules)
714 {
715 const GSDMLModule* module = FindModule(moduleId);
716 if ((module == nullptr) || module->submodules.empty())
717 {
718 continue;
719 }
720 addModuleSubmodule(slot, moduleId, 1, module->submodules.begin()->first);
721 }
722 }
723
724 return slots;
725}
726*/
727
728std::vector<rpc::IOSlot> GSDMLDevice::BuildIoSlots(const std::vector<std::tuple<int, std::string, int, std::string>>& plugged) const
729{
730 std::vector<rpc::IOSlot> slots;
731
732 // ========================================================================
733 // DAP virtual submodules
734 // ========================================================================
735 // DAP virtual submodules are different from the system-defined interface
736 // and port submodules. Their subslot numbers are assigned sequentially
737 // starting at 1.
738 //
739 for (std::size_t i = 0; i < dapSubmodules.size(); ++i)
740 {
741 const GSDMLSubmodule& sub = dapSubmodules[i];
742
743 rpc::IOSlot slot;
744 slot.slot = 0;
745 slot.subslot = static_cast<std::uint16_t>(i + 1);
746 slot.inputLength = static_cast<std::uint16_t>(sub.inputLength);
747 slot.outputLength = static_cast<std::uint16_t>(sub.outputLength);
750
751 slots.push_back(slot);
752 }
753
754 // ========================================================================
755 // DAP system-defined submodules
756 // ========================================================================
757 //
758 // Interface and port submodules retain the subslot numbers specified by
759 // the GSDML. They do not carry cyclic I/O data.
760 //
761 for (const GSDMLSystemSubmodule& sys : systemSubmodules)
762 {
763 rpc::IOSlot slot;
764 slot.slot = 0;
765 slot.subslot = sys.subslot;
766 slot.inputLength = 0;
767 slot.outputLength = 0;
769 slot.submoduleIdent = sys.submoduleIdentNumber;
770
771 slots.push_back(slot);
772 }
773
774 // ========================================================================
775 // Helper for first resolve a submodule
776 // ========================================================================
777 auto resolveSubmodule = [&](const GSDMLModule& module, const std::string& submoduleId) -> const GSDMLSubmodule*
778 {
779 // Inline submodule.
780 const auto inlineIt = module.submodules.find(submoduleId);
781
782 if (inlineIt != module.submodules.end())
783 {
784 return &inlineIt->second;
785 }
786
787 const auto refIt = std::ranges::find_if(module.submoduleReferences, [&](const GSDMLSubmoduleReference& ref)
788 {
789 return ref.submoduleId == submoduleId;
790 });
791
792 if (refIt == module.submoduleReferences.end())
793 {
794 return nullptr;
795 }
796
797 // Globally defined submodule.
798 return FindSubmodule(submoduleId);
799 };
800
801 // ========================================================================
802 // Helper for validating module in slots
803 // ========================================================================
804 auto isModuleAllowedInSlot = [&](const std::string& moduleId, int slotNumber)
805 {
806 bool hasRestrictions = false;
807
808 for (const auto& ref : moduleReferences)
809 {
810 if (ref.moduleId != moduleId)
811 {
812 continue;
813 }
814
815 if (!ref.allowedSlots.empty())
816 {
817 hasRestrictions = true;
818
819 if (std::ranges::find(
820 ref.allowedSlots,
821 slotNumber) != ref.allowedSlots.end())
822 {
823 return true;
824 }
825 }
826
827 if (std::ranges::find(
828 ref.fixedSlots,
829 slotNumber) != ref.fixedSlots.end())
830 {
831 return true;
832 }
833
834 if (std::ranges::find(
835 ref.usedSlots,
836 slotNumber) != ref.usedSlots.end())
837 {
838 return true;
839 }
840 }
841
842 return !hasRestrictions;
843 };
844 // ========================================================================
845 // Helper for valid subslot
846 // ========================================================================
847 auto validateSubslot = [&](const GSDMLModule& module, const std::string& submoduleId, int subslotNumber)
848 {
849 const auto refIt = std::ranges::find_if(module.submoduleReferences, [&](const GSDMLSubmoduleReference& ref)
850 {
851 return ref.submoduleId == submoduleId;
852 });
853
854 if (refIt == module.submoduleReferences.end())
855 {
856 return;
857 }
858
859 if (!refIt->allowedSubslots.empty() &&
860 std::ranges::find(refIt->allowedSubslots, subslotNumber) == refIt->allowedSubslots.end())
861 {
862 throw std::invalid_argument("Submodule '" + submoduleId + "' is not allowed in subslot " + std::to_string(subslotNumber) + " of module '" + module.id + "'");
863 }
864 };
865 // ========================================================================
866 // Helper for adding one module submodule
867 // ========================================================================
868 auto addModuleSubmodule = [&](int slotNumber, const std::string& moduleId, int subslotNumber, const std::string& submoduleId)
869 {
870 const GSDMLModule* module = FindModule(moduleId);
871
872 if (module == nullptr)
873 {
874 throw std::invalid_argument("Unknown module ID: " + moduleId);
875 }
876 /*
877 const GSDMLModuleReference* moduleRef = nullptr;
878
879 for (const auto& ref : moduleReferences)
880 {
881 if (ref.moduleId == moduleId)
882 {
883 moduleRef = &ref;
884 break;
885 }
886 }
887 */
888 // if (moduleRef != nullptr && !moduleRef->allowedSlots.empty() && std::ranges::find(moduleRef->allowedSlots, slotNumber) == moduleRef->allowedSlots.end())
889 if (!isModuleAllowedInSlot(moduleId, slotNumber))
890 {
891 throw std::invalid_argument("Module '" + moduleId + "' is not allowed in slot " + std::to_string(slotNumber));
892 }
893
894 validateSubslot(*module, submoduleId, subslotNumber);
895
896 const GSDMLSubmodule* submodule = resolveSubmodule(*module, submoduleId);
897
898 if (submodule == nullptr)
899 {
900 throw std::invalid_argument("Unknown submodule ID '" + submoduleId + "' in module '" + moduleId + "'");
901 }
902 /*
903 // Validate the requested slot against AllowedInSlots / UseableModules.
904 const auto allowedIt = allowedSlots.find(moduleId);
905
906 if (allowedIt != allowedSlots.end() && !allowedIt->second.empty())
907 {
908 const auto slotIt =
909 std::find(allowedIt->second.begin(),
910 allowedIt->second.end(),
911 slotNumber);
912
913 if (slotIt == allowedIt->second.end())
914 {
915 throw std::invalid_argument(
916 "Module '" + moduleId + "' is not allowed in slot " +
917 std::to_string(slotNumber));
918 }
919 }
920
921 const auto submoduleIt = module->submodules.find(submoduleId);
922
923 if (submoduleIt == module->submodules.end())
924 {
925 throw std::invalid_argument(
926 "Unknown submodule ID '" + submoduleId +
927 "' in module '" + moduleId + "'");
928 }
929
930 const GSDMLSubmodule& submodule = submoduleIt->second;
931*/
932 rpc::IOSlot ioSlot;
933 ioSlot.slot = static_cast<std::uint16_t>(slotNumber);
934 ioSlot.subslot = static_cast<std::uint16_t>(subslotNumber);
935 ioSlot.inputLength = static_cast<std::uint16_t>(submodule->inputLength);
936 ioSlot.outputLength = static_cast<std::uint16_t>(submodule->outputLength);
937 ioSlot.moduleIdent = module->moduleIdentNumber;
938 ioSlot.submoduleIdent = submodule->submoduleIdentNumber;
939
940 slots.push_back(ioSlot);
941 };
942
943 // ========================================================================
944 // Explicit module/submodule assignment
945 // ========================================================================
946 if (!plugged.empty())
947 {
948 for (const auto& [slotNumber, moduleId, subslotNumber, submoduleId] : plugged)
949 {
950 addModuleSubmodule(
951 slotNumber,
952 moduleId,
953 subslotNumber,
954 submoduleId);
955 }
956 return slots;
957 }
958 /*
959 // ========================================================================
960 // Default: use fixed modules from the GSDML
961 // ========================================================================
962 //
963 // `fixedModules` currently represents:
964 //
965 // slot number -> module ID
966 //
967 for (const auto& [slotNumber, moduleId] : fixedModules)
968 {
969 const GSDMLModule* module = FindModule(moduleId);
970
971 if (module == nullptr)
972 {
973 throw std::invalid_argument(
974 "Unknown fixed module ID: " + moduleId);
975 }
976
977 // ----------------------------------------------------------------------
978 // Module with inline VirtualSubmoduleList
979 // ----------------------------------------------------------------------
980 if (!module->submodules.empty())
981 {
982 std::uint16_t subslotNumber = 1;
983
984 for (const auto& [submoduleId, submodule] : module->submodules)
985 {
986 (void)submodule;
987
988 addModuleSubmodule(
989 slotNumber,
990 moduleId,
991 subslotNumber,
992 submoduleId);
993
994 ++subslotNumber;
995 }
996 }
997
998 // ----------------------------------------------------------------------
999 // Module with referenced UseableSubmodules
1000 // ----------------------------------------------------------------------
1001
1002 }
1003 return slots;
1004 */
1005
1006 // ========================================================================
1007 // Default configuration from GSDML
1008 // ========================================================================
1009
1010 for (const GSDMLModuleReference& moduleRef : moduleReferences)
1011 {
1012 const GSDMLModule* module = FindModule(moduleRef.moduleId);
1013
1014 if (module == nullptr)
1015 {
1016 throw std::invalid_argument("Unknown referenced module ID: " + moduleRef.moduleId);
1017 }
1018
1019 std::vector<int> selectedSlots;
1020
1021 if (!moduleRef.fixedSlots.empty())
1022 {
1023 selectedSlots = moduleRef.fixedSlots;
1024 }
1025 else if (!moduleRef.usedSlots.empty())
1026 {
1027 selectedSlots = moduleRef.usedSlots;
1028 }
1029 else
1030 {
1031 // AllowedInSlots only describes capability, not actual selection.
1032 continue;
1033 }
1034
1035 for (const int slotNumber : selectedSlots)
1036 {
1037 // ================================================================
1038 // Referenced SubmoduleItem definitions
1039 // ================================================================
1040
1041 if (!module->submoduleReferences.empty())
1042 {
1043 for (const auto& submoduleRef : module->submoduleReferences)
1044 {
1045 std::vector<int> selectedSubslots;
1046
1047 if (!submoduleRef.fixedSubslots.empty())
1048 {
1049 selectedSubslots = submoduleRef.fixedSubslots;
1050 }
1051 else if (!submoduleRef.usedSubslots.empty())
1052 {
1053 selectedSubslots = submoduleRef.usedSubslots;
1054 }
1055 else
1056 {
1057 continue;
1058 }
1059
1060 for (const int subslotNumber : selectedSubslots)
1061 {
1062 addModuleSubmodule(
1063 slotNumber,
1064 moduleRef.moduleId,
1065 subslotNumber,
1066 submoduleRef.submoduleId);
1067 }
1068 }
1069
1070 continue;
1071 }
1072
1073 // ================================================================
1074 // Inline VirtualSubmoduleItem definitions
1075 // ================================================================
1076
1077 for (const auto& [submoduleId, submodule] : module->submodules)
1078 {
1079 std::vector<int> selectedSubslots;
1080
1081 if (!submodule.fixedSubslots.empty())
1082 {
1083 selectedSubslots = submodule.fixedSubslots;
1084 }
1085 else if (!submodule.usedSubslots.empty())
1086 {
1087 selectedSubslots = submodule.usedSubslots;
1088 }
1089 else if (module->submodules.size() == 1U)
1090 {
1091 // Compatibility fallback for simple GSDML files where a
1092 // single virtual submodule has no explicit placement.
1093 selectedSubslots = {1};
1094 }
1095
1096 for (const int subslotNumber : selectedSubslots)
1097 {
1098 addModuleSubmodule(
1099 slotNumber,
1100 moduleRef.moduleId,
1101 subslotNumber,
1102 submoduleId);
1103 }
1104 }
1105 }
1106 }
1107 return slots;
1108}
1109
1110// =============================================================================
1111// Parsing
1112// =============================================================================
1113
1114GSDMLDevice ParseGsdmlString(const std::string& xml)
1115{
1116 XmlDocGuard docGuard;
1117 docGuard.doc = xmlReadMemory(xml.data(), static_cast<int>(xml.size()), "gsdml.xml", nullptr,
1118 XML_PARSE_NOBLANKS);
1119 if (docGuard.doc == nullptr)
1120 {
1121 throw std::runtime_error("Failed to parse GSDML: not well-formed XML");
1122 }
1123
1124 xmlNodePtr root = xmlDocGetRootElement(docGuard.doc);
1125 if (root == nullptr)
1126 {
1127 throw std::runtime_error("Failed to parse GSDML: empty document");
1128 }
1129
1130 XPathContextGuard ctxGuard;
1131 ctxGuard.ctx = xmlXPathNewContext(docGuard.doc);
1132 if (ctxGuard.ctx == nullptr)
1133 {
1134 throw std::runtime_error("Failed to create XPath context");
1135 }
1136 xmlXPathContextPtr ctx = ctxGuard.ctx;
1137
1138 GSDMLDevice device;
1139 // find PrimaryLanguage else just use root.
1140 xmlNodePtr primaryLanguageSection = FindFirst(ctx, root, "PrimaryLanguage");
1141
1142 std::map<std::string, std::string> textTable;
1143 if (primaryLanguageSection == nullptr)
1144 {
1145 textTable = BuildTextTable(ctx, root);
1146 }
1147 else
1148 {
1149 textTable = BuildTextTable(ctx, primaryLanguageSection);
1150 }
1151
1152 xmlNodePtr deviceIdentity = FindFirst(ctx, root, "DeviceIdentity");
1153 if (deviceIdentity == nullptr)
1154 {
1155 // throw std::runtime_error("Failed to parse GSDML: no DeviceIdentity element found");
1156 }
1157 else
1158 {
1159 device.vendorId = static_cast<std::uint16_t>(ParseInt(GetAttrOpt(deviceIdentity, "VendorID")));
1160 device.deviceId = static_cast<std::uint16_t>(ParseInt(GetAttrOpt(deviceIdentity, "DeviceID")));
1161 device.vendorName = ResolveName(ctx, deviceIdentity, textTable, "");
1162 if (device.vendorName.empty())
1163 {
1164 device.vendorName = ResolveVendorName(ctx, deviceIdentity, textTable, "");
1165 }
1166 }
1167 xmlNodePtr deviceFunction = FindFirst(ctx, root, "DeviceFunction");
1168 device.deviceName = (deviceFunction != nullptr) ? ResolveName(ctx, deviceFunction, textTable, "") : device.vendorName;
1169
1170 // Device Access Point (DAP): the device's own built-in module/submodule.
1171 xmlNodePtr dap = FindFirst(ctx, root, "DeviceAccessPointItem");
1172 if (dap != nullptr)
1173 {
1174 device.dapModuleId = GetAttr(dap, "ID");
1175 device.dapModuleIdentNumber = ParseInt(GetAttrOpt(dap, "ModuleIdentNumber"));
1176
1177 // Module information
1178 xmlNodePtr dapModuleInfo = FindFirst(ctx, dap, "ModuleInfo");
1179 if (dapModuleInfo != nullptr)
1180 {
1181 device.dapModuleName = ResolveName(ctx, dapModuleInfo, textTable, "");
1182 device.dapModuleInfo = ResolveInfoText(ctx, dapModuleInfo, textTable, "");
1183 }
1184
1185 // xmlNodePtr dapSubmodule = dap; // DAP's own submodule ident is on the DAP item itself.
1186 // device.dapSubmoduleIdentNumber = ParseInt(GetAttrOpt(dapSubmodule, "SubmoduleIdentNumber"));
1187
1188 device.dapSlots = ParseSlotSpec(GetAttrOpt(dap, "PhysicalSlots"));
1189
1190 xmlNodePtr sysList = FindFirst(ctx, dap, "SystemDefinedSubmoduleList");
1191 if (sysList != nullptr)
1192 {
1193 for (auto* subNode : FindChildren(ctx, sysList, "InterfaceSubmoduleItem"))
1194 {
1196 sys.submoduleIdentNumber = ParseInt(GetAttrOpt(subNode, "SubmoduleIdentNumber"));
1197 // sys.subslot = static_cast<std::uint16_t>(ParseInt(GetAttrOpt(subNode, "Subslot")));
1198 sys.subslot = static_cast<std::uint16_t>(ParseInt(GetAttrOpt(subNode, "SubslotNumber")));
1199
1200 // Module information
1201 xmlNodePtr moduleInfo = FindFirst(ctx, subNode, "ModuleInfo");
1202 if (moduleInfo != nullptr)
1203 {
1204 sys.moduleName = ResolveName(ctx, moduleInfo, textTable, "");
1205 }
1206 else
1207 {
1208 sys.moduleName = GetAttr(subNode, "ID");
1209 }
1210
1211 // Application relations information
1212 xmlNodePtr applicationRelations = FindFirst(ctx, subNode, "ApplicationRelations");
1213 if (applicationRelations != nullptr)
1214 {
1215 device.applicationRelations.numberOfAr = static_cast<std::uint16_t>(ParseInt(GetAttrOpt(applicationRelations, "NumberOfAR")));
1216 device.applicationRelations.startupMode = ParseStartupMode(GetAttrOpt(applicationRelations, "StartupMode"));
1217 xmlNodePtr timingProperties = FindFirst(ctx, applicationRelations, "TimingProperties");
1218 if (timingProperties != nullptr)
1219 {
1220 device.applicationRelations.sendClockTimingProperties = ParseStringRanges(GetAttrOpt(timingProperties, "SendClock"));
1221 device.applicationRelations.reductionRatioTimingProperties = ParseStringRanges(GetAttrOpt(timingProperties, "ReductionRatio"));
1222 }
1223 }
1224
1225 device.systemSubmodules.push_back(sys);
1226 }
1227 for (auto* subNode : FindChildren(ctx, sysList, "PortSubmoduleItem"))
1228 {
1230 sys.submoduleIdentNumber = ParseInt(GetAttrOpt(subNode, "SubmoduleIdentNumber"));
1231 // sys.subslot = static_cast<std::uint16_t>(ParseInt(GetAttrOpt(subNode, "Subslot")));
1232 sys.subslot = static_cast<std::uint16_t>(ParseInt(GetAttrOpt(subNode, "SubslotNumber")));
1233
1234 // Module information
1235 xmlNodePtr moduleInfo = FindFirst(ctx, subNode, "ModuleInfo");
1236 if (moduleInfo != nullptr)
1237 {
1238 sys.moduleName = ResolveName(ctx, moduleInfo, textTable, "");
1239 }
1240 else
1241 {
1242 sys.moduleName = GetAttr(subNode, "ID");
1243 }
1244
1245 device.systemSubmodules.push_back(sys);
1246 }
1247 }
1248
1249 // Modules the DAP itself provides directly (VirtualSubmoduleList on the DAP).
1250 xmlNodePtr dapVsl = FindFirst(ctx, dap, "VirtualSubmoduleList");
1251 if (dapVsl != nullptr)
1252 {
1253 // Also expose the DAP virtual submodule through the module catalogue.
1254 GSDMLModule dapModule;
1255 dapModule.id = device.dapModuleId;
1256 dapModule.moduleIdentNumber = device.dapModuleIdentNumber;
1257 dapModule.name = device.deviceName;
1258
1259 for (auto* subNode : FindChildren(ctx, dapVsl, "VirtualSubmoduleItem"))
1260 {
1261 GSDMLSubmodule sm = ParseSubmoduleNode(ctx, subNode, textTable);
1262
1263 // Keep DAP virtual submodules separately so BuildIoSlots() can reproduce
1264 // the implementation's DAP slot-0 entries.
1265 device.dapSubmodules.push_back(sm);
1266
1267 if (device.dapModuleId == sm.id)
1268 {
1270 }
1271 dapModule.submodules[sm.id.empty() ? "dap" : sm.id] = sm;
1272 }
1273 // if (sm.submoduleIdentNumber == 0)
1274 // {
1275 // // hier moet een fix
1276 // sm.submoduleIdentNumber = device.dapSubmoduleIdentNumber;
1277 // }
1278 if (device.dapSubmoduleIdentNumber == 0 && !device.dapSubmodules.empty())
1279 {
1280 device.dapSubmoduleIdentNumber = device.dapSubmodules.front().submoduleIdentNumber;
1281 }
1282 device.modules[dapModule.id] = std::move(dapModule);
1283 }
1284 }
1285
1286 // ModuleList: every pluggable module and its virtual submodules.
1287 xmlNodePtr moduleList = FindFirst(ctx, root, "ModuleList");
1288 if (moduleList != nullptr)
1289 {
1290 for (auto* moduleNode : FindChildren(ctx, moduleList, "ModuleItem"))
1291 {
1292 GSDMLModule mod;
1293 mod.id = GetAttr(moduleNode, "ID");
1294 mod.moduleIdentNumber = ParseInt(GetAttrOpt(moduleNode, "ModuleIdentNumber"));
1295 mod.name = ResolveName(ctx, moduleNode, textTable, mod.id);
1296 mod.physicalSubslots = ParseSlotSpec(GetAttrOpt(moduleNode, "PhysicalSubslots"));
1297
1298 xmlNodePtr vsl = FindFirst(ctx, moduleNode, "VirtualSubmoduleList");
1299 if (vsl != nullptr)
1300 {
1301 for (auto* subNode : FindChildren(ctx, vsl, "VirtualSubmoduleItem"))
1302 {
1303 GSDMLSubmodule sm = ParseSubmoduleNode(ctx, subNode, textTable);
1304 mod.submodules[sm.id] = std::move(sm);
1305 }
1306 }
1307
1308 // ----------------------------------------------------------------------
1309 // Module with referenced UseableSubmodules
1310 // ----------------------------------------------------------------------
1311 // The C++ GSDMLModule representation does not yet contain the
1312 // equivalent useable_submodules/submodule catalog information, so there
1313 // is nothing further to add here.
1314 xmlNodePtr usableSubmodules = FindFirst(ctx, moduleNode, "UseableSubmodules");
1315
1316 if (usableSubmodules != nullptr)
1317 {
1318 for (auto* refNode : FindChildren(ctx, usableSubmodules, "SubmoduleItemRef"))
1319 {
1321 ref.submoduleId = GetAttr(refNode, "SubmoduleItemTarget");
1322 ref.allowedSubslots = ParseSlotSpec(GetAttrOpt(refNode, "AllowedInSubslots"));
1323 ref.fixedSubslots = ParseSlotSpec(GetAttrOpt(refNode, "FixedInSubslots"));
1324 ref.usedSubslots = ParseSlotSpec(GetAttrOpt(refNode, "UsedInSubslots"));
1325 mod.submoduleReferences.push_back(std::move(ref));
1326 }
1327 }
1328
1329 device.modules[mod.id] = std::move(mod);
1330 }
1331 }
1332
1333 // ========================================================================
1334 // Global SubmoduleList
1335 // ========================================================================
1336 //
1337 // Some GSDML files define submodules inline inside a ModuleItem using
1338 // VirtualSubmoduleList. Others, such as Phoenix Contact GSDMLs, define
1339 // SubmoduleItem objects globally and reference them through
1340 // UseableSubmodules/SubmoduleItemRef.
1341 //
1342 xmlNodePtr submoduleList = FindFirst(ctx, root, "SubmoduleList");
1343
1344 if (submoduleList != nullptr)
1345 {
1346 for (auto* submoduleNode :
1347 FindChildren(ctx, submoduleList, "SubmoduleItem"))
1348 {
1349 GSDMLSubmodule submodule =
1350 ParseSubmoduleNode(ctx, submoduleNode, textTable);
1351
1352 device.submodules[submodule.id] = std::move(submodule);
1353 }
1354 }
1355
1356 // UseableModules: which slots each module may be plugged into, and
1357 // which modules are fixed (pre-plugged) at a given slot.
1358 xmlNodePtr useList = FindFirst(ctx, (dap != nullptr) ? dap : root, "UseableModules");
1359 if (useList != nullptr)
1360 {
1361 for (auto* useNode : FindChildren(ctx, useList, "ModuleItemRef"))
1362 {
1363 /*
1364 std::string moduleId = GetAttr(useNode, "ModuleItemTarget");
1365 device.allowedSlots[moduleId] = ParseSlotSpec(GetAttrOpt(useNode, "AllowedInSlots"));
1366*/
1368 ref.moduleId = GetAttr(useNode, "ModuleItemTarget");
1369 ref.allowedSlots = ParseSlotSpec(GetAttrOpt(useNode, "AllowedInSlots"));
1370 ref.fixedSlots = ParseSlotSpec(GetAttrOpt(useNode, "FixedInSlots"));
1371 ref.usedSlots = ParseSlotSpec(GetAttrOpt(useNode, "UsedInSlots"));
1372 device.moduleReferences.push_back(std::move(ref));
1373 }
1374 }
1375 // PredefinedPnioModules:
1376 //
1377 xmlNodePtr fixedList = FindFirst(ctx, (dap != nullptr) ? dap : root, "PredefinedPnioModules");
1378 if (fixedList != nullptr)
1379 {
1380 for (auto* fixedNode : FindChildren(ctx, fixedList, "PnioModuleItemRef"))
1381 {
1383 ref.moduleId = GetAttr(fixedNode, "ModuleItemTarget");
1384 ref.fixedSlots = ParseSlotSpec(GetAttrOpt(fixedNode, "FixedInSlots"));
1385 device.moduleReferences.push_back(std::move(ref));
1386
1387 /*
1388 for (int slot : ParseSlotSpec(GetAttrOpt(fixedNode, "FixedInSlots")))
1389 {
1390 device.fixedModules.emplace_back(slot, moduleId);
1391 }
1392 */
1393 }
1394 }
1395 return device;
1396}
1397
1398GSDMLDevice ParseGsdml(const std::string& path)
1399{
1400 XmlDocGuard docGuard;
1401 docGuard.doc = xmlReadFile(path.c_str(), nullptr, XML_PARSE_NOBLANKS);
1402 if (docGuard.doc == nullptr)
1403 {
1404 throw std::runtime_error("Failed to open or parse GSDML file: " + path);
1405 }
1406
1407 // Re-serialize and re-parse via ParseGsdmlString to keep a single
1408 // parsing code path (xmlReadFile already validated well-formedness,
1409 // so this second pass is cheap and keeps the implementation simple).
1410 xmlChar* buf = nullptr;
1411 int size = 0;
1412 xmlDocDumpMemory(docGuard.doc, &buf, &size);
1413 std::string xml(reinterpret_cast<const char*>(buf), static_cast<std::size_t>(size));
1414 xmlFree(buf);
1415
1416 return ParseGsdmlString(xml);
1417}
1418
1419} // namespace profinet::gsdml
xmlXPathObjectPtr obj
Definition gsdml.cpp:82
xmlXPathContextPtr ctx
Definition gsdml.cpp:70
xmlDocPtr doc
Definition gsdml.cpp:58
Declares the GSDML parser and PROFINET device-description data model.
GSDMLDevice ParseGsdmlString(const std::string &xml)
Parse GSDML content already held in memory.
Definition gsdml.cpp:1114
GSDMLDevice ParseGsdml(const std::string &path)
Parse a GSDML file into a GSDMLDevice.
Definition gsdml.cpp:1398
Complete parsed GSDML device profile.
Definition gsdml.h:166
std::string vendorName
Vendor name from the GSDML's DeviceIdentity.
Definition gsdml.h:168
std::string dapModuleInfo
The device access point's own (built-in) module info text.
Definition gsdml.h:181
std::map< std::string, GSDMLModule > modules
Every module declared in the GSDML's ModuleList, keyed by GSDML ID.
Definition gsdml.h:196
std::vector< GSDMLModuleReference > moduleReferences
Modules referenced by the DAP's UseableModules list.
Definition gsdml.h:202
std::vector< GSDMLSubmodule > dapSubmodules
DAP virtual submodules.
Definition gsdml.h:190
std::uint16_t vendorId
PROFINET vendor ID from the GSDML's DeviceIdentity (VendorID attribute).
Definition gsdml.h:172
rpc::ApplicationRelations applicationRelations
Definition gsdml.h:204
std::uint32_t dapSubmoduleIdentNumber
The device access point's own (built-in) submodule ident number.
Definition gsdml.h:183
const GSDMLSubmodule * FindSubmodule(const std::string &submoduleId) const
Look up a globally defined submodule.
Definition gsdml.cpp:636
std::vector< GSDMLSystemSubmodule > systemSubmodules
System-defined submodules built into the DAP (e.g. interface, port 1).
Definition gsdml.h:193
std::vector< rpc::IOSlot > BuildIoSlots(const std::vector< std::tuple< int, std::string, int, std::string > > &plugged={}) const
Build I/O slot descriptions from the parsed GSDML configuration.
Definition gsdml.cpp:728
std::uint16_t deviceId
PROFINET device ID from the GSDML's DeviceIdentity (DeviceID attribute).
Definition gsdml.h:174
std::uint32_t dapModuleIdentNumber
The device access point's own module ident number.
Definition gsdml.h:185
std::map< std::string, GSDMLSubmodule > submodules
Every globally defined SubmoduleItem, keyed by GSDML ID.
Definition gsdml.h:199
std::string dapModuleName
The device access point's own (built-in) module name.
Definition gsdml.h:179
std::string deviceName
Device name from the GSDML's DeviceIdentity / DeviceFunction.
Definition gsdml.h:170
const GSDMLModule * FindModule(const std::string &moduleId) const
Look up a module by its GSDML ID.
Definition gsdml.cpp:631
std::vector< int > dapSlots
Slot numbers where the DAP may be fixed/plugged (from UseableModules/PlugSlots).
Definition gsdml.h:187
std::string dapModuleId
The device access point's own (built-in) identifier to matchs with the submodule.
Definition gsdml.h:177
Reference describing how a module may be used by the DAP.
Definition gsdml.h:150
std::string moduleId
Referenced ModuleItem ID.
Definition gsdml.h:152
std::vector< int > usedSlots
Slots where the module is currently used/preselected.
Definition gsdml.h:161
std::vector< int > allowedSlots
Slots where the module is allowed.
Definition gsdml.h:155
std::vector< int > fixedSlots
Slots where the module is fixed.
Definition gsdml.h:158
A GSDML module definition (ModuleItem), possibly offering multiple virtual submodules.
Definition gsdml.h:106
std::string id
GSDML ID attribute (used to resolve ModuleItemTarget references).
Definition gsdml.h:108
std::vector< GSDMLSubmoduleReference > submoduleReferences
References to globally defined SubmoduleItem definitions.
Definition gsdml.h:125
std::map< std::string, GSDMLSubmodule > submodules
Virtual submodules this module offers, keyed by their GSDML ID.
Definition gsdml.h:118
std::string name
Human-readable module name (from the GSDML's Name/TextId, resolved via ExternalTextList).
Definition gsdml.h:112
std::vector< int > physicalSubslots
Physical subslots supported by this module.
Definition gsdml.h:128
std::uint32_t moduleIdentNumber
Module ident number (hex value from the GSDML, parsed to an integer).
Definition gsdml.h:110
Reference to a submodule that may be used by a module.
Definition gsdml.h:64
std::vector< int > usedSubslots
Subslots where this submodule is currently used/preselected.
Definition gsdml.h:75
std::vector< int > allowedSubslots
Subslots where this submodule is allowed.
Definition gsdml.h:69
std::vector< int > fixedSubslots
Subslots where this submodule is fixed.
Definition gsdml.h:72
std::string submoduleId
Referenced SubmoduleItem ID.
Definition gsdml.h:66
A GSDML virtual submodule definition (VirtualSubmoduleItem).
Definition gsdml.h:80
std::uint32_t submoduleIdentNumber
Submodule ident number (hex value from the GSDML, parsed to an integer).
Definition gsdml.h:84
std::string id
GSDML ID attribute (used to resolve VirtualSubmoduleItemTarget references).
Definition gsdml.h:82
int outputLength
Total output data length in bytes (sum of Output DataItems).
Definition gsdml.h:90
int inputLength
Total input data length in bytes (sum of Input DataItems).
Definition gsdml.h:88
A GSDML system-defined submodule (e.g. built into the DAP: interface, port).
Definition gsdml.h:137
std::string moduleName
Referenced ModuleItem name.
Definition gsdml.h:145
std::uint16_t subslot
Subslot this submodule occupies.
Definition gsdml.h:142
std::uint32_t submoduleIdentNumber
Submodule ident number.
Definition gsdml.h:139
std::vector< std::uint16_t > reductionRatioTimingProperties
Definition rpcTypes.h:137
std::vector< std::uint16_t > sendClockTimingProperties
Definition rpcTypes.h:135
One slot/subslot's expected module configuration and IO data sizes.
Definition rpcTypes.h:96
std::uint16_t inputLength
Expected input data length in bytes (0 if no input data).
Definition rpcTypes.h:104
std::uint16_t slot
Slot number.
Definition rpcTypes.h:98
std::uint16_t outputLength
Expected output data length in bytes (0 if no output data).
Definition rpcTypes.h:107
std::uint32_t moduleIdent
Expected module ident number.
Definition rpcTypes.h:110
std::uint32_t submoduleIdent
Expected submodule ident number.
Definition rpcTypes.h:113
std::uint16_t subslot
Subslot number.
Definition rpcTypes.h:101