private static ImplementConfiguration AddImplementConfiguration(DeviceElement adaptDeviceElement, DeviceElementHierarchy deviceHierarchy, AgGateway.ADAPT.ApplicationDataModel.ADM.Catalog catalog)
        {
            ImplementConfiguration implementConfig = new ImplementConfiguration();

            //Description
            implementConfig.Description = $"{deviceHierarchy.DeviceElement.Device.DeviceDesignator} : {deviceHierarchy.DeviceElement.DeviceElementDesignator}";

            //Device Element ID
            implementConfig.DeviceElementId = adaptDeviceElement.Id.ReferenceId;

            //Offsets
            implementConfig.Offsets = new List <NumericRepresentationValue>();
            if (deviceHierarchy.XOffsetRepresentation != null)
            {
                implementConfig.Offsets.Add(deviceHierarchy.XOffsetRepresentation);
            }
            if (deviceHierarchy.YOffsetRepresentation != null)
            {
                implementConfig.Offsets.Add(deviceHierarchy.YOffsetRepresentation);
            }
            if (deviceHierarchy.ZOffsetRepresentation != null)
            {
                implementConfig.Offsets.Add(deviceHierarchy.ZOffsetRepresentation);
            }

            //Total Width
            if (deviceHierarchy.Width != null)
            {
                implementConfig.PhysicalWidth = deviceHierarchy.WidthRepresentation;
            }

            //Row Width
            NumericRepresentationValue rowWidth = deviceHierarchy.GetLowestLevelSectionWidth();

            if (rowWidth != null)
            {
                implementConfig.Width = rowWidth;
            }

            catalog.DeviceElementConfigurations.Add(implementConfig);

            return(implementConfig);
        }
        private static ImplementConfiguration AddImplementConfiguration(DeviceElement adaptDeviceElement, DeviceHierarchyElement deviceHierarchy, AgGateway.ADAPT.ApplicationDataModel.ADM.Catalog catalog)
        {
            ImplementConfiguration implementConfig = new ImplementConfiguration();

            //Description
            implementConfig.Description = $"{deviceHierarchy.DeviceElement.Device.DeviceDesignator} : {deviceHierarchy.DeviceElement.DeviceElementDesignator}";

            //Device Element ID
            implementConfig.DeviceElementId = adaptDeviceElement.Id.ReferenceId;

            //Offsets
            implementConfig.Offsets = new List <NumericRepresentationValue>();
            if (deviceHierarchy.XOffsetRepresentation != null)
            {
                implementConfig.Offsets.Add(deviceHierarchy.XOffsetRepresentation);
            }
            if (deviceHierarchy.YOffsetRepresentation != null)
            {
                implementConfig.Offsets.Add(deviceHierarchy.YOffsetRepresentation);
            }
            if (deviceHierarchy.ZOffsetRepresentation != null)
            {
                implementConfig.Offsets.Add(deviceHierarchy.ZOffsetRepresentation);
            }

            //Total Width
            if (deviceHierarchy.Width != null)
            {
                //In an earlier implementation, we used Width for an individual Row Width and Physical Width for the max width.
                //Going forward, we are adopting a more conventional definition that Width is any reported width of the implement, and
                //Physical width is a reported max width specifically.
                //Widths set from Timelog binaries will have already observed this convention as they did not contain the row width variation.
                implementConfig.Width = deviceHierarchy.WidthRepresentation;
                if (deviceHierarchy.WidthDDI == "0046")
                {
                    implementConfig.PhysicalWidth = deviceHierarchy.WidthRepresentation;
                }
            }

            catalog.DeviceElementConfigurations.Add(implementConfig);

            return(implementConfig);
        }
        private void ExportDeviceProperties(ISODeviceElement isoDeviceElement, DeviceElement adaptDeviceElement)
        {
            //Connectors
            if (isoDeviceElement.ChildDeviceElements != null)
            {
                foreach (ISODeviceElement connectorElement in isoDeviceElement.ChildDeviceElements.Where(d => d.DeviceElementType == ISODeviceElementType.Connector))
                {
                    int?connectorID = TaskDataMapper.InstanceIDMap.GetADAPTID(connectorElement.DeviceElementId);
                    if (connectorID.HasValue)
                    {
                        Connector  connector = DataModel.Catalog.Connectors.First(c => c.Id.ReferenceId == connectorID.Value);
                        HitchPoint hitch     = DataModel.Catalog.HitchPoints.FirstOrDefault(h => h.Id.ReferenceId == connector.HitchPointId);
                        if (hitch != null && hitch.ReferencePoint != null)
                        {
                            ExportDeviceProperty(connectorElement, hitch.ReferencePoint.XOffset, ++_devicePropertyObjectID);
                            ExportDeviceProperty(connectorElement, hitch.ReferencePoint.YOffset, ++_devicePropertyObjectID);
                            ExportDeviceProperty(connectorElement, hitch.ReferencePoint.ZOffset, ++_devicePropertyObjectID);
                        }
                    }
                }
            }

            //Device Element Widths & Offsets
            IEnumerable <DeviceElementConfiguration> configs = DataModel.Catalog.DeviceElementConfigurations.Where(c => c.DeviceElementId == adaptDeviceElement.Id.ReferenceId);

            foreach (DeviceElementConfiguration config in configs)
            {
                if (config is MachineConfiguration)
                {
                    MachineConfiguration machineConfig     = config as MachineConfiguration;
                    ISODeviceElement     navigationElement = isoDeviceElement.Device.DeviceElements.FirstOrDefault(d => d.DeviceElementType == ISODeviceElementType.Navigation);
                    if (navigationElement == null)
                    {
                        if (machineConfig.GpsReceiverXOffset != null)
                        {
                            ExportDeviceProperty(navigationElement, machineConfig.GpsReceiverXOffset, ++_devicePropertyObjectID);
                        }
                        if (machineConfig.GpsReceiverYOffset != null)
                        {
                            ExportDeviceProperty(navigationElement, machineConfig.GpsReceiverYOffset, ++_devicePropertyObjectID);
                        }
                        if (machineConfig.GpsReceiverZOffset != null)
                        {
                            ExportDeviceProperty(navigationElement, machineConfig.GpsReceiverZOffset, ++_devicePropertyObjectID);
                        }
                    }
                }
                else if (config is ImplementConfiguration)
                {
                    ImplementConfiguration implementConfig = config as ImplementConfiguration;
                    if (implementConfig.Width != null)
                    {
                        ExportDeviceProperty(isoDeviceElement, implementConfig.Width, ++_devicePropertyObjectID);
                    }
                    if (implementConfig.Offsets != null)
                    {
                        implementConfig.Offsets.ForEach(o => ExportDeviceProperty(isoDeviceElement, o, ++_devicePropertyObjectID));
                    }
                }
                else if (config is SectionConfiguration)
                {
                    SectionConfiguration sectionConfig = config as SectionConfiguration;
                    if (sectionConfig.InlineOffset != null)
                    {
                        ExportDeviceProperty(isoDeviceElement, sectionConfig.InlineOffset, ++_devicePropertyObjectID);
                    }
                    if (sectionConfig.LateralOffset != null)
                    {
                        ExportDeviceProperty(isoDeviceElement, sectionConfig.LateralOffset, ++_devicePropertyObjectID);
                    }
                    if (sectionConfig.SectionWidth != null)
                    {
                        ExportDeviceProperty(isoDeviceElement, sectionConfig.SectionWidth, ++_devicePropertyObjectID);
                    }
                }
            }
        }
        public void SetWidthsAndOffsetsFromSpatialData(IEnumerable <ISOSpatialRow> isoRecords, DeviceElementConfiguration config, RepresentationMapper representationMapper)
        {
            //Set values on this object and associated DeviceElementConfiguration
            if (Width == null)
            {
                Width = GetWidthFromSpatialData(isoRecords, DeviceElement.DeviceElementId, representationMapper);
            }

            if (config.Offsets == null)
            {
                config.Offsets = new List <NumericRepresentationValue>();
            }

            if (XOffset == null)
            {
                XOffset = GetXOffsetFromSpatialData(isoRecords, DeviceElement.DeviceElementId, representationMapper);
                if (XOffsetRepresentation != null)
                {
                    config.Offsets.Add(XOffsetRepresentation);
                }
            }

            if (YOffset == null)
            {
                YOffset = GetYOffsetFromSpatialData(isoRecords, DeviceElement.DeviceElementId, representationMapper);
                if (YOffsetRepresentation != null)
                {
                    config.Offsets.Add(YOffsetRepresentation);
                }
            }

            if (ZOffset == null)
            {
                ZOffset = GetZOffsetFromSpatialData(isoRecords, DeviceElement.DeviceElementId, representationMapper);
                if (ZOffsetRepresentation != null)
                {
                    config.Offsets.Add(ZOffsetRepresentation);
                }
            }

            //Update config values as appropriate
            if (this.DeviceElement.DeviceElementType == ISODeviceElementType.Navigation)
            {
                MachineConfiguration machineConfig = config as MachineConfiguration;
                if (machineConfig.GpsReceiverXOffset == null)
                {
                    machineConfig.GpsReceiverXOffset = XOffsetRepresentation;
                }
                if (machineConfig.GpsReceiverYOffset == null)
                {
                    machineConfig.GpsReceiverYOffset = YOffsetRepresentation;
                }
                if (machineConfig.GpsReceiverZOffset == null)
                {
                    machineConfig.GpsReceiverZOffset = ZOffsetRepresentation;
                }
            }
            else
            {
                if (config is SectionConfiguration)
                {
                    SectionConfiguration sectionConfig = config as SectionConfiguration;
                    if (sectionConfig.SectionWidth == null)
                    {
                        sectionConfig.SectionWidth = WidthRepresentation;
                    }
                    if (sectionConfig.InlineOffset == null)
                    {
                        sectionConfig.InlineOffset = XOffsetRepresentation;
                    }
                    if (sectionConfig.LateralOffset == null)
                    {
                        sectionConfig.LateralOffset = YOffsetRepresentation;
                    }
                }
                else if (config is ImplementConfiguration)
                {
                    ImplementConfiguration implementConfig = config as ImplementConfiguration;
                    if (implementConfig.Width == null)
                    {
                        implementConfig.Width = WidthRepresentation;
                    }
                }
            }
        }
        public static void DescribeImplement(Catalog catalog, LoggedData loggedData)
        {
            Console.WriteLine();
            Console.WriteLine("-----------------------");
            Console.WriteLine("Equipment Configuration");
            Console.WriteLine("-----------------------");
            Console.WriteLine();


            //A LoggedData will have a single EquipmentConfigurationGroup that contains any EquipmentConfigurations in the file
            EquipmentConfigurationGroup equipConfigGroup = loggedData.EquipmentConfigurationGroup;

            //The configuration of the equipment can vary by each region, although it is likely that the equipment configuration remains consistent across many regions
            //Any distinct configurations represented in the field operation will be EquipmentConfigurations within this group
            List <EquipmentConfiguration> distinctConfigurations = equipConfigGroup.EquipmentConfigurations;

            Console.WriteLine($"Field operation has {distinctConfigurations.Count} distinct equipment configuration(s).");
            Console.WriteLine();

            //While a single OperationData object supports a list of EquipmentConfigurations, the 2020 plugin will always reference a single EquipmentConfiguration on any one OperationData.
            //This allows the consumer to predictively map data based a known equipment definition for that data.

            //Going deeper on the first OperationData
            Console.WriteLine("The first region and OperationData within the field operation has this configuration:");
            EquipmentConfiguration equipConfig = distinctConfigurations.SingleOrDefault(c => c.Id.ReferenceId == loggedData.OperationData.First().EquipmentConfigurationIds.Single());

            //The equipment configuration maps to 2 connectors, explaining what machinery was hitched together
            Connector connector1 = catalog.Connectors.SingleOrDefault(c => c.Id.ReferenceId == equipConfig.Connector1Id);
            Connector connector2 = catalog.Connectors.SingleOrDefault(c => c.Id.ReferenceId == equipConfig.Connector2Id);

            //Each connector contains two pieces of information, the DeviceElementConfiguration that connector/hitch is a part of, and metadata on a specific hitch point.
            DeviceElementConfiguration deviceElementConfiguration1 = catalog.DeviceElementConfigurations.SingleOrDefault(c => c.Id.ReferenceId == connector1.DeviceElementConfigurationId);
            HitchPoint hitchPoint1 = catalog.HitchPoints.SingleOrDefault(h => h.Id.ReferenceId == connector1.HitchPointId);

            DeviceElementConfiguration deviceElementConfiguration2 = catalog.DeviceElementConfigurations.SingleOrDefault(c => c.Id.ReferenceId == connector2.DeviceElementConfigurationId);
            HitchPoint hitchPoint2 = catalog.HitchPoints.SingleOrDefault(h => h.Id.ReferenceId == connector2.HitchPointId);

            //DeviceElementConfigurations are a polymorphic object within ADAPT.
            //A DeviceElementConfiguration may be of type
            //  MachineConfiguration (describing a tractor/vehicle)
            //  ImplementConfiguration (describing an entire implement)
            //  SectionConfiguration (describing a subsection or individual row of an implement)

            //DeviceElementConfigurations are part of a 3-object hierarchy that describes a piece of equipment
            //1. DeviceModel - A high-level description of the equipment: brand, manufacturer, description.   Any single piece of equipment has only 1 device model.
            //2. DeviceElement -A hierarchical descripion part of the equipment: brand, manufacturer, description, and type of element (section, machine, implement, etc.).
            //                  A DeviceElement maps to a single DeviceModel, and may be a parent and/or child of other DeviceElements.
            //                  E.g., each section is a child of the root implement DeviceElement
            //3. DeviceElementConfigurations - The DeviceElementConfiguration is an extension of the DeviceElement, each mapping to a single DeviceElement,
            //                                  but having specific phyproperties such as width and offsets.

            //The 2020 equipment configuration will always have a Machine/Vehicle as the Connector1 and an Implement as the Connector2.
            MachineConfiguration   vehicleConfiguration   = deviceElementConfiguration1 as MachineConfiguration;
            ImplementConfiguration implementConfiguration = deviceElementConfiguration2 as ImplementConfiguration;

            HitchPoint vehicleHitch   = hitchPoint1;
            HitchPoint implementHitch = hitchPoint2;

            //The DeviceElements expose the hierarchy between parts of the equipment
            Console.WriteLine();
            Console.WriteLine("Vehicle DeviceElement Hierarchy:");
            DeviceElement vehicleDeviceElement = catalog.DeviceElements.SingleOrDefault(d => d.Id.ReferenceId == vehicleConfiguration.DeviceElementId);

            DescribeDeviceHierarchy(catalog, vehicleDeviceElement, 0, new List <DeviceElement>());

            Console.WriteLine();
            Console.WriteLine("Implement DeviceElement Hierarchy:");
            List <DeviceElement> implementChildElements = new List <DeviceElement>();
            DeviceElement        implementDeviceElement = catalog.DeviceElements.SingleOrDefault(d => d.Id.ReferenceId == implementConfiguration.DeviceElementId);

            DescribeDeviceHierarchy(catalog, implementDeviceElement, 0, implementChildElements);

            Console.WriteLine();


            Console.WriteLine();
            Console.WriteLine("-----------------------");
            Console.WriteLine("Implement Width Values");
            Console.WriteLine("-----------------------");
            Console.WriteLine();

            //The Implement and Section DeviceElementConfigurations carry width information.
            Console.WriteLine($"The {implementConfiguration.Description} is {implementConfiguration.PhysicalWidth.Value.Value} {implementConfiguration.PhysicalWidth.Value.UnitOfMeasure.Code} wide.");
            foreach (DeviceElement childElement in implementChildElements)
            {
                DeviceElementConfiguration deviceElementConfiguration = catalog.DeviceElementConfigurations.SingleOrDefault(c => c.DeviceElementId == childElement.Id.ReferenceId);
                if (deviceElementConfiguration != null)
                {
                    SectionConfiguration sectionConfiguration = deviceElementConfiguration as SectionConfiguration;
                    if (sectionConfiguration != null)
                    {
                        Console.WriteLine($"{sectionConfiguration.Description} is {sectionConfiguration.SectionWidth.Value.Value} {sectionConfiguration.SectionWidth.Value.UnitOfMeasure.Code} wide.");
                    }
                }
            }


            Console.WriteLine();
            Console.WriteLine("-----------------------");
            Console.WriteLine("Equipment Offset Values");
            Console.WriteLine("-----------------------");
            Console.WriteLine();

            //Various offset values describe where each device element is located vs. other elements via data on the device element configuration

            //Vehicle GPS Receiver
            DescribeOffset("GPS Receiver", vehicleConfiguration.GpsReceiverXOffset, vehicleConfiguration.GpsReceiverYOffset, "tractor reference point (center of rear axle)");

            //Tractor hitch offset
            DescribeOffset("vehicle hitch point", vehicleHitch.ReferencePoint.XOffset, vehicleHitch.ReferencePoint.YOffset, "tractor reference point (center of rear axle)");

            //Implement hitch offset
            DescribeOffset("implement hitch point", implementHitch.ReferencePoint.XOffset, implementHitch.ReferencePoint.YOffset, "implement reference point (center of implement)");

            //Implmement control point offset (inverse of the prior)
            DescribeOffset("implement control point offset", implementConfiguration.ControlPoint.XOffset, implementConfiguration.ControlPoint.YOffset, "tractor hitch point");

            //Section offsets (measured to center of each section)
            foreach (DeviceElement childElement in implementChildElements)
            {
                DeviceElementConfiguration deviceElementConfiguration = catalog.DeviceElementConfigurations.SingleOrDefault(c => c.DeviceElementId == childElement.Id.ReferenceId);
                if (deviceElementConfiguration != null)
                {
                    SectionConfiguration sectionConfiguration = deviceElementConfiguration as SectionConfiguration;
                    if (sectionConfiguration != null)
                    {
                        DescribeOffset($"{childElement.Description} offset", sectionConfiguration.InlineOffset, sectionConfiguration.LateralOffset, "tractor hitch point");
                    }
                }
            }
        }