24[[nodiscard]]
bool EqualInsensitive(
const char lhs,
const char rhs)
noexcept
26 const auto left =
static_cast<unsigned char>(lhs);
27 const auto right =
static_cast<unsigned char>(rhs);
28 return std::tolower(left) == std::tolower(right);
32[[nodiscard]]
bool IsAsciiAlpha(
const char value)
noexcept
34 const auto character =
static_cast<unsigned char>(value);
35 return std::isalpha(character) != 0;
42[[nodiscard]]
bool ContainsReservedMarker(
const std::string_view value)
noexcept
44 constexpr std::string_view kReserve =
"RESERVE";
45 constexpr std::string_view kReserved =
"RESERVED";
46 constexpr std::string_view kNotAvailable =
"-";
48 for (std::size_t offset = 0; offset < value.size(); ++offset)
50 const auto matchesAt = [&](
const std::string_view marker)
52 if (offset + marker.size() > value.size())
57 for (std::size_t index = 0; index < marker.size(); ++index)
59 if (!EqualInsensitive(value[offset + index], marker[index]))
65 if (offset > 0 && IsAsciiAlpha(value[offset - 1]))
70 const std::size_t end = offset + marker.size();
71 if (end == value.size())
76 const char next = value[end];
77 return !IsAsciiAlpha(next) ||
78 (std::isupper(
static_cast<unsigned char>(next)) != 0);
81 if (matchesAt(kReserved) || matchesAt(kReserve) || matchesAt(kNotAvailable))
91void AddUnique(std::vector<int>& values,
const std::vector<int>& candidates)
93 for (
const int candidate : candidates)
95 if (std::ranges::find(values, candidate) == values.end())
97 values.push_back(candidate);
103void NormalizeLocations(std::vector<int>& values)
105 std::ranges::sort(values);
106 values.erase(std::ranges::unique(values).begin(), values.end());
110[[nodiscard]] std::string MakeSignalName(
const gsdml::GSDMLDataItem& item)
112 if (!item.name.empty())
117 if (!item.textId.empty())
126[[nodiscard]] IoSignal MakeSignal(
const gsdml::GSDMLDataItem& item,
128 const gsdml::GSDMLModule& module,
129 const gsdml::GSDMLSubmodule& submodule,
130 const std::size_t byteOffset,
131 const std::vector<int>& possibleSlots,
132 const std::vector<int>& possibleSubslots,
133 const gsdml::GSDMLBitDataItem* bit =
nullptr)
137 throw std::invalid_argument(
"GSDML DataItem has a negative length");
141 signal.direction = direction;
142 signal.name = bit !=
nullptr ? !bit->name.empty() ? bit->name : !bit->textId.empty() ? bit->textId
144 : MakeSignalName(item);
145 signal.textId = bit !=
nullptr ? bit->textId : item.textId;
146 signal.parentDataItemTextId = bit !=
nullptr ? item.textId :
"";
147 signal.bitSignal = bit !=
nullptr;
148 signal.parentDataItemByteOffset = byteOffset;
151 signal.byteOffset = byteOffset + (bit->bitOffset / 8U);
153 signal.bitOffset = bit->bitOffset % 8U;
157 signal.byteOffset = byteOffset;
158 signal.bitOffset = 0U;
160 signal.gsdmlDataType = item.dataType;
163 signal.moduleId =
module.id;
164 signal.moduleName =
module.name;
165 signal.submoduleId = submodule.id;
166 signal.submoduleName = submodule.name;
167 signal.moduleIdentNumber =
module.moduleIdentNumber;
168 signal.submoduleIdentNumber = submodule.submoduleIdentNumber;
170 signal.byteLength = bit !=
nullptr ? 1U :
static_cast<std::size_t
>(item.length);
171 signal.possibleSlots = possibleSlots;
172 signal.possibleSubslots = possibleSubslots;
178[[nodiscard]] std::vector<int> ResolveModuleSlots(
const gsdml::GSDMLDevice& device,
179 const std::string& moduleId)
181 std::vector<int> result;
183 for (
const auto& reference : device.moduleReferences)
185 if (reference.moduleId != moduleId)
190 AddUnique(result, reference.allowedSlots);
191 AddUnique(result, reference.fixedSlots);
192 AddUnique(result, reference.usedSlots);
195 NormalizeLocations(result);
200[[nodiscard]] std::vector<int> ResolveSubmoduleSlots(
const gsdml::GSDMLSubmodule& submodule)
202 std::vector<int> result;
203 AddUnique(result, submodule.allowedSubslots);
204 AddUnique(result, submodule.fixedSubslots);
205 AddUnique(result, submodule.usedSubslots);
206 NormalizeLocations(result);
211[[nodiscard]] std::vector<int> ResolveSubmoduleSlots(
const gsdml::GSDMLModule& module,
212 const std::string& submoduleId)
214 std::vector<int> result;
216 for (
const auto& reference : module.submoduleReferences)
218 if (reference.submoduleId != submoduleId)
223 AddUnique(result, reference.allowedSubslots);
224 AddUnique(result, reference.fixedSubslots);
225 AddUnique(result, reference.usedSubslots);
228 NormalizeLocations(result);
233void AddSubmoduleItems(
const gsdml::GSDMLModule& module,
234 const gsdml::GSDMLSubmodule& submodule,
235 const std::vector<int>& possibleSlots,
236 const std::vector<int>& possibleSubslots,
237 std::vector<IoSignal>& sensors,
238 std::vector<IoSignal>& actuators)
240 std::size_t inputOffset = 0;
241 for (
const auto& item : submodule.inputItems)
245 throw std::invalid_argument(
"GSDML DataItem has a negative length");
247 if (item.useAsBits && !item.bitDataItems.empty())
249 for (
const auto& bit : item.bitDataItems)
253 sensors.push_back(MakeSignal(item,
266 const std::string name = MakeSignalName(item);
269 sensors.push_back(MakeSignal(item,
278 inputOffset +=
static_cast<std::size_t
>(item.length);
281 std::size_t outputOffset = 0;
282 for (
const auto& item : submodule.outputItems)
286 throw std::invalid_argument(
"GSDML DataItem has a negative length");
288 if (item.useAsBits && !item.bitDataItems.empty())
290 for (
const auto& bit : item.bitDataItems)
308 const std::string name = MakeSignalName(item);
311 actuators.push_back(MakeSignal(item,
320 outputOffset +=
static_cast<std::size_t
>(item.length);
326void AddModule(
const gsdml::GSDMLDevice& device,
327 const gsdml::GSDMLModule& module,
328 const std::vector<int>& possibleSlots,
329 std::vector<IoSignal>& sensors,
330 std::vector<IoSignal>& actuators)
332 for (
const auto& [submoduleId, submodule] : module.submodules)
334 auto possibleSubslots = ResolveSubmoduleSlots(submodule);
335 const auto referenceSubslots = ResolveSubmoduleSlots(module, submoduleId);
336 AddUnique(possibleSubslots, referenceSubslots);
337 NormalizeLocations(possibleSubslots);
339 AddSubmoduleItems(module,
347 std::vector<std::string> processedSubmoduleIds;
349 for (
const auto& reference : module.submoduleReferences)
351 const auto inlineIt =
module.submodules.find(reference.submoduleId);
352 if (inlineIt != module.submodules.end())
357 if (std::ranges::find(processedSubmoduleIds, reference.submoduleId) !=
358 processedSubmoduleIds.end())
362 processedSubmoduleIds.push_back(reference.submoduleId);
364 const auto* submodule = device.FindSubmodule(reference.submoduleId);
365 if (submodule ==
nullptr)
367 throw std::invalid_argument(
368 "Module '" + module.id +
"' references unknown submodule '" +
369 reference.submoduleId +
"'");
372 auto possibleSubslots = ResolveSubmoduleSlots(*submodule);
373 const auto referenceSubslots = ResolveSubmoduleSlots(module, reference.submoduleId);
374 AddUnique(possibleSubslots, referenceSubslots);
375 NormalizeLocations(possibleSubslots);
377 AddSubmoduleItems(module,
387void AddDapSignals(
const gsdml::GSDMLDevice& device,
388 std::vector<IoSignal>& sensors,
389 std::vector<IoSignal>& actuators)
391 gsdml::GSDMLModule dapModule;
392 dapModule.id = device.dapModuleId;
393 dapModule.name = device.dapModuleName.empty() ? device.deviceName : device.dapModuleName;
394 dapModule.moduleIdentNumber = device.dapModuleIdentNumber;
396 for (std::size_t index = 0; index < device.dapSubmodules.size(); ++index)
398 const auto& submodule = device.dapSubmodules[index];
399 std::vector<int> possibleSlots{0};
400 std::vector<int> possibleSubslots{
static_cast<int>(index + 1U)};
402 AddSubmoduleItems(dapModule,
435 if ((dataType ==
"Boolean") || (dataType ==
"Bit"))
440 if ((dataType ==
"Integer8") || (dataType ==
"Integer16") ||
441 (dataType ==
"Integer32") || (dataType ==
"Integer64"))
446 if ((dataType ==
"Unsigned8") || (dataType ==
"Unsigned16") ||
447 (dataType ==
"Unsigned32") || (dataType ==
"Unsigned64"))
452 if ((dataType ==
"Float32") || (dataType ==
"Float64"))
460bool IsReservedSignal(
const std::string_view textId,
const std::string_view bitId,
const std::string_view name)
noexcept
462 return ContainsReservedMarker(textId) || ContainsReservedMarker(bitId) || ContainsReservedMarker(name);
467 return ContainsReservedMarker(textId) || ContainsReservedMarker(name);
478 for (
const auto& [moduleId, module] : device.
modules)
483 ResolveModuleSlots(device, module.id),
490 std::vector<std::string> processedModuleIds;
494 const auto*
module = device.FindModule(reference.moduleId);
495 if (module ==
nullptr)
497 throw std::invalid_argument(
498 "GSDML references unknown module '" + reference.moduleId +
"'");
501 const auto existing = std::ranges::find(processedModuleIds, reference.moduleId);
502 if (existing != processedModuleIds.end())
507 processedModuleIds.push_back(reference.moduleId);
509 std::vector<int> possibleSlots;
512 if (candidate.moduleId != reference.moduleId)
517 AddUnique(possibleSlots, candidate.allowedSlots);
518 AddUnique(possibleSlots, candidate.fixedSlots);
519 AddUnique(possibleSlots, candidate.usedSlots);
521 NormalizeLocations(possibleSlots);
Defines the GSDML-derived logical process-data signal catalogue.
Builds and owns the logical sensor and actuator signals described by a GSDML device.
const std::vector< IoSignal > & Actuators() const noexcept
Returns all non-reserved actuator/output signals.
std::size_t Size() const noexcept
Returns the total number of sensor and actuator signals.
std::vector< IoSignal > actuators
std::vector< IoSignal > sensors
static IoSignalCatalog FromGsdml(const gsdml::GSDMLDevice &device)
Build a signal catalogue from a parsed GSDML device.
const std::vector< IoSignal > & Sensors() const noexcept
Returns all non-reserved sensor/input signals.
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.
IoSignalValueType
Logical value category used when exposing a signal to higher layers.
@ 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.
IoSignalDirection
Logical direction of a process-data signal from the controller's perspective.
@ 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.
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.
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 IsBitSignal() const noexcept
Returns whether this signal represents one BitDataItem.
bool doubleCompatible
Whether the connector can represent this logical value using its double field.
bool bitSignal
Whether this logical signal originated from a BitDataItem.
IoSignalDirection direction
Sensor or actuator direction.