AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
isobus_device_descriptor_object_pool.cpp
Go to the documentation of this file.
1//================================================================================================
8//================================================================================================
10
13#include "isobus/utility/platform_endianness.hpp"
14#include "isobus/utility/to_string.hpp"
15
16#include <algorithm>
17#include <cassert>
18#include <cstring>
19#include <iomanip>
20#include <sstream>
21
22namespace isobus
23{
24 DeviceDescriptorObjectPool::DeviceDescriptorObjectPool(std::uint8_t taskControllerServerVersion) :
25 taskControllerCompatibilityLevel(taskControllerServerVersion)
26 {
28 }
29
30 bool DeviceDescriptorObjectPool::add_device(std::string deviceDesignator,
31 std::string deviceSoftwareVersion,
32 std::string deviceSerialNumber,
33 std::string deviceStructureLabel,
34 std::array<std::uint8_t, task_controller_object::DeviceObject::MAX_STRUCTURE_AND_LOCALIZATION_LABEL_LENGTH> deviceLocalizationLabel,
35 std::vector<std::uint8_t> deviceExtendedStructureLabel,
36 std::uint64_t clientIsoNAME)
37 {
38 bool retVal = true;
39
40 for (auto &currentObject : objectList)
41 {
42 if (task_controller_object::ObjectTypes::Device == currentObject->get_object_type())
43 {
44 retVal = false;
45 break;
46 }
47 }
48
49 if (retVal)
50 {
53 {
54 LOG_WARNING("[DDOP]: Device designator " +
55 deviceDesignator +
56 " is greater than the max byte length of 32. Value will be truncated.");
58 }
61 {
62 LOG_WARNING("[DDOP]: Device designator " +
63 deviceDesignator +
64 " is greater than the max byte length of 128. Value will be truncated.");
66 }
69 {
70 LOG_INFO("[DDOP]: Device designator " +
71 deviceDesignator +
72 " byte length is greater than the max character count of 32. " +
73 "This is only acceptable if you have 32 or fewer UTF-8 characters!" +
74 " Please verify your DDOP configuration meets this requirement.");
75 }
76
79 {
80 LOG_WARNING("[DDOP]: Device serial number " +
81 deviceSerialNumber +
82 " is greater than the max byte length of 32. Value will be truncated.");
84 }
87 {
88 LOG_WARNING("[DDOP]: Device serial number " +
89 deviceSerialNumber +
90 " is greater than the max byte length of 128. Value will be truncated.");
92 }
95 {
96 LOG_INFO("[DDOP]: Device serial number " +
97 deviceSerialNumber +
98 " byte length is greater than the max character count of 32. " +
99 "This is only acceptable if you have 32 or fewer UTF-8 characters!" +
100 " Please verify your DDOP configuration meets this requirement.");
101 }
102
104 {
105 LOG_WARNING("[DDOP]: Device structure label " +
106 deviceStructureLabel +
107 " is greater than the max length of 7. Value will be truncated.");
109 }
111 {
112 LOG_WARNING("[DDOP]: Device extended structure label is greater than the max length of 32. Value will be truncated.");
114 }
115
116 if (deviceLocalizationLabel[6] != 0xFF)
117 {
118 LOG_WARNING("[DDOP]: Device localization label byte 7 must be the reserved value 0xFF. This value will be enforced when DDOP binary is generated.");
119 }
120 objectList.emplace_back(new task_controller_object::DeviceObject(deviceDesignator,
121 deviceSoftwareVersion,
122 deviceSerialNumber,
123 deviceStructureLabel,
124 deviceLocalizationLabel,
125 deviceExtendedStructureLabel,
126 clientIsoNAME,
128 }
129 else
130 {
131 LOG_ERROR("[DDOP]: Cannot add more than 1 Device object to a DDOP.");
132 }
133 return retVal;
134 }
135
136 bool DeviceDescriptorObjectPool::add_device_element(std::string deviceElementDesignator,
137 std::uint16_t deviceElementNumber,
138 std::uint16_t parentObjectID,
140 std::uint16_t uniqueID)
141 {
142 bool retVal = check_object_id_unique(uniqueID);
143
144 if (retVal)
145 {
146 // Object will be added
147 // Check for warnings
149 (deviceElementDesignator.size() > task_controller_object::Object::MAX_DESIGNATOR_LEGACY_LENGTH))
150 {
151 LOG_WARNING("[DDOP]: Device element designator " +
152 deviceElementDesignator +
153 " is greater than the max length of 32. Value will be truncated.");
155 }
157 (deviceElementDesignator.size() > task_controller_object::Object::MAX_DESIGNATOR_LENGTH))
158 {
159 LOG_WARNING("[DDOP]: Device element designator " +
160 deviceElementDesignator +
161 " is greater than the max length of 128. Value will be truncated.");
162 deviceElementDesignator.resize(task_controller_object::Object::MAX_DESIGNATOR_LENGTH);
163 }
164
165 objectList.emplace_back(new task_controller_object::DeviceElementObject(deviceElementDesignator,
166 deviceElementNumber,
167 parentObjectID,
168 deviceElementType,
169 uniqueID));
170 }
171 else
172 {
173 LOG_ERROR("[DDOP]: Device element ID " +
174 isobus::to_string(static_cast<int>(uniqueID)) +
175 " is not unique. Object will not be added to the DDOP.");
176 }
177 return retVal;
178 }
179
180 bool DeviceDescriptorObjectPool::add_device_process_data(std::string processDataDesignator,
181 std::uint16_t processDataDDI,
182 std::uint16_t deviceValuePresentationObjectID,
183 std::uint8_t processDataProperties,
184 std::uint8_t processDataTriggerMethods,
185 std::uint16_t uniqueID)
186 {
187 bool retVal = check_object_id_unique(uniqueID);
188
189 if (retVal)
190 {
191 // Object will be added
192 // Check for warnings
193 if ((processDataProperties & 0x02) && (processDataProperties & 0x04))
194 {
195 LOG_WARNING("[DDOP]: Process data object " +
196 isobus::to_string(static_cast<int>(uniqueID)) +
197 " has mutually exclusive options 'settable' and 'control source' set.");
198 }
199
202 {
203 LOG_WARNING("[DDOP]: Device process data designator " +
204 processDataDesignator +
205 " is greater than the max byte length of 32. Value will be truncated.");
207 }
209 (processDataDesignator.size() > task_controller_object::Object::MAX_DESIGNATOR_LENGTH))
210 {
211 LOG_WARNING("[DDOP]: Device process data designator " +
212 processDataDesignator +
213 " is greater than the max byte length of 128. Value will be truncated.");
214 processDataDesignator.resize(task_controller_object::Object::MAX_DESIGNATOR_LENGTH);
215 }
218 {
219 LOG_INFO("[DDOP]: Device process data designator " +
220 processDataDesignator +
221 " byte length is greater than the max character count of 32. " +
222 "This is only acceptable if you have 32 or fewer UTF-8 characters!" +
223 " Please verify your DDOP configuration meets this requirement.");
224 }
225
226 objectList.emplace_back(new task_controller_object::DeviceProcessDataObject(processDataDesignator,
227 processDataDDI,
228 deviceValuePresentationObjectID,
229 processDataProperties,
230 processDataTriggerMethods,
231 uniqueID));
232 }
233 else
234 {
235 LOG_ERROR("[DDOP]: Device process data ID " +
236 isobus::to_string(static_cast<int>(uniqueID)) +
237 " is not unique. Object will not be added to the DDOP.");
238 }
239 return retVal;
240 }
241
242 bool DeviceDescriptorObjectPool::add_device_property(std::string propertyDesignator,
243 std::int32_t propertyValue,
244 std::uint16_t propertyDDI,
245 std::uint16_t valuePresentationObject,
246 std::uint16_t uniqueID)
247 {
248 bool retVal = check_object_id_unique(uniqueID);
249
250 if (retVal)
251 {
252 // Object will be added
253 // Check for warnings
256 {
257 LOG_WARNING("[DDOP]: Device property designator " +
258 propertyDesignator +
259 " is greater than the max byte length of 32. Value will be truncated.");
261 }
263 (propertyDesignator.size() > task_controller_object::Object::MAX_DESIGNATOR_LENGTH))
264 {
265 LOG_WARNING("[DDOP]: Device property designator " +
266 propertyDesignator +
267 " is greater than the max byte length of 128. Value will be truncated.");
269 }
272 {
273 LOG_INFO("[DDOP]: Device property designator " +
274 propertyDesignator +
275 " byte length is greater than the max character count of 32. " +
276 "This is only acceptable if you have 32 or fewer UTF-8 characters!" +
277 " Please verify your DDOP configuration meets this requirement.");
278 }
279
280 objectList.emplace_back(new task_controller_object::DevicePropertyObject(propertyDesignator,
281 propertyValue,
282 propertyDDI,
283 valuePresentationObject,
284 uniqueID));
285 }
286 else
287 {
288 LOG_ERROR("[DDOP]: Device property ID " +
289 isobus::to_string(static_cast<int>(uniqueID)) +
290 " is not unique. Object will not be added to the DDOP.");
291 }
292 return retVal;
293 }
294
296 std::int32_t offsetValue,
297 float scaleFactor,
298 std::uint8_t numberDecimals,
299 std::uint16_t uniqueID)
300 {
301 bool retVal = check_object_id_unique(uniqueID);
302
303 if (retVal)
304 {
305 // Object will be added
306 // Check for warnings
309 {
310 LOG_WARNING("[DDOP]: Device value presentation unit designator " +
311 unitDesignator +
312 " is greater than the max byte length of 32. Value will be truncated.");
314 }
317 {
318 LOG_WARNING("[DDOP]: Device value presentation unit designator " +
319 unitDesignator +
320 " is greater than the max byte length of 128. Value will be truncated.");
322 }
325 {
326 LOG_INFO("[DDOP]: Device value presentation unit designator " +
327 unitDesignator +
328 " byte length is greater than the max character count of 32. " +
329 "This is only acceptable if you have 32 or fewer UTF-8 characters!" +
330 " Please verify your DDOP configuration meets this requirement.");
331 }
332
334 offsetValue,
335 scaleFactor,
336 numberDecimals,
337 uniqueID));
338 }
339 else
340 {
341 LOG_ERROR("[DDOP]: Device value presentation object ID " +
342 isobus::to_string(static_cast<int>(uniqueID)) +
343 " is not unique. Object will not be added to the DDOP.");
344 }
345 return retVal;
346 }
347
348 bool DeviceDescriptorObjectPool::deserialize_binary_object_pool(std::vector<std::uint8_t> &binaryPool, NAME clientNAME)
349 {
350 return deserialize_binary_object_pool(binaryPool.data(), static_cast<std::uint32_t>(binaryPool.size()), clientNAME);
351 }
352
353 bool DeviceDescriptorObjectPool::deserialize_binary_object_pool(const std::uint8_t *binaryPool, std::uint32_t binaryPoolSizeBytes, NAME clientNAME)
354 {
355 bool retVal = true;
356
357 if ((nullptr != binaryPool) && (0 != binaryPoolSizeBytes))
358 {
359 LOG_DEBUG("[DDOP]: Attempting to deserialize a binary object pool with size %u.", binaryPoolSizeBytes);
360 clear();
361
362 // Iterate over the DDOP and convert to objects.
363 // Using the size to track how much is left to process.
364 while (0 != binaryPoolSizeBytes)
365 {
366 // Verify there's enough data to read the XML namespace
367 if (binaryPoolSizeBytes > 3)
368 {
369 std::string xmlNameSpace;
370 xmlNameSpace.push_back(static_cast<char>(binaryPool[0]));
371 xmlNameSpace.push_back(static_cast<char>(binaryPool[1]));
372 xmlNameSpace.push_back(static_cast<char>(binaryPool[2]));
373
374 if ("DVC" == xmlNameSpace)
375 {
376 // Process a Device Object
377 std::uint8_t numberDesignatorBytes = 0; // Labelled "N" in 11783-10 table A.1
378 std::uint8_t numberSoftwareVersionBytes = 0; // Labelled "M" in 11783-10 table A.1
379 std::uint8_t numberDeviceSerialNumberBytes = 0; // Labelled "O" in 11783-10 table A.1
380 std::uint8_t numberExtendedStructureLabelBytes = 0; // Version 4+ only
381
382 if ((binaryPoolSizeBytes >= 6) &&
383 (binaryPool[5] < 128))
384 {
385 numberDesignatorBytes = binaryPool[5];
386 }
387 else
388 {
389 LOG_ERROR("[DDOP]: Binary device object designator has invalid length.");
390 retVal = false;
391 }
392
393 if ((binaryPoolSizeBytes >= static_cast<std::uint32_t>(7 + numberDesignatorBytes)) &&
394 (binaryPool[6 + numberDesignatorBytes] < 128))
395 {
396 numberSoftwareVersionBytes = binaryPool[6 + numberDesignatorBytes];
397 }
398 else
399 {
400 LOG_ERROR("[DDOP]: Binary device object software version has invalid length.");
401 retVal = false;
402 }
403
404 if ((binaryPoolSizeBytes >= static_cast<std::uint32_t>(16 + numberDesignatorBytes + numberSoftwareVersionBytes)) &&
405 (binaryPool[15 + numberDesignatorBytes + numberSoftwareVersionBytes] < 128))
406 {
407 numberDeviceSerialNumberBytes = binaryPool[15 + numberDesignatorBytes + numberSoftwareVersionBytes];
408 }
409 else
410 {
411 LOG_ERROR("[DDOP]: Binary device object serial number has invalid length.");
412 retVal = false;
413 }
414
416 {
417 if ((binaryPoolSizeBytes >= static_cast<std::uint32_t>(31 + numberDeviceSerialNumberBytes + numberDesignatorBytes + numberSoftwareVersionBytes)) &&
418 (binaryPool[30 + numberDeviceSerialNumberBytes + numberDesignatorBytes + numberSoftwareVersionBytes] <= 32))
419 {
420 numberExtendedStructureLabelBytes = binaryPool[30 + numberDeviceSerialNumberBytes + numberDesignatorBytes + numberSoftwareVersionBytes];
421 }
422 else
423 {
424 LOG_ERROR("[DDOP]: Binary device object with version 4 contains invalid extended structure label length.");
425 retVal = false;
426 }
427 }
428
429 std::uint32_t expectedSize = (31 + numberDeviceSerialNumberBytes + numberDesignatorBytes + numberSoftwareVersionBytes + numberExtendedStructureLabelBytes);
430
432 {
433 expectedSize--; // One byte less due to no extended structure label length
434 }
435
436 if ((binaryPoolSizeBytes >= expectedSize) && retVal)
437 {
438 std::string deviceDesignator;
439 std::string deviceSoftwareVersion;
440 std::string deviceSerialNumber;
441 std::string deviceStructureLabel;
442 std::array<std::uint8_t, 7> localizationLabel;
443 std::vector<std::uint8_t> extendedStructureLabel;
444 std::uint64_t ddopClientNAME = 0;
445
446 for (std::uint16_t i = 0; i < numberDesignatorBytes; i++)
447 {
448 deviceDesignator.push_back(binaryPool[6 + i]);
449 }
450
451 for (std::uint16_t i = 0; i < numberSoftwareVersionBytes; i++)
452 {
453 deviceSoftwareVersion.push_back(binaryPool[7 + numberDesignatorBytes + i]);
454 }
455
456 for (std::uint8_t i = 0; i < 8; i++)
457 {
458 std::uint64_t currentNameByte = binaryPool[7 + numberDesignatorBytes + numberSoftwareVersionBytes + i];
459 ddopClientNAME |= (currentNameByte << (8 * i));
460 }
461
462 if ((0 != clientNAME.get_full_name()) && (ddopClientNAME != clientNAME.get_full_name()))
463 {
464 LOG_ERROR("[DDOP]: Failed adding deserialized device object. DDOP NAME doesn't match client's actual NAME.");
465 retVal = false;
466 }
467 else if (0 == clientNAME.get_full_name())
468 {
469 clientNAME.set_full_name(ddopClientNAME);
470 }
471
472 for (std::uint16_t i = 0; i < numberDeviceSerialNumberBytes; i++)
473 {
474 deviceSerialNumber.push_back(binaryPool[16 + numberDesignatorBytes + numberSoftwareVersionBytes + i]);
475 }
476
477 for (std::uint16_t i = 0; i < 7; i++)
478 {
479 deviceStructureLabel.push_back(binaryPool[16 + numberDeviceSerialNumberBytes + numberDesignatorBytes + numberSoftwareVersionBytes + i]);
480 }
481
482 for (std::uint16_t i = 0; i < 7; i++)
483 {
484 localizationLabel.at(i) = (binaryPool[23 + numberDeviceSerialNumberBytes + numberDesignatorBytes + numberSoftwareVersionBytes + i]);
485 }
486
487 for (std::uint16_t i = 0; i < numberExtendedStructureLabelBytes; i++)
488 {
489 extendedStructureLabel.push_back(binaryPool[31 + numberDeviceSerialNumberBytes + numberDesignatorBytes + numberSoftwareVersionBytes + i]);
490 }
491
492 if (add_device(deviceDesignator, deviceSoftwareVersion, deviceSerialNumber, deviceStructureLabel, localizationLabel, extendedStructureLabel, clientNAME.get_full_name()))
493 {
494 binaryPoolSizeBytes -= expectedSize;
495 binaryPool += expectedSize;
496 }
497 else
498 {
499 LOG_ERROR("[DDOP]: Failed adding deserialized device object. DDOP schema is not valid.");
500 retVal = false;
501 }
502 }
503 else
504 {
505 LOG_ERROR("[DDOP]: Not enough binary DDOP data left to parse device object. DDOP schema is not valid");
506 retVal = false;
507 }
508 }
509 else if ("DET" == xmlNameSpace)
510 {
511 // Process a device element object
512 std::uint8_t numberDesignatorBytes = 0;
513 std::uint8_t numberOfObjectIDs = 0;
514
515 if (binaryPoolSizeBytes >= 7)
516 {
517 numberDesignatorBytes = binaryPool[6];
518 }
519 else
520 {
521 LOG_ERROR("[DDOP]: Binary device element object has invalid length.");
522 retVal = false;
523 }
524
525 if (binaryPoolSizeBytes >= static_cast<std::uint32_t>(12 + numberDesignatorBytes))
526 {
527 numberOfObjectIDs = binaryPool[11 + numberDesignatorBytes];
528 }
529 else
530 {
531 LOG_ERROR("[DDOP]: Binary device element object has invalid length to process referenced object IDs.");
532 retVal = false;
533 }
534
535 if (binaryPool[5] > static_cast<std::uint8_t>(task_controller_object::DeviceElementObject::Type::NavigationReference))
536 {
537 LOG_ERROR("[DDOP]: Binary device element object has invalid element type.");
538 retVal = false;
539 }
540
541 std::uint32_t expectedSize = (13 + (2 * numberOfObjectIDs) + numberDesignatorBytes);
542
543 if ((binaryPoolSizeBytes >= expectedSize) && retVal)
544 {
545 std::string deviceElementDesignator;
546 std::uint16_t parentObject = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[9 + numberDesignatorBytes]) | (static_cast<std::uint16_t>(binaryPool[10 + numberDesignatorBytes]) << 8));
547 std::uint16_t uniqueID = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[3]) | (static_cast<std::uint16_t>(binaryPool[4]) << 8));
548 std::uint16_t elementNumber = static_cast<std::uint16_t>(binaryPool[7 + numberDesignatorBytes]) | (static_cast<std::uint16_t>(binaryPool[8 + numberDesignatorBytes]) << 8);
549 auto type = static_cast<task_controller_object::DeviceElementObject::Type>(binaryPool[5]);
550
551 for (std::uint16_t i = 0; i < numberDesignatorBytes; i++)
552 {
553 deviceElementDesignator.push_back(binaryPool[7 + i]);
554 }
555
556 if (add_device_element(deviceElementDesignator, elementNumber, parentObject, type, uniqueID))
557 {
558 auto DETObject = std::static_pointer_cast<task_controller_object::DeviceElementObject>(get_object_by_id(uniqueID));
559
560 if (nullptr != DETObject)
561 {
562 for (std::uint8_t i = 0; i < numberOfObjectIDs; i++)
563 {
564 std::uint16_t childID = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[13 + (2 * i) + numberDesignatorBytes]) | (static_cast<std::uint16_t>(binaryPool[14 + (2 * i) + numberDesignatorBytes]) << 8));
565 DETObject->add_reference_to_child_object(childID);
566 }
567 }
568
569 binaryPoolSizeBytes -= expectedSize;
570 binaryPool += expectedSize;
571 }
572 else
573 {
574 LOG_ERROR("[DDOP]: Failed adding deserialized device element object. DDOP schema is not valid.");
575 retVal = false;
576 }
577 }
578 else
579 {
580 LOG_ERROR("[DDOP]: Not enough binary DDOP data left to parse device element object. DDOP schema is not valid");
581 retVal = false;
582 }
583 }
584 else if ("DPD" == xmlNameSpace)
585 {
586 // Process a device process data object
587 std::uint8_t numberDesignatorBytes = 0;
588
589 if ((binaryPoolSizeBytes >= 10) &&
590 (binaryPool[9] < 128))
591 {
592 numberDesignatorBytes = binaryPool[9];
593 }
594 else
595 {
596 LOG_ERROR("[DDOP]: Binary device process data object has invalid length.");
597 retVal = false;
598 }
599
600 std::uint32_t expectedSize = (12 + numberDesignatorBytes);
601
602 if ((binaryPoolSizeBytes >= expectedSize) && retVal)
603 {
604 std::string processDataDesignator;
605 std::uint16_t DDI = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[5]) | (static_cast<std::uint16_t>(binaryPool[6]) << 8));
606 std::uint16_t uniqueID = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[3]) | (static_cast<std::uint16_t>(binaryPool[4]) << 8));
607 std::uint16_t presentationObjectID = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[10 + numberDesignatorBytes]) | (static_cast<std::uint16_t>(binaryPool[11 + numberDesignatorBytes]) << 8));
608
609 for (std::uint16_t i = 0; i < numberDesignatorBytes; i++)
610 {
611 processDataDesignator.push_back(binaryPool[10 + i]);
612 }
613
614 if (add_device_process_data(processDataDesignator, DDI, presentationObjectID, binaryPool[7], binaryPool[8], uniqueID))
615 {
616 binaryPoolSizeBytes -= expectedSize;
617 binaryPool += expectedSize;
618 }
619 else
620 {
621 LOG_ERROR("[DDOP]: Failed adding deserialized device process data object. DDOP schema is not valid.");
622 retVal = false;
623 }
624 }
625 else
626 {
627 LOG_ERROR("[DDOP]: Not enough binary DDOP data left to parse device process data object. DDOP schema is not valid");
628 retVal = false;
629 }
630 }
631 else if ("DPT" == xmlNameSpace)
632 {
633 std::uint8_t numberDesignatorBytes = 0;
634
635 if ((binaryPoolSizeBytes >= 12) &&
636 (binaryPool[11] < 128))
637 {
638 numberDesignatorBytes = binaryPool[11];
639 }
640 else
641 {
642 LOG_ERROR("[DDOP]: Binary device property object has invalid length.");
643 retVal = false;
644 }
645
646 std::uint32_t expectedSize = (14 + numberDesignatorBytes);
647
648 if ((binaryPoolSizeBytes >= expectedSize) && retVal)
649 {
650 std::string designator;
651 std::int32_t propertyValue = static_cast<std::int32_t>(binaryPool[7]) |
652 (static_cast<std::int32_t>(binaryPool[8]) << 8) |
653 (static_cast<std::int32_t>(binaryPool[9]) << 16) |
654 (static_cast<std::int32_t>(binaryPool[10]) << 24);
655 std::uint16_t DDI = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[5]) | (static_cast<std::uint16_t>(binaryPool[6]) << 8));
656 std::uint16_t uniqueID = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[3]) | (static_cast<std::uint16_t>(binaryPool[4]) << 8));
657 std::uint16_t presentationObjectID = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[12 + numberDesignatorBytes]) | (static_cast<std::uint16_t>(binaryPool[13 + numberDesignatorBytes]) << 8));
658
659 for (std::uint16_t i = 0; i < numberDesignatorBytes; i++)
660 {
661 designator.push_back(binaryPool[12 + i]);
662 }
663
664 if (add_device_property(designator, propertyValue, DDI, presentationObjectID, uniqueID))
665 {
666 binaryPoolSizeBytes -= expectedSize;
667 binaryPool += expectedSize;
668 }
669 else
670 {
671 LOG_ERROR("[DDOP]: Failed adding deserialized device property object. DDOP schema is not valid.");
672 retVal = false;
673 }
674 }
675 else
676 {
677 LOG_ERROR("[DDOP]: Not enough binary DDOP data left to parse device property object. DDOP schema is not valid");
678 retVal = false;
679 }
680 }
681 else if ("DVP" == xmlNameSpace)
682 {
683 std::uint8_t numberDesignatorBytes = 0;
684
685 if ((binaryPoolSizeBytes >= 15) &&
686 (binaryPool[14] < 128))
687 {
688 numberDesignatorBytes = binaryPool[14];
689 }
690 else
691 {
692 LOG_ERROR("[DDOP]: Binary device value presentation object has invalid length.");
693 retVal = false;
694 }
695
696 std::uint32_t expectedSize = (15 + numberDesignatorBytes);
697
698 if ((binaryPoolSizeBytes >= expectedSize) && retVal)
699 {
700 std::string designator;
701 std::int32_t offset = static_cast<std::int32_t>(binaryPool[5]) |
702 (static_cast<std::int32_t>(binaryPool[6]) << 8) |
703 (static_cast<std::int32_t>(binaryPool[7]) << 16) |
704 (static_cast<std::int32_t>(binaryPool[8]) << 24);
705 std::array<std::uint8_t, sizeof(float)> scaleBytes = {
706 binaryPool[9],
707 binaryPool[10],
708 binaryPool[11],
709 binaryPool[12],
710 };
711 float scale = 0.0f;
712 std::uint16_t uniqueID = static_cast<std::uint16_t>(static_cast<std::uint16_t>(binaryPool[3]) | (static_cast<std::uint16_t>(binaryPool[4]) << 8));
713
714 if (is_big_endian())
715 {
716 std::reverse(scaleBytes.begin(), scaleBytes.end());
717 }
718
719 memcpy(&scale, scaleBytes.data(), sizeof(float));
720
721 for (std::uint16_t i = 0; i < numberDesignatorBytes; i++)
722 {
723 designator.push_back(binaryPool[15 + i]);
724 }
725
726 if (add_device_value_presentation(designator, offset, scale, binaryPool[13], uniqueID))
727 {
728 binaryPoolSizeBytes -= expectedSize;
729 binaryPool += expectedSize;
730 }
731 else
732 {
733 LOG_ERROR("[DDOP]: Failed adding deserialized device value presentation object. DDOP schema is not valid.");
734 retVal = false;
735 }
736 }
737 else
738 {
739 LOG_ERROR("[DDOP]: Not enough binary DDOP data left to parse device value presentation object. DDOP schema is not valid");
740 retVal = false;
741 }
742 }
743 else
744 {
745 LOG_ERROR("[DDOP]: Cannot process an unknown XML namespace from binary DDOP. DDOP schema is invalid.");
746 retVal = false;
747 }
748 }
749 else
750 {
751 retVal = false;
752 }
753
754 if (!retVal)
755 {
756 LOG_ERROR("[DDOP]: Binary DDOP deserialization aborted.");
757 break;
758 }
759 }
760 }
761 else
762 {
763 retVal = false;
764 LOG_ERROR("[DDOP]: Cannot deserialize a DDOP with zero length.");
765 }
766 return retVal;
767 }
768
769 bool DeviceDescriptorObjectPool::generate_binary_object_pool(std::vector<std::uint8_t> &resultantPool)
770 {
771 bool retVal = true;
772
773 resultantPool.clear();
774
776 {
777 LOG_WARNING("[DDOP]: A DDOP is being generated for a TC version that is unsupported. This may cause issues.");
778 }
779
781 {
782 retVal = true;
783 for (auto &currentObject : objectList)
784 {
785 auto objectBinary = currentObject->get_binary_object();
786
787 if (!objectBinary.empty())
788 {
789 resultantPool.insert(resultantPool.end(), objectBinary.begin(), objectBinary.end());
790 }
791 else
792 {
793 LOG_ERROR("[DDOP]: Failed to create all object binaries. Your DDOP is invalid.");
794 retVal = false;
795 break;
796 }
797 }
798 }
799 else
800 {
801 LOG_ERROR("[DDOP]: Failed to resolve all object IDs in DDOP. Your DDOP contains invalid object references.");
802 retVal = false;
803 }
804 return retVal;
805 }
806
808 {
809 bool retVal = true;
810
811 resultantString.clear();
812
814 {
815 LOG_WARNING("[DDOP]: An XML DDOP is being generated for a TC version that is unsupported. This may cause issues.");
816 }
817
819 {
820 std::ostringstream xmlOutput;
821 std::ios initialStreamFormat(NULL);
822 std::size_t numberOfDevices = 1;
823 std::size_t numberOfElements = 1;
824 initialStreamFormat.copyfmt(xmlOutput);
825 retVal = true;
826
827 xmlOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
828 xmlOutput << "<ISO11783_TaskData VersionMajor=\"3\" VersionMinor=\"0\" DataTransferOrigin=\"1\">" << std::endl;
829
830 // Find the device object, which will be the first object written
831 for (std::size_t i = 0; i < size(); i++)
832 {
833 auto currentObject = get_object_by_index(static_cast<std::uint16_t>(i));
834
835 if ((nullptr != currentObject) &&
836 (task_controller_object::ObjectTypes::Device == currentObject->get_object_type()))
837 {
838 // Found device
839 auto rootDevice = std::static_pointer_cast<task_controller_object::DeviceObject>(currentObject);
840 xmlOutput << "<DVC A=\"DVC-" << static_cast<int>(numberOfDevices);
841 numberOfDevices++;
842 xmlOutput << "\" B=\"" << rootDevice->get_designator();
843 xmlOutput << "\" C=\"" << rootDevice->get_software_version();
844 xmlOutput << "\" D=\"" << std::uppercase << std::hex << std::setfill('0') << std::setw(16) << static_cast<unsigned long long int>(rootDevice->get_iso_name());
845 xmlOutput.copyfmt(initialStreamFormat);
846 xmlOutput << "\" E=\"" << rootDevice->get_serial_number();
847 xmlOutput << "\" F=\"";
848
849 auto lStructureLabel = rootDevice->get_structure_label();
850 for (std::uint8_t j = 0; j < 7; j++)
851 {
852 std::uint8_t structureByte = static_cast<std::uint8_t>(lStructureLabel.at(6 - j));
853 xmlOutput << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(structureByte);
854 }
855
856 xmlOutput << "\" G=\"";
857
858 for (std::uint_fast8_t j = 0; j < 7; j++)
859 {
860 xmlOutput << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(rootDevice->get_localization_label().at(6 - j));
861 }
862 xmlOutput.copyfmt(initialStreamFormat);
863 xmlOutput << "\">" << std::endl;
864
865 // Next, process all elements
866 for (std::size_t j = 0; j < this->size(); j++)
867 {
868 auto currentSubObject = get_object_by_index(static_cast<std::uint16_t>(j));
869
870 if ((nullptr != currentSubObject) &&
871 (task_controller_object::ObjectTypes::DeviceElement == currentSubObject->get_object_type()))
872 {
873 auto deviceElement = std::static_pointer_cast<task_controller_object::DeviceElementObject>(currentSubObject);
874
875 xmlOutput << "\t<DET A=\"DET-" << static_cast<int>(numberOfElements);
876 numberOfElements++;
877 xmlOutput << "\" B=\"" << static_cast<int>(deviceElement->get_object_id());
878 xmlOutput << "\" C=\"" << static_cast<int>(deviceElement->get_type());
879 xmlOutput << "\" D=\"" << deviceElement->get_designator();
880 xmlOutput << "\" E=\"" << static_cast<int>(deviceElement->get_element_number());
881 xmlOutput << "\" F=\"" << static_cast<int>(deviceElement->get_parent_object());
882
883 if (deviceElement->get_number_child_objects() > 0)
884 {
885 xmlOutput << "\">" << std::endl;
886
887 // Process a list of all device object references
888 for (std::uint16_t k = 0; k < deviceElement->get_number_child_objects(); k++)
889 {
890 xmlOutput << "\t\t<DOR A=\"" << static_cast<int>(deviceElement->get_child_object_id(k)) << "\"/>" << std::endl;
891 }
892 xmlOutput << "\t</DET>" << std::endl;
893 }
894 else
895 {
896 xmlOutput << "\"/>" << std::endl;
897 }
898 }
899 }
900
901 // Next, process all DPDs
902 for (std::size_t j = 0; j < this->size(); j++)
903 {
904 auto currentSubObject = get_object_by_index(static_cast<std::uint16_t>(j));
905
906 if ((nullptr != currentSubObject) &&
907 (task_controller_object::ObjectTypes::DeviceProcessData == currentSubObject->get_object_type()))
908 {
909 auto deviceProcessData = std::static_pointer_cast<task_controller_object::DeviceProcessDataObject>(currentSubObject);
910
911 xmlOutput << "\t<DPD A=\"" << static_cast<int>(deviceProcessData->get_object_id());
912 xmlOutput << "\" B=\"" << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << static_cast<int>(deviceProcessData->get_ddi());
913 xmlOutput.copyfmt(initialStreamFormat);
914 xmlOutput << "\" C=\"" << static_cast<int>(deviceProcessData->get_properties_bitfield());
915 xmlOutput << "\" D=\"" << static_cast<int>(deviceProcessData->get_trigger_methods_bitfield());
916 xmlOutput << "\" E=\"" << deviceProcessData->get_designator();
917 if (0xFFFF != deviceProcessData->get_device_value_presentation_object_id())
918 {
919 xmlOutput << "\" F=\"" << static_cast<int>(deviceProcessData->get_device_value_presentation_object_id());
920 }
921 xmlOutput << "\"/>" << std::endl;
922 }
923 }
924
925 // Next, process all child DPTs
926 for (std::size_t j = 0; j < this->size(); j++)
927 {
928 auto currentSubObject = get_object_by_index(static_cast<std::uint16_t>(j));
929
930 if ((nullptr != currentSubObject) &&
931 (task_controller_object::ObjectTypes::DeviceProperty == currentSubObject->get_object_type()))
932 {
933 auto deviceProperty = std::static_pointer_cast<task_controller_object::DevicePropertyObject>(currentSubObject);
934
935 xmlOutput << "\t<DPT A=\"" << static_cast<int>(deviceProperty->get_object_id());
936 xmlOutput << "\" B=\"" << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << static_cast<int>(deviceProperty->get_ddi());
937 xmlOutput.copyfmt(initialStreamFormat);
938 xmlOutput << "\" C=\"" << static_cast<int>(deviceProperty->get_value());
939 xmlOutput << "\" D=\"" << deviceProperty->get_designator();
940 if (0xFFFF != deviceProperty->get_device_value_presentation_object_id())
941 {
942 xmlOutput << "\" E=\"" << static_cast<int>(deviceProperty->get_device_value_presentation_object_id());
943 }
944 xmlOutput << "\"/>" << std::endl;
945 }
946 }
947
948 // Next, process all child DVPs
949 for (std::size_t j = 0; j < this->size(); j++)
950 {
951 auto currentSubObject = get_object_by_index(static_cast<std::uint16_t>(j));
952
953 if ((nullptr != currentSubObject) &&
954 (task_controller_object::ObjectTypes::DeviceValuePresentation == currentSubObject->get_object_type()))
955 {
956 auto deviceValuePresentation = std::static_pointer_cast<task_controller_object::DeviceValuePresentationObject>(currentSubObject);
957
958 xmlOutput << "\t<DVP A=\"" << static_cast<int>(deviceValuePresentation->get_object_id());
959 xmlOutput << "\" B=\"" << static_cast<int>(deviceValuePresentation->get_offset());
960 xmlOutput << "\" C=\"" << std::fixed << std::setprecision(6) << deviceValuePresentation->get_scale();
961 xmlOutput.copyfmt(initialStreamFormat);
962 xmlOutput << "\" D=\"" << static_cast<int>(deviceValuePresentation->get_number_of_decimals());
963 xmlOutput << "\" E=\"" << deviceValuePresentation->get_designator();
964 xmlOutput << "\"/>" << std::endl;
965 }
966 }
967
968 // Close DVC object
969 xmlOutput << "</DVC>" << std::endl;
970 xmlOutput << "</ISO11783_TaskData>" << std::endl;
971 resultantString = xmlOutput.str();
972 LOG_DEBUG("[DDOP]: Generated ISO XML DDOP data OK");
973 break;
974 }
975 }
976 }
977 else
978 {
979 LOG_ERROR("[DDOP]: Failed to resolve all object IDs in DDOP. Your DDOP contains invalid object references.");
980 retVal = false;
981 }
982 return retVal;
983 }
984
985 std::shared_ptr<task_controller_object::Object> DeviceDescriptorObjectPool::get_object_by_id(std::uint16_t objectID)
986 {
987 std::shared_ptr<task_controller_object::Object> retVal;
988
989 for (auto &currentObject : objectList)
990 {
991 if (currentObject->get_object_id() == objectID)
992 {
993 retVal = currentObject;
994 break;
995 }
996 }
997 return retVal;
998 }
999
1000 std::shared_ptr<task_controller_object::Object> DeviceDescriptorObjectPool::get_object_by_index(std::uint16_t index)
1001 {
1002 std::shared_ptr<task_controller_object::Object> retVal = nullptr;
1003
1004 if (index < objectList.size())
1005 {
1006 retVal = objectList.at(index);
1007 }
1008 return retVal;
1009 }
1010
1012 {
1013 bool retVal = false;
1014
1015 for (auto object = objectList.begin(); object != objectList.end(); object++)
1016 {
1017 if ((nullptr != *object) && (*object)->get_object_id() == objectID)
1018 {
1019 objectList.erase(object);
1020 retVal = true;
1021 break;
1022 }
1023 }
1024 return retVal;
1025 }
1026
1028 {
1029 assert(tcVersion <= MAX_TC_VERSION_SUPPORTED); // You can't set the version higher than the max
1030
1032
1033 // Manipulate the device object if it exists
1034 auto deviceObject = std::static_pointer_cast<task_controller_object::DeviceObject>(get_object_by_id(0));
1035 if (nullptr != deviceObject)
1036 {
1037 deviceObject->set_use_extended_structure_label(taskControllerCompatibilityLevel >= 4);
1038 }
1039 }
1040
1045
1050
1052 {
1053 objectList.clear();
1054 }
1055
1057 {
1058 return static_cast<std::uint16_t>(objectList.size());
1059 }
1060
1062 {
1063 bool retVal = true;
1064
1065 for (auto &currentObject : objectList)
1066 {
1067 assert(nullptr != currentObject);
1068 switch (currentObject->get_object_type())
1069 {
1071 {
1072 // Process parent object
1073 auto currentDeviceElement = reinterpret_cast<task_controller_object::DeviceElementObject *>(currentObject.get());
1074 if (NULL_OBJECT_ID != currentDeviceElement->get_parent_object())
1075 {
1076 auto parent = get_object_by_id(currentDeviceElement->get_parent_object());
1077 if (nullptr != parent.get())
1078 {
1079 switch (parent->get_object_type())
1080 {
1083 {
1084 // Device and device element are allowed
1085 }
1086 break;
1087
1088 default:
1089 {
1090 LOG_ERROR("[DDOP]: Object " +
1091 isobus::to_string(static_cast<int>(currentObject->get_object_id())) +
1092 " has an invalid parent object type. Only device element objects or device objects may be its parent.");
1093 retVal = false;
1094 }
1095 break;
1096 }
1097 }
1098 else
1099 {
1100 LOG_ERROR("[DDOP]: Object " +
1101 isobus::to_string(static_cast<int>(currentDeviceElement->get_parent_object())) +
1102 " is not found.");
1103 retVal = false;
1104 }
1105 }
1106 else
1107 {
1108 LOG_ERROR("[DDOP]: Object " +
1109 isobus::to_string(static_cast<int>(currentObject->get_object_id())) +
1110 " is an orphan. It's parent is 0xFFFF!");
1111 retVal = false;
1112 }
1113
1114 if (retVal)
1115 {
1116 // Process children now that parent has been validated
1117 for (std::uint16_t i = 0; i < currentDeviceElement->get_number_child_objects(); i++)
1118 {
1119 auto child = get_object_by_id(currentDeviceElement->get_child_object_id(i));
1120 if (nullptr == child.get())
1121 {
1122 LOG_ERROR("[DDOP]: Object " +
1123 isobus::to_string(static_cast<int>(currentDeviceElement->get_child_object_id(i))) +
1124 " is not found.");
1125 retVal = false;
1126 break;
1127 }
1128 else if ((task_controller_object::ObjectTypes::DeviceProcessData != child->get_object_type()) &&
1129 (task_controller_object::ObjectTypes::DeviceProperty != child->get_object_type()))
1130 {
1131 LOG_ERROR("[DDOP]: Object %u has child %u which is an object type that is not allowed.",
1132 currentDeviceElement->get_child_object_id(i),
1133 child->get_object_id());
1134 LOG_ERROR("[DDOP]: A DET object may only have DPD and DPT children.");
1135 retVal = false;
1136 break;
1137 }
1138 }
1139 }
1140 }
1141 break;
1142
1144 {
1145 auto currentProcessData = reinterpret_cast<task_controller_object::DeviceProcessDataObject *>(currentObject.get());
1146
1147 if (NULL_OBJECT_ID != currentProcessData->get_device_value_presentation_object_id())
1148 {
1149 auto child = get_object_by_id(currentProcessData->get_device_value_presentation_object_id());
1150 if (nullptr == child.get())
1151 {
1152 LOG_ERROR("[DDOP]: Object " +
1153 isobus::to_string(static_cast<int>(currentProcessData->get_device_value_presentation_object_id())) +
1154 " is not found.");
1155 retVal = false;
1156 break;
1157 }
1158 else if (task_controller_object::ObjectTypes::DeviceValuePresentation != child->get_object_type())
1159 {
1160 LOG_ERROR("[DDOP]: Object %u has a child %u with an object type that is not allowed.",
1161 currentProcessData->get_device_value_presentation_object_id(),
1162 child->get_object_id());
1163 LOG_ERROR("[DDOP]: A DPD object may only have DVP children.");
1164 retVal = false;
1165 break;
1166 }
1167 }
1168 }
1169 break;
1170
1172 {
1173 auto currentProperty = reinterpret_cast<task_controller_object::DevicePropertyObject *>(currentObject.get());
1174
1175 if (NULL_OBJECT_ID != currentProperty->get_device_value_presentation_object_id())
1176 {
1177 auto child = get_object_by_id(currentProperty->get_device_value_presentation_object_id());
1178 if (nullptr == child.get())
1179 {
1180 LOG_ERROR("[DDOP]: Object " +
1181 isobus::to_string(static_cast<int>(currentProperty->get_device_value_presentation_object_id())) +
1182 " is not found.");
1183 retVal = false;
1184 break;
1185 }
1186 else if (task_controller_object::ObjectTypes::DeviceValuePresentation != child->get_object_type())
1187 {
1188 LOG_ERROR("[DDOP]: Object %u has a child %u with an object type that is not allowed.",
1189 currentProperty->get_device_value_presentation_object_id(),
1190 child->get_object_id());
1191 LOG_ERROR("[DDOP]: A DPT object may only have DVP children.");
1192 retVal = false;
1193 break;
1194 }
1195 }
1196 }
1197 break;
1198
1199 default:
1200 {
1201 // This object has no child/parent to validate
1202 }
1203 break;
1204 }
1205
1206 if (!retVal)
1207 {
1208 break;
1209 }
1210 }
1211 return retVal;
1212 }
1213
1215 {
1216 bool retVal = true;
1217
1218 if ((0 != uniqueID) && (NULL_OBJECT_ID != uniqueID))
1219 {
1220 for (auto &currentObject : objectList)
1221 {
1222 if (uniqueID == currentObject->get_object_id())
1223 {
1224 retVal = false;
1225 break;
1226 }
1227 }
1228 }
1229 else
1230 {
1231 retVal = false;
1232 }
1233 return retVal;
1234 }
1235
1236} // namespace isobus
General constants used throughout this library.
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
std::uint8_t taskControllerCompatibilityLevel
Stores the max TC version.
void clear()
Clears the DDOP back to an empty state.
std::shared_ptr< task_controller_object::Object > get_object_by_index(std::uint16_t index)
Gets an object from the DDOP by index based on object creation.
bool generate_task_data_iso_xml(std::string &resultantString)
bool add_device_element(std::string deviceElementDesignator, std::uint16_t deviceElementNumber, std::uint16_t parentObjectID, task_controller_object::DeviceElementObject::Type deviceElementType, std::uint16_t uniqueID)
Adds a device element object to the DDOP.
static constexpr std::uint8_t MAX_TC_VERSION_SUPPORTED
The max TC version a DDOP object can support as of today.
std::shared_ptr< task_controller_object::Object > get_object_by_id(std::uint16_t objectID)
Gets an object from the DDOP that corresponds to a certain object ID.
bool generate_binary_object_pool(std::vector< std::uint8_t > &resultantPool)
std::uint8_t get_task_controller_compatibility_level() const
Returns the current TC version used when generating a binary DDOP.
static std::uint8_t get_max_supported_task_controller_version()
Returns The maximum TC version supported by the CAN stack's DDOP generator.
bool remove_object_by_id(std::uint16_t objectID)
Removes an object from the DDOP using its object ID.
bool add_device(std::string deviceDesignator, std::string deviceSoftwareVersion, std::string deviceSerialNumber, std::string deviceStructureLabel, std::array< std::uint8_t, task_controller_object::DeviceObject::MAX_STRUCTURE_AND_LOCALIZATION_LABEL_LENGTH > deviceLocalizationLabel, std::vector< std::uint8_t > deviceExtendedStructureLabel, std::uint64_t clientIsoNAME)
Adds a device object to the DDOP.
std::vector< std::shared_ptr< task_controller_object::Object > > objectList
Maintains a list of all added objects.
DeviceDescriptorObjectPool()=default
Default constructor for a DDOP. Sets TC compatibility to version 4.
bool add_device_process_data(std::string processDataDesignator, std::uint16_t processDataDDI, std::uint16_t deviceValuePresentationObjectID, std::uint8_t processDataProperties, std::uint8_t processDataTriggerMethods, std::uint16_t uniqueID)
Adds a device process data object to the DDOP.
std::uint16_t size() const
Returns the number of objects in the DDOP.
void set_task_controller_compatibility_level(std::uint8_t tcVersion)
Sets the TC version to use when generating a binary DDOP.
bool resolve_parent_ids_to_objects()
Checks to see that all parent object IDs correspond to an object in this DDOP.
bool deserialize_binary_object_pool(std::vector< std::uint8_t > &binaryPool, NAME clientNAME=NAME(0))
Attempts to take a binary object pool and convert it back into C++ objects. Useful for a task control...
bool add_device_property(std::string propertyDesignator, std::int32_t propertyValue, std::uint16_t propertyDDI, std::uint16_t valuePresentationObject, std::uint16_t uniqueID)
Adds a device property object to the DDOP.
bool check_object_id_unique(std::uint16_t uniqueID) const
Checks the DDOP to see if an object ID has already been used.
bool add_device_value_presentation(std::string unitDesignator, std::int32_t offsetValue, float scaleFactor, std::uint8_t numberDecimals, std::uint16_t uniqueID)
Adds a device value presentation object to the DDOP.
A class that represents an ISO11783 control function NAME from an address claim.
Definition can_NAME.hpp:24
std::uint64_t get_full_name() const
Gets the raw 64 bit NAME.
Definition can_NAME.cpp:151
void set_full_name(std::uint64_t value)
Sets the raw, encoded 64 bit NAME.
Definition can_NAME.cpp:156
DeviceElementObject is the object definition of the XML element DeviceElement. The attribute Type spe...
@ NavigationReference
This device element type defines the navigation reference position for navigation devices such as GPS...
Each device shall have one single DeviceObject in its device descriptor object pool....
static constexpr std::size_t MAX_STRUCTURE_AND_LOCALIZATION_LABEL_LENGTH
Defines the max length of the device structure label and device localization label (in bytes)
static constexpr std::size_t MAX_EXTENDED_STRUCTURE_LABEL_LENGTH
Defines the max length of the device extended structure label (in bytes)
The DeviceProcessDataObject is the object definition of the XML element DeviceProcessData....
DevicePropertyObject is the object definition of the XML element DeviceProperty. Each object contains...
This object contains the presentation information to display the value of a DeviceProcessData or Devi...
static constexpr std::size_t MAX_DESIGNATOR_LEGACY_LENGTH
Defines the max length of a designator (in bytes) for TCs older than version 4.
static constexpr std::size_t MAX_DESIGNATOR_LENGTH
Defines the max length of a designator (in bytes)
Defines an interface for creating a Task Controller DDOP.
@ DeviceProperty
A device property element.
@ DeviceProcessData
Contains a single process data variable definition.
@ DeviceElement
Subcomponent of a device. Has multiple sub-types.
@ DeviceValuePresentation
Contains the presentation information to display the value of a DeviceProcessData or DeviceProperty o...
@ Device
The root object. Each device shall have one single Device.
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
constexpr std::uint16_t NULL_OBJECT_ID
Special ID used to indicate no object.