diff --git a/isobus/include/isobus/isobus/isobus_device_descriptor_object_pool_helpers.hpp b/isobus/include/isobus/isobus/isobus_device_descriptor_object_pool_helpers.hpp index 85624740..68cc8ec9 100644 --- a/isobus/include/isobus/isobus/isobus_device_descriptor_object_pool_helpers.hpp +++ b/isobus/include/isobus/isobus/isobus_device_descriptor_object_pool_helpers.hpp @@ -174,21 +174,21 @@ namespace isobus static ProductControlInformation parse_bin(DeviceDescriptorObjectPool &ddop, std::shared_ptr elementObject); - /// @brief Sets the value and presence based on a DDI match. + /// @brief Sets the value and settable flag based on a DDI match for an object. /// @param[in,out] objectPoolValue The object pool value to set. - /// @param[in] property The device property object. + /// @param[in] object The object to use to update the object pool value. /// @param[in] ddi The DDI to check against. - static void set_value_from_property(ObjectPoolValue &objectPoolValue, - const std::shared_ptr &property, - DataDescriptionIndex ddi); - - /// @brief Sets the settable flag based on a DDI match for process data. - /// @param[in,out] objectPoolValue The object pool value to update. - /// @param[in] processData The device process data object. - /// @param[in] ddi The DDI to check against. - static void set_editable_from_process_data(ObjectPoolValue &objectPoolValue, - const std::shared_ptr &processData, - DataDescriptionIndex ddi); + static void set_value_and_editable_from_object(ObjectPoolValue &objectPoolValue, + const std::shared_ptr &object, + DataDescriptionIndex ddi); + + /// @brief Sets the rate information based on the supplied object if the DDI is known to be a rate DDI. + /// @param rate The rate metadata to update. + /// @param object The object to use to update the product control information. + /// @param ddi The DDI to check against. + static void set_product_control_information_rate(RateMetadata &rate, + const std::shared_ptr &object, + std::uint16_t ddi); /// @brief Sets the max rate field of the product control information based on the supplied object /// if the DDI is known to be a max rate DDI. diff --git a/isobus/include/isobus/isobus/isobus_task_controller_client_objects.hpp b/isobus/include/isobus/isobus/isobus_task_controller_client_objects.hpp index fde28a90..f301283b 100644 --- a/isobus/include/isobus/isobus/isobus_task_controller_client_objects.hpp +++ b/isobus/include/isobus/isobus/isobus_task_controller_client_objects.hpp @@ -9,6 +9,8 @@ #ifndef ISOBUS_TASK_CONTROLLER_CLIENT_OBJECTS_HPP #define ISOBUS_TASK_CONTROLLER_CLIENT_OBJECTS_HPP +#include "isobus/utility/data_span.hpp" + #include #include #include @@ -22,11 +24,11 @@ namespace isobus /// @brief Enumerates the different kinds of DDOP objects enum class ObjectTypes { - Device, ///< The root object. Each device shall have one single Device - DeviceElement, ///< Subcomponent of a device. Has multiple sub-types - DeviceProcessData, ///< Contains a single process data variable definition - DeviceProperty, ///< A device property element - DeviceValuePresentation ///< Contains the presentation information to display the value of a DeviceProcessData or DeviceProperty object + Device, ///< The root object. Each device shall have one single Device (DVC) + DeviceElement, ///< Subcomponent of a device. Has multiple sub-types (DET) + DeviceProcessData, ///< Contains a single process data variable definition (DPD) + DeviceProperty, ///< A device property element (DPT) + DeviceValuePresentation ///< Contains the presentation information to display the value of a DeviceProcessData or DeviceProperty object (DVP) }; /// @brief A base class for a Task Controller Object @@ -272,6 +274,10 @@ namespace isobus /// @returns true if the child object ID was found and removed, otherwise false bool remove_reference_to_child_object(std::uint16_t childID); + /// @brief Returns a span of the child objects added with `add_reference_to_child_object`. + /// @returns A span of the child object IDs + DataSpan get_child_object_ids() const; + /// @brief Returns the number of child objects added with `add_reference_to_child_object`. /// @note The maximum number of child objects is technically 65535 because the serialized /// form of the value uses a 16-bit integer to store the count. @@ -368,6 +374,11 @@ namespace isobus /// @param[in] properties The new properties bitfield to set void set_properties_bitfield(std::uint8_t properties); + /// @brief Tests whether a property is set in the properties bitfield + /// @param property The property to test for + /// @returns `true` if the property is set, otherwise `false` + bool has_property(DeviceProcessDataObject::PropertiesBit property); + /// @brief Returns the object's available trigger methods /// @returns The available trigger methods bitfield for this object std::uint8_t get_trigger_methods_bitfield() const; diff --git a/isobus/src/isobus_device_descriptor_object_pool_helpers.cpp b/isobus/src/isobus_device_descriptor_object_pool_helpers.cpp index 2bab459a..cf428cb3 100644 --- a/isobus/src/isobus_device_descriptor_object_pool_helpers.cpp +++ b/isobus/src/isobus_device_descriptor_object_pool_helpers.cpp @@ -178,28 +178,18 @@ namespace isobus } // Find child DDIs that we care about - for (std::uint16_t i = 0; i < elementObject->get_number_child_objects(); i++) + for (std::uint16_t childObjectId : elementObject->get_child_object_ids()) { - auto child = ddop.get_object_by_id(elementObject->get_child_object_id(i)); + auto child = ddop.get_object_by_id(childObjectId); if (nullptr != child) { - if (task_controller_object::ObjectTypes::DeviceProperty == child->get_object_type()) - { - auto property = std::static_pointer_cast(child); - set_value_from_property(boomToPopulate.xOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetX); - set_value_from_property(boomToPopulate.yOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetY); - set_value_from_property(boomToPopulate.zOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetZ); - } - else if (task_controller_object::ObjectTypes::DeviceProcessData == child->get_object_type()) - { - auto processData = std::static_pointer_cast(child); - set_editable_from_process_data(boomToPopulate.xOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetX); - set_editable_from_process_data(boomToPopulate.yOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetY); - set_editable_from_process_data(boomToPopulate.zOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetZ); - } - else if ((task_controller_object::ObjectTypes::DeviceElement == child->get_object_type()) && - (task_controller_object::DeviceElementObject::Type::Bin == std::static_pointer_cast(child)->get_type())) + set_value_and_editable_from_object(boomToPopulate.xOffset_mm, child, DataDescriptionIndex::DeviceElementOffsetX); + set_value_and_editable_from_object(boomToPopulate.yOffset_mm, child, DataDescriptionIndex::DeviceElementOffsetY); + set_value_and_editable_from_object(boomToPopulate.zOffset_mm, child, DataDescriptionIndex::DeviceElementOffsetZ); + + if ((task_controller_object::ObjectTypes::DeviceElement == child->get_object_type()) && + (task_controller_object::DeviceElementObject::Type::Bin == std::static_pointer_cast(child)->get_type())) { auto binInfo = parse_bin(ddop, std::static_pointer_cast(child)); @@ -223,38 +213,27 @@ namespace isobus { Section retVal; - for (std::uint16_t i = 0; i < elementObject->get_number_child_objects(); i++) + for (std::uint16_t childObjectId : elementObject->get_child_object_ids()) { - auto sectionChildObject = ddop.get_object_by_id(elementObject->get_child_object_id(i)); + auto sectionChildObject = ddop.get_object_by_id(childObjectId); if (nullptr != sectionChildObject) { - if (task_controller_object::ObjectTypes::DeviceProperty == sectionChildObject->get_object_type()) + set_value_and_editable_from_object(retVal.xOffset_mm, sectionChildObject, DataDescriptionIndex::DeviceElementOffsetX); + set_value_and_editable_from_object(retVal.yOffset_mm, sectionChildObject, DataDescriptionIndex::DeviceElementOffsetY); + set_value_and_editable_from_object(retVal.zOffset_mm, sectionChildObject, DataDescriptionIndex::DeviceElementOffsetZ); + set_value_and_editable_from_object(retVal.width_mm, sectionChildObject, DataDescriptionIndex::ActualWorkingWidth); + if (!retVal.width_mm.exists()) { - auto property = std::static_pointer_cast(sectionChildObject); - set_value_from_property(retVal.xOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetX); - set_value_from_property(retVal.yOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetY); - set_value_from_property(retVal.zOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetZ); - set_value_from_property(retVal.width_mm, property, DataDescriptionIndex::ActualWorkingWidth); - if (!retVal.width_mm.exists()) - { - set_value_from_property(retVal.width_mm, property, DataDescriptionIndex::MaximumWorkingWidth); - } + set_value_and_editable_from_object(retVal.width_mm, sectionChildObject, DataDescriptionIndex::MaximumWorkingWidth); } - else if (task_controller_object::ObjectTypes::DeviceProcessData == sectionChildObject->get_object_type()) + if (!retVal.width_mm.exists()) { - auto processData = std::static_pointer_cast(sectionChildObject); - set_editable_from_process_data(retVal.xOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetX); - set_editable_from_process_data(retVal.yOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetY); - set_editable_from_process_data(retVal.zOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetZ); - set_editable_from_process_data(retVal.width_mm, processData, DataDescriptionIndex::ActualWorkingWidth); - if (!retVal.width_mm.exists()) - { - set_editable_from_process_data(retVal.width_mm, processData, DataDescriptionIndex::MaximumWorkingWidth); - } + set_value_and_editable_from_object(retVal.width_mm, sectionChildObject, DataDescriptionIndex::DefaultWorkingWidth); } - else if ((task_controller_object::ObjectTypes::DeviceElement == sectionChildObject->get_object_type()) && - (task_controller_object::DeviceElementObject::Type::Bin == std::static_pointer_cast(sectionChildObject)->get_type())) + + if ((task_controller_object::ObjectTypes::DeviceElement == sectionChildObject->get_object_type()) && + (task_controller_object::DeviceElementObject::Type::Bin == std::static_pointer_cast(sectionChildObject)->get_type())) { auto binInfo = parse_bin(ddop, std::static_pointer_cast(sectionChildObject)); @@ -291,38 +270,27 @@ namespace isobus } // Process child DDIs of this sub boom to locate offset and width - for (std::uint16_t i = 0; i < elementObject->get_number_child_objects(); i++) + for (std::uint16_t childObjectId : elementObject->get_child_object_ids()) { - auto childObject = ddop.get_object_by_id(elementObject->get_child_object_id(i)); + auto childObject = ddop.get_object_by_id(childObjectId); if (nullptr != childObject) { - if (task_controller_object::ObjectTypes::DeviceProperty == childObject->get_object_type()) + set_value_and_editable_from_object(retVal.xOffset_mm, childObject, DataDescriptionIndex::DeviceElementOffsetX); + set_value_and_editable_from_object(retVal.yOffset_mm, childObject, DataDescriptionIndex::DeviceElementOffsetY); + set_value_and_editable_from_object(retVal.zOffset_mm, childObject, DataDescriptionIndex::DeviceElementOffsetZ); + set_value_and_editable_from_object(retVal.width_mm, childObject, DataDescriptionIndex::ActualWorkingWidth); + if (!retVal.width_mm.exists()) { - auto property = std::static_pointer_cast(childObject); - set_value_from_property(retVal.xOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetX); - set_value_from_property(retVal.yOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetY); - set_value_from_property(retVal.zOffset_mm, property, DataDescriptionIndex::DeviceElementOffsetZ); - set_value_from_property(retVal.width_mm, property, DataDescriptionIndex::ActualWorkingWidth); - if (!retVal.width_mm.exists()) - { - set_value_from_property(retVal.width_mm, property, DataDescriptionIndex::MaximumWorkingWidth); - } + set_value_and_editable_from_object(retVal.width_mm, childObject, DataDescriptionIndex::MaximumWorkingWidth); } - else if (task_controller_object::ObjectTypes::DeviceProcessData == childObject->get_object_type()) + if (!retVal.width_mm.exists()) { - auto processData = std::static_pointer_cast(childObject); - set_editable_from_process_data(retVal.xOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetX); - set_editable_from_process_data(retVal.yOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetY); - set_editable_from_process_data(retVal.zOffset_mm, processData, DataDescriptionIndex::DeviceElementOffsetZ); - set_editable_from_process_data(retVal.width_mm, processData, DataDescriptionIndex::ActualWorkingWidth); - if (!retVal.width_mm.exists()) - { - set_editable_from_process_data(retVal.width_mm, processData, DataDescriptionIndex::MaximumWorkingWidth); - } + set_value_and_editable_from_object(retVal.width_mm, childObject, DataDescriptionIndex::DefaultWorkingWidth); } - else if ((task_controller_object::ObjectTypes::DeviceElement == childObject->get_object_type()) && - (task_controller_object::DeviceElementObject::Type::Bin == std::static_pointer_cast(childObject)->get_type())) + + if ((task_controller_object::ObjectTypes::DeviceElement == childObject->get_object_type()) && + (task_controller_object::DeviceElementObject::Type::Bin == std::static_pointer_cast(childObject)->get_type())) { auto binInfo = parse_bin(ddop, std::static_pointer_cast(childObject)); @@ -346,9 +314,9 @@ namespace isobus if (task_controller_object::DeviceElementObject::Type::Bin == elementObject->get_type()) { retVal.elementNumber = elementObject->get_element_number(); - for (std::uint16_t i = 0; i < elementObject->get_number_child_objects(); i++) + for (std::uint16_t childObjectId : elementObject->get_child_object_ids()) { - auto object = ddop.get_object_by_id(elementObject->get_child_object_id(i)); + auto object = ddop.get_object_by_id(childObjectId); if (nullptr != object) { @@ -376,24 +344,55 @@ namespace isobus return retVal; } - void DeviceDescriptorObjectPoolHelper::set_value_from_property(ObjectPoolValue &objectPoolValue, - const std::shared_ptr &property, - DataDescriptionIndex ddi) + void DeviceDescriptorObjectPoolHelper::set_value_and_editable_from_object(ObjectPoolValue &objectPoolValue, + const std::shared_ptr &object, + DataDescriptionIndex ddi) { - if (property->get_ddi() == static_cast(ddi)) + if (task_controller_object::ObjectTypes::DeviceProcessData == object->get_object_type()) { - objectPoolValue.value = property->get_value(); - objectPoolValue.isValuePresent = true; + auto processData = std::static_pointer_cast(object); + if (processData->get_ddi() == static_cast(ddi)) + { + objectPoolValue.isSettable = processData->has_property(task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable); + } + } + else if (task_controller_object::ObjectTypes::DeviceProperty == object->get_object_type()) + { + auto property = std::static_pointer_cast(object); + if (property->get_ddi() == static_cast(ddi)) + { + objectPoolValue.value = property->get_value(); + objectPoolValue.isValuePresent = true; + } } } - void DeviceDescriptorObjectPoolHelper::set_editable_from_process_data(ObjectPoolValue &objectPoolValue, - const std::shared_ptr &processData, - DataDescriptionIndex ddi) + void DeviceDescriptorObjectPoolHelper::set_product_control_information_rate(RateMetadata &rate, + const std::shared_ptr &object, + std::uint16_t ddi) { - if (processData->get_ddi() == static_cast(ddi)) + if (task_controller_object::ObjectTypes::DeviceProcessData == object->get_object_type()) + { + auto processData = std::static_pointer_cast(object); + + if (ddi == processData->get_ddi()) + { + rate.isSettable = (0 != (static_cast(task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable) & processData->get_properties_bitfield())); + rate.objectID = processData->get_object_id(); + rate.dataDictionaryIdentifier = processData->get_ddi(); + } + } + else if (task_controller_object::ObjectTypes::DeviceProperty == object->get_object_type()) { - objectPoolValue.isSettable = (0 != (static_cast(task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable) & processData->get_properties_bitfield())); + auto property = std::static_pointer_cast(object); + + if (ddi == property->get_ddi()) + { + rate.objectID = property->get_object_id(); + rate.isValuePresent = true; + rate.value = property->get_value(); + rate.dataDictionaryIdentifier = property->get_ddi(); + } } } @@ -420,29 +419,7 @@ namespace isobus case static_cast(DataDescriptionIndex::MaximumSpacingApplicationRate): case static_cast(DataDescriptionIndex::MaximumRevolutionsPerTime): { - if (task_controller_object::ObjectTypes::DeviceProcessData == object->get_object_type()) - { - auto processData = std::static_pointer_cast(object); - - if (ddi == processData->get_ddi()) - { - productControlInformation.rateMaximum.isSettable = (0 != (static_cast(task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable) & processData->get_properties_bitfield())); - productControlInformation.rateMaximum.objectID = processData->get_object_id(); - productControlInformation.rateMaximum.dataDictionaryIdentifier = processData->get_ddi(); - } - } - else if (task_controller_object::ObjectTypes::DeviceProperty == object->get_object_type()) - { - auto property = std::static_pointer_cast(object); - - if (ddi == property->get_ddi()) - { - productControlInformation.rateMaximum.objectID = property->get_object_id(); - productControlInformation.rateMaximum.isValuePresent = true; - productControlInformation.rateMaximum.value = property->get_value(); - productControlInformation.rateMaximum.dataDictionaryIdentifier = property->get_ddi(); - } - } + set_product_control_information_rate(productControlInformation.rateMaximum, object, ddi); } break; @@ -474,29 +451,7 @@ namespace isobus case static_cast(DataDescriptionIndex::MinimumSpacingApplicationRate): case static_cast(DataDescriptionIndex::MinimumRevolutionsPerTime): { - if (task_controller_object::ObjectTypes::DeviceProcessData == object->get_object_type()) - { - auto processData = std::static_pointer_cast(object); - - if (ddi == processData->get_ddi()) - { - productControlInformation.rateMinimum.isSettable = (0 != (static_cast(task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable) & processData->get_properties_bitfield())); - productControlInformation.rateMinimum.objectID = processData->get_object_id(); - productControlInformation.rateMinimum.dataDictionaryIdentifier = processData->get_ddi(); - } - } - else if (task_controller_object::ObjectTypes::DeviceProperty == object->get_object_type()) - { - auto property = std::static_pointer_cast(object); - - if (ddi == property->get_ddi()) - { - productControlInformation.rateMinimum.objectID = property->get_object_id(); - productControlInformation.rateMinimum.isValuePresent = true; - productControlInformation.rateMinimum.value = property->get_value(); - productControlInformation.rateMinimum.dataDictionaryIdentifier = property->get_ddi(); - } - } + set_product_control_information_rate(productControlInformation.rateMinimum, object, ddi); } break; @@ -523,29 +478,7 @@ namespace isobus case static_cast(DataDescriptionIndex::DefaultVolumePerTimeApplicationRate): case static_cast(DataDescriptionIndex::DefaultSpacingApplicationRate): { - if (task_controller_object::ObjectTypes::DeviceProcessData == object->get_object_type()) - { - auto processData = std::static_pointer_cast(object); - - if (ddi == processData->get_ddi()) - { - productControlInformation.rateDefault.isSettable = (0 != (static_cast(task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable) & processData->get_properties_bitfield())); - productControlInformation.rateDefault.objectID = processData->get_object_id(); - productControlInformation.rateDefault.dataDictionaryIdentifier = processData->get_ddi(); - } - } - else if (task_controller_object::ObjectTypes::DeviceProperty == object->get_object_type()) - { - auto property = std::static_pointer_cast(object); - - if (ddi == property->get_ddi()) - { - productControlInformation.rateDefault.objectID = property->get_object_id(); - productControlInformation.rateDefault.isValuePresent = true; - productControlInformation.rateDefault.value = property->get_value(); - productControlInformation.rateDefault.dataDictionaryIdentifier = property->get_ddi(); - } - } + set_product_control_information_rate(productControlInformation.rateDefault, object, ddi); } break; @@ -577,29 +510,7 @@ namespace isobus case static_cast(DataDescriptionIndex::SetpointSpacingApplicationRate): case static_cast(DataDescriptionIndex::SetpointRevolutionsSpecifiedAsCountPerTime): { - if (task_controller_object::ObjectTypes::DeviceProcessData == object->get_object_type()) - { - auto processData = std::static_pointer_cast(object); - - if (ddi == processData->get_ddi()) - { - productControlInformation.rateSetpoint.isSettable = (0 != (static_cast(task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable) & processData->get_properties_bitfield())); - productControlInformation.rateSetpoint.objectID = processData->get_object_id(); - productControlInformation.rateSetpoint.dataDictionaryIdentifier = processData->get_ddi(); - } - } - else if (task_controller_object::ObjectTypes::DeviceProperty == object->get_object_type()) - { - auto property = std::static_pointer_cast(object); - - if (ddi == property->get_ddi()) - { - productControlInformation.rateSetpoint.objectID = property->get_object_id(); - productControlInformation.rateSetpoint.isValuePresent = true; - productControlInformation.rateSetpoint.value = property->get_value(); - productControlInformation.rateSetpoint.dataDictionaryIdentifier = property->get_ddi(); - } - } + set_product_control_information_rate(productControlInformation.rateSetpoint, object, ddi); } break; @@ -631,29 +542,7 @@ namespace isobus case static_cast(DataDescriptionIndex::ActualSpacingApplicationRate): case static_cast(DataDescriptionIndex::ActualRevolutionsPerTime): { - if (task_controller_object::ObjectTypes::DeviceProcessData == object->get_object_type()) - { - auto processData = std::static_pointer_cast(object); - - if (ddi == processData->get_ddi()) - { - productControlInformation.rateActual.isSettable = (0 != (static_cast(task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable) & processData->get_properties_bitfield())); - productControlInformation.rateActual.objectID = processData->get_object_id(); - productControlInformation.rateActual.dataDictionaryIdentifier = processData->get_ddi(); - } - } - else if (task_controller_object::ObjectTypes::DeviceProperty == object->get_object_type()) - { - auto property = std::static_pointer_cast(object); - - if (ddi == property->get_ddi()) - { - productControlInformation.rateActual.objectID = property->get_object_id(); - productControlInformation.rateActual.isValuePresent = true; - productControlInformation.rateActual.value = property->get_value(); - productControlInformation.rateActual.dataDictionaryIdentifier = property->get_ddi(); - } - } + set_product_control_information_rate(productControlInformation.rateActual, object, ddi); } break; diff --git a/isobus/src/isobus_task_controller_client_objects.cpp b/isobus/src/isobus_task_controller_client_objects.cpp index 78c42a63..a68028e8 100644 --- a/isobus/src/isobus_task_controller_client_objects.cpp +++ b/isobus/src/isobus_task_controller_client_objects.cpp @@ -319,6 +319,11 @@ namespace isobus return retVal; } + DataSpan DeviceElementObject::get_child_object_ids() const + { + return DataSpan(referenceList.data(), referenceList.size()); + } + std::uint16_t DeviceElementObject::get_number_child_objects() const { return static_cast(referenceList.size()); @@ -416,6 +421,11 @@ namespace isobus propertiesBitfield = properties; } + bool DeviceProcessDataObject::has_property(DeviceProcessDataObject::PropertiesBit property) + { + return (0 != (propertiesBitfield & static_cast(property))); + } + std::uint8_t DeviceProcessDataObject::get_trigger_methods_bitfield() const { return triggerMethodsBitfield;