private void ExportDeviceProperty(ISODeviceElement deviceElement, NumericRepresentationValue representationValue, int objectID)
        {
            ISODeviceProperty dpt = new ISODeviceProperty();

            dpt.ObjectID = (uint)objectID;
            switch (representationValue.Representation.Code)
            {
            case "0043":
            case "0046":
                dpt.DDI        = representationValue.Representation.Code;
                dpt.Designator = "Width";
                dpt.Value      = representationValue.AsIntViaMappedDDI(RepresentationMapper);
                break;

            case "vrOffsetInline":
                dpt.DDI        = "0086";
                dpt.Designator = "XOffset";
                dpt.Value      = representationValue.AsIntViaMappedDDI(RepresentationMapper);
                break;

            case "vrOffsetLateral":
                dpt.DDI        = "0087";
                dpt.Designator = "YOffset";
                dpt.Value      = representationValue.AsIntViaMappedDDI(RepresentationMapper);
                break;

            case "vrOffsetVertical":
                dpt.DDI        = "0088";
                dpt.Designator = "ZOffset";
                dpt.Value      = representationValue.AsIntViaMappedDDI(RepresentationMapper);
                break;
            }

            if (!string.IsNullOrEmpty(dpt.DDI))
            {
                deviceElement.Device.DeviceProperties.Add(dpt);
                deviceElement.DeviceObjectReferences.Add(new ISODeviceObjectReference()
                {
                    DeviceObjectId = (uint)objectID
                });
            }
        }
        public DeviceElementHierarchy(ISODeviceElement deviceElement, int depth, RepresentationMapper representationMapper, HashSet <int> crawledElements = null, DeviceElementHierarchy parent = null)
        {
            RepresentationMapper = representationMapper;
            //This Hashset will track that we don't build infinite hierarchies.
            //The plugin does not support peer control at this time.
            _crawledElements = crawledElements;
            if (_crawledElements == null)
            {
                _crawledElements = new HashSet <int>();
            }

            if (_crawledElements.Add((int)deviceElement.DeviceElementObjectId))
            {
                Type          = deviceElement.DeviceElementType;
                DeviceElement = deviceElement;
                Depth         = depth;
                Order         = (int)deviceElement.DeviceElementNumber; //Using this number as analagous to this purpose.  The ISO spec requires these numbers increment from left to right as in ADAPT.  (See ISO11783-10:2015(E) B.3.2 Element number)

                //DeviceProperty assigned Widths & Offsets
                //DeviceProcessData assigned values will be assigned as the SectionMapper reads timelog data.

                //Width
                ISODeviceProperty widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0046");
                if (widthProperty != null)
                {
                    Width    = widthProperty.Value;
                    WidthDDI = "0046";
                }
                else
                {
                    widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0043");
                    if (widthProperty != null)
                    {
                        Width    = widthProperty.Value;
                        WidthDDI = "0043";
                    }
                }

                //Offsets
                ISODeviceProperty xOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0086");
                if (xOffsetProperty != null)
                {
                    XOffset = xOffsetProperty.Value;
                }

                ISODeviceProperty yOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0087");
                if (yOffsetProperty != null)
                {
                    YOffset = yOffsetProperty.Value;
                }

                ISODeviceProperty zOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0088");
                if (zOffsetProperty != null)
                {
                    ZOffset = zOffsetProperty.Value;
                }

                //Children
                IEnumerable <ISODeviceElement> childDeviceElements = deviceElement.Device.DeviceElements.Where(det => det.ParentObjectId == deviceElement.DeviceElementObjectId);// && det.DeviceElementType == ISOEnumerations.ISODeviceElementType.Section);
                if (childDeviceElements.Any())
                {
                    int childDepth = depth + 1;
                    Children = new List <DeviceElementHierarchy>();
                    foreach (ISODeviceElement det in childDeviceElements)
                    {
                        DeviceElementHierarchy child = new DeviceElementHierarchy(det, childDepth, representationMapper, _crawledElements, this);
                        Children.Add(child);
                    }
                }

                //Parent
                Parent = parent;
            }
        }
        public DeviceHierarchyElement(ISODeviceElement deviceElement,
                                      int depth,
                                      RepresentationMapper representationMapper,
                                      bool mergeSingleBinsIntoBoom,
                                      Dictionary <string, List <string> > missingGeometryDefinitions,
                                      HashSet <int> crawledElements = null,
                                      DeviceHierarchyElement parent = null)
        {
            RepresentationMapper = representationMapper;
            MergedElements       = new List <ISODeviceElement>();
            //This Hashset will track that we don't build infinite hierarchies.
            //The plugin does not support peer control at this time.
            _crawledElements = crawledElements;
            if (_crawledElements == null)
            {
                _crawledElements = new HashSet <int>();
            }

            if (_crawledElements.Add((int)deviceElement.DeviceElementObjectId))
            {
                Type          = deviceElement.DeviceElementType;
                DeviceElement = deviceElement;
                Depth         = depth;
                Order         = (int)deviceElement.DeviceElementNumber; //Using this number as analagous to this purpose.  The ISO spec requires these numbers increment from left to right as in ADAPT.  (See ISO11783-10:2015(E) B.3.2 Element number)

                //DeviceProperty assigned Widths & Offsets
                //DeviceProcessData assigned values will be assigned as the SectionMapper reads timelog data.

                //Width
                ISODeviceProperty widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0046"); //Max width
                if (widthProperty != null)
                {
                    Width    = widthProperty.Value;
                    WidthDDI = "0046";
                }
                else
                {
                    widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0044"); //Default working width
                    if (widthProperty != null)
                    {
                        Width    = widthProperty.Value;
                        WidthDDI = "0044";
                    }

                    if (widthProperty == null)
                    {
                        widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0043"); //Actual working width
                        if (widthProperty != null)
                        {
                            Width    = widthProperty.Value;
                            WidthDDI = "0043";
                        }
                    }
                }

                if (Width == null)
                {
                    //We are missing a width DPT.   Log it (0046 as a substitute here for any valid width) for possible retrieval from the TLG binaries.
                    AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0046");
                }

                //Offsets
                ISODeviceProperty xOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0086");
                if (xOffsetProperty != null)
                {
                    XOffset = xOffsetProperty.Value;
                }
                else
                {
                    AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0086");
                }

                ISODeviceProperty yOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0087");
                if (yOffsetProperty != null)
                {
                    YOffset = yOffsetProperty.Value;
                }
                else
                {
                    AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0087");
                }

                ISODeviceProperty zOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0088");
                if (zOffsetProperty != null)
                {
                    ZOffset = zOffsetProperty.Value;
                }
                else
                {
                    AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0088");
                }

                //Children
                IEnumerable <ISODeviceElement> childDeviceElements = deviceElement.Device.DeviceElements.Where(det => det.ParentObjectId == deviceElement.DeviceElementObjectId && det.ParentObjectId != det.DeviceElementObjectId); //Do not create children for an element classified as its own parent
                if (childDeviceElements.Any())
                {
                    int childDepth = depth + 1;
                    Children = new List <DeviceHierarchyElement>();
                    foreach (ISODeviceElement childDeviceElement in childDeviceElements)
                    {
                        //If there is a single bin child of the boom (usually alongside sections),
                        //we can logically combine the bin and boom to have a clean hierarchy
                        //where sections are the direct children of the element containing the rates.
                        //We currently use an import property (MergeSingleBinsIntoBoom) to enable this functionality.
                        //Note that if there are any duplicate DDIs on both the Bin and Boom (non-standard per Annex F.3),
                        //the FirstOrDefault() logic in the setter methods in the SpatialRecordMapper will prefer the Boom and suppress the Bin data.
                        if (mergeSingleBinsIntoBoom &&
                            (DeviceElement.DeviceElementType == ISODeviceElementType.Device || DeviceElement.DeviceElementType == ISODeviceElementType.Function) &&
                            childDeviceElement.DeviceElementType == ISODeviceElementType.Bin &&
                            childDeviceElements.Count(c => c.DeviceElementType == ISODeviceElementType.Bin) == 1)
                        {
                            //Set the element into the MergedElements list
                            MergedElements.Add(childDeviceElement);

                            //Set its children as children of the boom
                            foreach (ISODeviceElement binChild in childDeviceElement.ChildDeviceElements.Where(det => det.ParentObjectId == childDeviceElement.DeviceElementObjectId && det.ParentObjectId != det.DeviceElementObjectId))
                            {
                                Children.Add(new DeviceHierarchyElement(binChild, childDepth, representationMapper, mergeSingleBinsIntoBoom, missingGeometryDefinitions, _crawledElements, this));
                            }

                            //This functionality will not work in the ADAPT framework today for multiple bins on one boom (i.e., ISO 11783-10:2015(E) F.23 & F.33).
                            //For these, we will fall back to the more basic default functionality in HandleBinDeviceElements()
                            //where we separate bins and sections into different depths within the ADAPT device hierarchy.
                            //Plugin implementers will need to rationalize the separate bins to the single boom,
                            //with the rate for each bin associated to the corresponding DeviceElement in the ADAPT model.
                            //Were this multi-bin/single boom DDOP common, we could perhaps extend the WorkingData(?) class with some new piece of information
                            //To differentiate like data elements from different bins and thereby extend the merge functionality to this case.
                        }
                        else
                        {
                            //Add the child device element
                            DeviceHierarchyElement child = new DeviceHierarchyElement(childDeviceElement, childDepth, representationMapper, mergeSingleBinsIntoBoom, missingGeometryDefinitions, _crawledElements, this);
                            Children.Add(child);
                        }
                    }
                }

                //Parent
                Parent = parent;
            }
        }