14#include <gtest/gtest.h>
32gsdml::GSDMLDataItem MakeDataItem(
const std::string& dataType,
34 const std::string& textId)
36 gsdml::GSDMLDataItem item;
37 item.dataType = dataType;
44gsdml::GSDMLModule MakeModule(
const std::string& moduleId,
45 const std::string& moduleName,
46 const std::string& submoduleId,
47 const std::string& submoduleName)
49 gsdml::GSDMLModule
module;
51 module.name = moduleName;
52 module.moduleIdentNumber = 0x1000U;
54 gsdml::GSDMLSubmodule submodule;
55 submodule.id = submoduleId;
56 submodule.name = submoduleName;
57 submodule.submoduleIdentNumber = 0x2000U;
59 module.submodules.emplace(submoduleId, std::move(submodule));
64gsdml::GSDMLModuleReference MakeModuleReference(
const std::string& moduleId,
65 std::vector<int> allowedSlots = {},
66 std::vector<int> fixedSlots = {},
67 std::vector<int> usedSlots = {})
69 gsdml::GSDMLModuleReference reference;
70 reference.moduleId = moduleId;
71 reference.allowedSlots = std::move(allowedSlots);
72 reference.fixedSlots = std::move(fixedSlots);
73 reference.usedSlots = std::move(usedSlots);
78gsdml::GSDMLSubmoduleReference MakeSubmoduleReference(
79 const std::string& submoduleId,
80 std::vector<int> allowedSubslots = {},
81 std::vector<int> fixedSubslots = {},
82 std::vector<int> usedSubslots = {})
84 gsdml::GSDMLSubmoduleReference reference;
85 reference.submoduleId = submoduleId;
86 reference.allowedSubslots = std::move(allowedSubslots);
87 reference.fixedSubslots = std::move(fixedSubslots);
88 reference.usedSubslots = std::move(usedSubslots);
95TEST(IoSignalTypeTests, ClassifiesAllDoubleCompatibleScalarTypes)
100 for (
const std::string dataType : {
101 "Integer8",
"Integer16",
"Integer32",
"Integer64"})
106 for (
const std::string dataType : {
107 "Unsigned8",
"Unsigned16",
"Unsigned32",
"Unsigned64"})
112 for (
const std::string dataType : {
"Float32",
"Float64"})
119TEST(IoSignalTypeTests, ClassifiesUnsupportedAndUnknownTypes)
121 for (
const std::string dataType : {
129 "F_MessageTrailer4Byte",
130 "F_MessageTrailer5Byte",
139TEST(IoSignalReservedTests, DetectsReservedTextIdsCaseInsensitively)
150TEST(IoSignalReservedTests, DoesNotMisclassifyNormalSignals)
162TEST(IoSignalTests, DirectionAccessorsReportCorrectDirection)
176TEST(IoSignalTests, DoubleCompatibilityAccessorMirrorsFlag)
188TEST(IoSignalCatalogTests, EmptyDeviceProducesEmptyCatalogue)
193 EXPECT_TRUE(catalog.Sensors().empty());
194 EXPECT_TRUE(catalog.Actuators().empty());
195 EXPECT_EQ(catalog.Size(), 0U);
199TEST(IoSignalCatalogTests, SeparatesInputAndOutputSignals)
202 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
203 auto& submodule =
module.submodules.at("SUBMODULE");
205 submodule.inputItems.push_back(MakeDataItem(
"Unsigned8", 1,
"SensorA"));
206 submodule.outputItems.push_back(MakeDataItem(
"Boolean", 1,
"ActuatorA"));
207 device.
modules.emplace(module.id, module);
211 ASSERT_EQ(catalog.Sensors().size(), 1U);
212 ASSERT_EQ(catalog.Actuators().size(), 1U);
213 EXPECT_EQ(catalog.Sensors()[0].name,
"SensorA");
214 EXPECT_EQ(catalog.Actuators()[0].name,
"ActuatorA");
215 EXPECT_TRUE(catalog.Sensors()[0].IsSensor());
216 EXPECT_TRUE(catalog.Actuators()[0].IsActuator());
220TEST(IoSignalCatalogTests, CalculatesIndependentInputAndOutputOffsets)
223 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
224 auto& submodule =
module.submodules.at("SUBMODULE");
226 submodule.inputItems = {
227 MakeDataItem(
"Unsigned8", 1,
"Input0"),
228 MakeDataItem(
"Unsigned16", 2,
"Input1"),
229 MakeDataItem(
"Float32", 4,
"Input2"),
231 submodule.outputItems = {
232 MakeDataItem(
"Boolean", 1,
"Output0"),
233 MakeDataItem(
"Unsigned32", 4,
"Output1"),
235 device.
modules.emplace(module.id, module);
239 ASSERT_EQ(catalog.Sensors().size(), 3U);
240 ASSERT_EQ(catalog.Actuators().size(), 2U);
242 EXPECT_EQ(catalog.Sensors()[0].byteOffset, 0U);
243 EXPECT_EQ(catalog.Sensors()[1].byteOffset, 1U);
244 EXPECT_EQ(catalog.Sensors()[2].byteOffset, 3U);
246 EXPECT_EQ(catalog.Actuators()[0].byteOffset, 0U);
247 EXPECT_EQ(catalog.Actuators()[1].byteOffset, 1U);
251TEST(IoSignalCatalogTests, ZeroLengthDataItemDoesNotAdvanceOffset)
254 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
255 auto& submodule =
module.submodules.at("SUBMODULE");
257 submodule.inputItems = {
258 MakeDataItem(
"OctetString", 0,
"Empty"),
259 MakeDataItem(
"Unsigned8", 1,
"Next"),
261 device.
modules.emplace(module.id, module);
265 ASSERT_EQ(catalog.Sensors().size(), 2U);
266 EXPECT_EQ(catalog.Sensors()[0].byteOffset, 0U);
267 EXPECT_EQ(catalog.Sensors()[0].byteLength, 0U);
268 EXPECT_EQ(catalog.Sensors()[1].byteOffset, 0U);
272TEST(IoSignalCatalogTests, NegativeDataItemLengthThrows)
275 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
276 module.submodules.at("SUBMODULE").inputItems.push_back(MakeDataItem("Unsigned8", -1, "Invalid"));
277 device.
modules.emplace(module.id, module);
283TEST(IoSignalCatalogTests, EmptyTextIdUsesFallbackName)
286 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
287 module.submodules.at("SUBMODULE").inputItems.push_back(MakeDataItem("Unsigned8", 1, ""));
288 device.
modules.emplace(module.id, module);
292 ASSERT_EQ(catalog.Sensors().size(), 1U);
293 EXPECT_EQ(catalog.Sensors()[0].name,
"<unnamed>");
294 EXPECT_TRUE(catalog.Sensors()[0].textId.empty());
298TEST(IoSignalCatalogTests, FiltersReservedItems)
301 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
302 auto& submodule =
module.submodules.at("SUBMODULE");
304 submodule.inputItems = {
305 MakeDataItem(
"Unsigned8", 1,
"Position"),
306 MakeDataItem(
"Unsigned8", 1,
"T_ID_RESERVE_INPUT"),
307 MakeDataItem(
"Unsigned8", 1,
"Status"),
309 submodule.outputItems = {
310 MakeDataItem(
"Boolean", 1,
"T_ID_RESERVE_OUTPUT"),
311 MakeDataItem(
"Boolean", 1,
"Open"),
313 device.
modules.emplace(module.id, module);
317 ASSERT_EQ(catalog.Sensors().size(), 2U);
318 ASSERT_EQ(catalog.Actuators().size(), 1U);
319 EXPECT_EQ(catalog.Sensors()[0].name,
"Position");
320 EXPECT_EQ(catalog.Sensors()[1].name,
"Status");
321 EXPECT_EQ(catalog.Actuators()[0].name,
"Open");
325TEST(IoSignalCatalogTests, ReservedItemStillAdvancesOffset)
328 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
329 auto& submodule =
module.submodules.at("SUBMODULE");
331 submodule.inputItems = {
332 MakeDataItem(
"Unsigned16", 2,
"Before"),
333 MakeDataItem(
"Unsigned32", 4,
"T_ID_RESERVE_INPUT"),
334 MakeDataItem(
"Unsigned8", 1,
"After"),
336 device.
modules.emplace(module.id, module);
340 ASSERT_EQ(catalog.Sensors().size(), 2U);
341 EXPECT_EQ(catalog.Sensors()[0].name,
"Before");
342 EXPECT_EQ(catalog.Sensors()[0].byteOffset, 0U);
343 EXPECT_EQ(catalog.Sensors()[1].name,
"After");
344 EXPECT_EQ(catalog.Sensors()[1].byteOffset, 6U);
348TEST(IoSignalCatalogTests, KeepsUnsupportedSignalsWithCompatibilityFalse)
351 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
352 auto& submodule =
module.submodules.at("SUBMODULE");
353 submodule.inputItems.push_back(MakeDataItem(
"VisibleString", 8,
"Description"));
354 submodule.outputItems.push_back(MakeDataItem(
"OctetString", 4,
"RawBytes"));
355 device.
modules.emplace(module.id, module);
359 ASSERT_EQ(catalog.Sensors().size(), 1U);
360 ASSERT_EQ(catalog.Actuators().size(), 1U);
362 EXPECT_FALSE(catalog.Sensors()[0].IsDoubleCompatible());
363 EXPECT_FALSE(catalog.Actuators()[0].IsDoubleCompatible());
367TEST(IoSignalCatalogTests, SupportedScalarSignalsAreDoubleCompatible)
370 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
371 auto& submodule =
module.submodules.at("SUBMODULE");
372 submodule.inputItems = {
373 MakeDataItem(
"Boolean", 1,
"Boolean"),
374 MakeDataItem(
"Integer64", 8,
"Signed"),
375 MakeDataItem(
"Unsigned64", 8,
"Unsigned"),
376 MakeDataItem(
"Float64", 8,
"Floating"),
378 device.
modules.emplace(module.id, module);
382 ASSERT_EQ(catalog.Sensors().size(), 4U);
383 for (
const auto& signal : catalog.Sensors())
385 EXPECT_TRUE(signal.IsDoubleCompatible());
390TEST(IoSignalCatalogTests, PreservesModuleAndSubmoduleMetadata)
393 auto module = MakeModule("MODULE", "Main module", "SUBMODULE", "I/O data");
394 module.submodules.at("SUBMODULE").inputItems.push_back(MakeDataItem("Unsigned16", 2, "Position"));
395 device.
modules.emplace(module.id, module);
399 ASSERT_EQ(catalog.Sensors().size(), 1U);
400 const auto& signal = catalog.Sensors()[0];
401 EXPECT_EQ(signal.moduleId,
"MODULE");
402 EXPECT_EQ(signal.moduleName,
"Main module");
403 EXPECT_EQ(signal.submoduleId,
"SUBMODULE");
404 EXPECT_EQ(signal.submoduleName,
"I/O data");
405 EXPECT_EQ(signal.moduleIdentNumber, 0x1000U);
406 EXPECT_EQ(signal.submoduleIdentNumber, 0x2000U);
407 EXPECT_EQ(signal.gsdmlDataType,
"Unsigned16");
408 EXPECT_EQ(signal.byteLength, 2U);
412TEST(IoSignalCatalogTests, CombinesAndNormalizesPossibleModuleSlots)
415 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
416 module.submodules.at("SUBMODULE").inputItems.push_back(MakeDataItem("Unsigned8", 1, "Sensor"));
417 device.
modules.emplace(module.id, module);
428 ASSERT_EQ(catalog.Sensors().size(), 1U);
429 EXPECT_EQ(catalog.Sensors()[0].possibleSlots, (std::vector<int>{1, 2, 3, 5, 7}));
433TEST(IoSignalCatalogTests, CombinesAndNormalizesPossibleInlineSubmoduleSlots)
436 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
437 auto& submodule =
module.submodules.at("SUBMODULE");
438 submodule.inputItems.push_back(MakeDataItem(
"Unsigned8", 1,
"Sensor"));
439 submodule.allowedSubslots = {4, 2, 4};
440 submodule.fixedSubslots = {1};
441 submodule.usedSubslots = {3, 2};
442 device.
modules.emplace(module.id, module);
446 ASSERT_EQ(catalog.Sensors().size(), 1U);
447 EXPECT_EQ(catalog.Sensors()[0].possibleSubslots, (std::vector<int>{1, 2, 3, 4}));
451TEST(IoSignalCatalogTests, IncludesAllModulesWhenNoModuleReferencesExist)
454 auto first = MakeModule(
"FIRST",
"First",
"SUB1",
"Input");
455 first.submodules.at(
"SUB1").inputItems.push_back(MakeDataItem(
"Boolean", 1,
"A"));
456 auto second = MakeModule(
"SECOND",
"Second",
"SUB2",
"Input");
457 second.submodules.at(
"SUB2").inputItems.push_back(MakeDataItem(
"Boolean", 1,
"B"));
459 device.
modules.emplace(first.id, first);
460 device.
modules.emplace(second.id, second);
464 ASSERT_EQ(catalog.Sensors().size(), 2U);
465 EXPECT_EQ(catalog.Sensors()[0].name,
"A");
466 EXPECT_EQ(catalog.Sensors()[1].name,
"B");
470TEST(IoSignalCatalogTests, UsesOnlyReferencedModulesWhenReferencesExist)
473 auto included = MakeModule(
"INCLUDED",
"Included",
"SUB",
"Input");
474 included.submodules.at(
"SUB").inputItems.push_back(MakeDataItem(
"Boolean", 1,
"Keep"));
475 auto excluded = MakeModule(
"EXCLUDED",
"Excluded",
"SUB",
"Input");
476 excluded.submodules.at(
"SUB").inputItems.push_back(MakeDataItem(
"Boolean", 1,
"Drop"));
478 device.
modules.emplace(included.id, included);
479 device.
modules.emplace(excluded.id, excluded);
484 ASSERT_EQ(catalog.Sensors().size(), 1U);
485 EXPECT_EQ(catalog.Sensors()[0].name,
"Keep");
489TEST(IoSignalCatalogTests, MergesDuplicateModuleReferences)
492 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
493 module.submodules.at("SUBMODULE").inputItems.push_back(MakeDataItem("Boolean", 1, "Ready"));
494 device.
modules.emplace(module.id, module);
497 MakeModuleReference(
"MODULE", {1, 2}));
499 MakeModuleReference(
"MODULE", {}, {}, {2, 4}));
503 ASSERT_EQ(catalog.Sensors().size(), 1U);
504 EXPECT_EQ(catalog.Sensors()[0].possibleSlots, (std::vector<int>{1, 2, 4}));
508TEST(IoSignalCatalogTests, ResolvesGlobalSubmoduleReference)
511 auto module = MakeModule("MODULE", "Module", "INLINE", "Inline");
512 module.submodules.clear();
517 module.submoduleReferences.push_back(reference);
520 global.
id =
"GLOBAL";
521 global.
name =
"Global I/O";
523 global.
inputItems.push_back(MakeDataItem(
"Unsigned16", 2,
"Position"));
525 device.
modules.emplace(module.id, module);
528 MakeModuleReference(
"MODULE", {1, 2}));
532 ASSERT_EQ(catalog.Sensors().size(), 1U);
533 EXPECT_EQ(catalog.Sensors()[0].submoduleId,
"GLOBAL");
534 EXPECT_EQ(catalog.Sensors()[0].submoduleName,
"Global I/O");
535 EXPECT_EQ(catalog.Sensors()[0].possibleSlots, (std::vector<int>{1, 2}));
536 EXPECT_EQ(catalog.Sensors()[0].possibleSubslots, (std::vector<int>{2, 4}));
540TEST(IoSignalCatalogTests, PrefersInlineSubmoduleOverDuplicateGlobalReference)
543 auto module = MakeModule("MODULE", "Module", "DUPLICATE", "Inline");
544 module.submodules.at("DUPLICATE").inputItems.push_back(MakeDataItem("Boolean", 1, "InlineSignal"));
545 module.submoduleReferences.push_back(MakeSubmoduleReference("DUPLICATE", {1, 2}));
548 global.
id =
"DUPLICATE";
549 global.
name =
"Global";
550 global.
inputItems.push_back(MakeDataItem(
"Boolean", 1,
"GlobalSignal"));
552 device.
modules.emplace(module.id, module);
557 ASSERT_EQ(catalog.Sensors().size(), 1U);
558 EXPECT_EQ(catalog.Sensors()[0].name,
"InlineSignal");
562TEST(IoSignalCatalogTests, UnknownModuleReferenceThrows)
566 MakeModuleReference(
"MISSING"));
572TEST(IoSignalCatalogTests, MergesDuplicateSubmoduleReferences)
575 auto module = MakeModule("MODULE", "Module", "INLINE", "Inline");
576 module.submodules.clear();
577 module.submoduleReferences.push_back(
578 MakeSubmoduleReference("GLOBAL", {1, 2}));
579 module.submoduleReferences.push_back(
580 MakeSubmoduleReference("GLOBAL", {}, {}, {2, 4}));
583 global.
id =
"GLOBAL";
584 global.
inputItems.push_back(MakeDataItem(
"Boolean", 1,
"Ready"));
586 device.
modules.emplace(module.id, module);
589 MakeModuleReference(
"MODULE"));
593 ASSERT_EQ(catalog.Sensors().size(), 1U);
594 EXPECT_EQ(catalog.Sensors()[0].possibleSubslots, (std::vector<int>{1, 2, 4}));
598TEST(IoSignalCatalogTests, UnknownSubmoduleReferenceThrows)
601 auto module = MakeModule("MODULE", "Module", "INLINE", "Inline");
602 module.submodules.clear();
603 module.submoduleReferences.push_back(
604 MakeSubmoduleReference("MISSING"));
605 device.
modules.emplace(module.id, module);
607 MakeModuleReference(
"MODULE"));
613TEST(IoSignalCatalogTests, IncludesDapVirtualSubmoduleSignals)
622 first.
name =
"DAP input";
624 first.
inputItems.push_back(MakeDataItem(
"Boolean", 1,
"Ready"));
628 second.
name =
"DAP output";
630 second.
outputItems.push_back(MakeDataItem(
"Boolean", 1,
"Enable"));
636 ASSERT_EQ(catalog.Sensors().size(), 1U);
637 ASSERT_EQ(catalog.Actuators().size(), 1U);
638 EXPECT_EQ(catalog.Sensors()[0].possibleSlots, (std::vector<int>{0}));
639 EXPECT_EQ(catalog.Sensors()[0].possibleSubslots, (std::vector<int>{1}));
640 EXPECT_EQ(catalog.Sensors()[0].moduleId,
"DAP");
641 EXPECT_EQ(catalog.Actuators()[0].possibleSlots, (std::vector<int>{0}));
642 EXPECT_EQ(catalog.Actuators()[0].possibleSubslots, (std::vector<int>{2}));
646TEST(IoSignalCatalogTests, RetainsDuplicateSignalNamesAcrossSubmodules)
649 auto first = MakeModule(
"FIRST",
"First",
"SUB1",
"Input 1");
650 first.submodules.at(
"SUB1").inputItems.push_back(MakeDataItem(
"Boolean", 1,
"Ready"));
651 auto second = MakeModule(
"SECOND",
"Second",
"SUB2",
"Input 2");
652 second.submodules.at(
"SUB2").inputItems.push_back(MakeDataItem(
"Boolean", 1,
"Ready"));
653 device.
modules.emplace(first.id, first);
654 device.
modules.emplace(second.id, second);
658 ASSERT_EQ(catalog.Sensors().size(), 2U);
659 EXPECT_EQ(catalog.Sensors()[0].name,
"Ready");
660 EXPECT_EQ(catalog.Sensors()[1].name,
"Ready");
661 EXPECT_NE(catalog.Sensors()[0].moduleId, catalog.Sensors()[1].moduleId);
665TEST(IoSignalCatalogTests, MaintainsDeterministicCatalogueOrder)
668 auto first = MakeModule(
"A",
"A",
"SUB_A",
"A");
669 first.submodules.at(
"SUB_A").inputItems = {
670 MakeDataItem(
"Boolean", 1,
"A0"),
671 MakeDataItem(
"Boolean", 1,
"A1"),
673 auto second = MakeModule(
"B",
"B",
"SUB_B",
"B");
674 second.submodules.at(
"SUB_B").inputItems = {
675 MakeDataItem(
"Boolean", 1,
"B0"),
678 device.
modules.emplace(second.id, second);
679 device.
modules.emplace(first.id, first);
683 ASSERT_EQ(catalog.Sensors().size(), 3U);
684 EXPECT_EQ(catalog.Sensors()[0].name,
"A0");
685 EXPECT_EQ(catalog.Sensors()[1].name,
"A1");
686 EXPECT_EQ(catalog.Sensors()[2].name,
"B0");
693 const std::string& textId,
const std::vector<std::pair<std::size_t, std::string>>& bits)
701 for (
const auto& [offset, bitTextId] : bits)
714TEST(IoSignalCatalogTests, ExpandsBitDataItemsIntoIndividualSignals)
718 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
720 auto& submodule =
module.submodules.at("SUBMODULE");
722 submodule.inputItems.push_back(MakeBitDataItem(
"Unsigned8",
725 {{0U,
"READY"}, {3U,
"FAULT"}, {7U,
"REMOTE"}}));
727 device.
modules.emplace(module.id, module);
731 ASSERT_EQ(catalog.Sensors().size(), 3U);
733 EXPECT_EQ(catalog.Sensors()[0].name,
"READY");
734 EXPECT_EQ(catalog.Sensors()[1].name,
"FAULT");
735 EXPECT_EQ(catalog.Sensors()[2].name,
"REMOTE");
737 EXPECT_TRUE(catalog.Sensors()[0].IsBitSignal());
738 EXPECT_TRUE(catalog.Sensors()[1].IsBitSignal());
739 EXPECT_TRUE(catalog.Sensors()[2].IsBitSignal());
741 EXPECT_EQ(catalog.Sensors()[0].bitOffset, 0U);
742 EXPECT_EQ(catalog.Sensors()[1].bitOffset, 3U);
743 EXPECT_EQ(catalog.Sensors()[2].bitOffset, 7U);
745 EXPECT_EQ(catalog.Sensors()[0].byteOffset, 0U);
746 EXPECT_EQ(catalog.Sensors()[1].byteOffset, 0U);
747 EXPECT_EQ(catalog.Sensors()[2].byteOffset, 0U);
749 EXPECT_EQ(catalog.Sensors()[0].byteLength, 1U);
750 EXPECT_EQ(catalog.Sensors()[1].byteLength, 1U);
751 EXPECT_EQ(catalog.Sensors()[2].byteLength, 1U);
753 EXPECT_EQ(catalog.Sensors()[0].parentDataItemTextId,
"STATUS");
754 EXPECT_EQ(catalog.Sensors()[1].parentDataItemTextId,
"STATUS");
755 EXPECT_EQ(catalog.Sensors()[2].parentDataItemTextId,
"STATUS");
759TEST(IoSignalCatalogTests, BitSignalsAreBooleanAndDoubleCompatible)
764 MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
766 auto& submodule =
module.submodules.at("SUBMODULE");
768 submodule.outputItems.push_back(MakeBitDataItem(
"Unsigned8",
771 {{0U,
"OPEN"}, {1U,
"CLOSE"}}));
773 device.
modules.emplace(module.id, module);
777 ASSERT_EQ(catalog.Actuators().size(), 2U);
779 for (
const auto& signal : catalog.Actuators())
781 EXPECT_TRUE(signal.IsBitSignal());
783 EXPECT_TRUE(signal.IsDoubleCompatible());
784 EXPECT_TRUE(signal.IsActuator());
789TEST(IoSignalCatalogTests, BitDataItemDoesNotCreateParentSignal)
793 auto module = MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
795 auto& submodule =
module.submodules.at("SUBMODULE");
797 submodule.inputItems.push_back(MakeBitDataItem(
"Unsigned8",
800 {{0U,
"READY"}, {1U,
"FAULT"}}));
802 device.
modules.emplace(module.id, module);
806 ASSERT_EQ(catalog.Sensors().size(), 2U);
808 EXPECT_EQ(catalog.Sensors()[0].name,
"READY");
809 EXPECT_EQ(catalog.Sensors()[1].name,
"FAULT");
811 EXPECT_NE(catalog.Sensors()[0].name,
"STATUS");
812 EXPECT_NE(catalog.Sensors()[1].name,
"STATUS");
816TEST(IoSignalCatalogTests, ConvertsBitOffsetToByteAndBitOffset)
821 MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
823 auto& submodule =
module.submodules.at("SUBMODULE");
825 submodule.inputItems = {
839 device.
modules.emplace(module.id, module);
844 ASSERT_EQ(catalog.Sensors().size(), 5U);
846 EXPECT_EQ(catalog.Sensors()[0].name,
"BIT_0");
847 EXPECT_EQ(catalog.Sensors()[0].byteOffset, 0U);
848 EXPECT_EQ(catalog.Sensors()[0].bitOffset, 0U);
850 EXPECT_EQ(catalog.Sensors()[1].name,
"BIT_7");
851 EXPECT_EQ(catalog.Sensors()[1].byteOffset, 0U);
852 EXPECT_EQ(catalog.Sensors()[1].bitOffset, 7U);
854 EXPECT_EQ(catalog.Sensors()[2].name,
"BIT_8");
855 EXPECT_EQ(catalog.Sensors()[2].byteOffset, 1U);
856 EXPECT_EQ(catalog.Sensors()[2].bitOffset, 0U);
858 EXPECT_EQ(catalog.Sensors()[3].name,
"BIT_10");
859 EXPECT_EQ(catalog.Sensors()[3].byteOffset, 1U);
860 EXPECT_EQ(catalog.Sensors()[3].bitOffset, 2U);
862 EXPECT_EQ(catalog.Sensors()[4].name,
"BIT_15");
863 EXPECT_EQ(catalog.Sensors()[4].byteOffset, 1U);
864 EXPECT_EQ(catalog.Sensors()[4].bitOffset, 7U);
867TEST(IoSignalCatalogTests, BitSignalsRetainParentDataItemOffset)
872 MakeModule("MODULE", "Module", "SUBMODULE", "Process data");
874 auto& submodule =
module.submodules.at("SUBMODULE");
876 submodule.inputItems = {
877 MakeDataItem(
"Unsigned8", 1,
"BEFORE"),
885 device.
modules.emplace(module.id, module);
890 ASSERT_EQ(catalog.Sensors().size(), 2U);
892 const auto& signal = catalog.Sensors()[1];
894 EXPECT_EQ(signal.parentDataItemByteOffset, 1U);
895 EXPECT_EQ(signal.byteOffset, 2U);
896 EXPECT_EQ(signal.bitOffset, 2U);
Defines the GSDML-derived logical process-data signal catalogue.
static IoSignalCatalog FromGsdml(const gsdml::GSDMLDevice &device)
Build a signal catalogue from a parsed GSDML device.
bool IsReservedSignal(std::string_view textId, std::string_view name) noexcept
Determine whether a DataItem should be hidden from the normal signal catalogue.
IoSignalValueType ClassifyDataType(std::string_view dataType) noexcept
Classify a GSDML DataType into a logical connector value category.
@ FloatingPoint
IEEE floating-point value.
@ Boolean
Boolean value, represented by the connector as 0.0 or 1.0.
@ SignedInteger
Signed integer value.
@ UnsignedInteger
Unsigned integer value.
@ Unsupported
Data for which this catalogue does not define a numeric double representation.
TEST(IoSignalTypeTests, ClassifiesAllDoubleCompatibleScalarTypes)
@ Sensor
Data received from the PROFINET device and therefore exposed as a sensor.
@ Actuator
Data sent to the PROFINET device and therefore exposed as an actuator.
One I/O data item within a submodule's cyclic data (GSDML DataItem element).
int length
Length in bytes of this item.
std::string dataType
GSDML DataType attribute, e.g. "Unsigned8", "Integer16", "OctetString".
std::vector< GSDMLBitDataItem > bitDataItems
Individual BitDataItem definitions belonging to this DataItem.
std::string textId
Human-readable text ID/name for this item, if the GSDML supplied one.
Complete parsed GSDML device profile.
std::map< std::string, GSDMLModule > modules
Every module declared in the GSDML's ModuleList, keyed by GSDML ID.
std::vector< GSDMLModuleReference > moduleReferences
Modules referenced by the DAP's UseableModules list.
std::vector< GSDMLSubmodule > dapSubmodules
DAP virtual submodules.
std::uint32_t dapModuleIdentNumber
The device access point's own module ident number.
std::map< std::string, GSDMLSubmodule > submodules
Every globally defined SubmoduleItem, keyed by GSDML ID.
std::string dapModuleName
The device access point's own (built-in) module name.
std::string dapModuleId
The device access point's own (built-in) identifier to matchs with the submodule.
Reference describing how a module may be used by the DAP.
std::string moduleId
Referenced ModuleItem ID.
std::vector< int > usedSlots
Slots where the module is currently used/preselected.
std::vector< int > allowedSlots
Slots where the module is allowed.
std::vector< int > fixedSlots
Slots where the module is fixed.
Reference to a submodule that may be used by a module.
std::vector< int > allowedSubslots
Subslots where this submodule is allowed.
std::string submoduleId
Referenced SubmoduleItem ID.
A GSDML virtual submodule definition (VirtualSubmoduleItem).
std::string name
Human-readable submodule name (from the GSDML's Name/TextId, resolved via ExternalTextList).
std::uint32_t submoduleIdentNumber
Submodule ident number (hex value from the GSDML, parsed to an integer).
std::vector< GSDMLDataItem > inputItems
Individual input data items, in wire order.
std::string id
GSDML ID attribute (used to resolve VirtualSubmoduleItemTarget references).
std::vector< GSDMLDataItem > outputItems
Individual output data items, in wire order.
Describes one logical process-data signal available from a GSDML device.
bool IsSensor() const noexcept
Returns whether this signal is an input/sensor.
bool IsDoubleCompatible() const noexcept
Returns whether this signal can be represented by the IO connector's double field.
bool IsActuator() const noexcept
Returns whether this signal is an output/actuator.
bool doubleCompatible
Whether the connector can represent this logical value using its double field.
IoSignalDirection direction
Sensor or actuator direction.