private IEnumerable <DateTimeOffset> ExtractAllTimes(EHSNMidsecMeasDischargeMeasurementChannel channel)
        {
            var panels = channel.Panels ?? new EHSNMidsecMeasDischargeMeasurementChannelPanel[0];
            var edges  = channel.Edges ?? new EHSNMidsecMeasDischargeMeasurementChannelEdge[0];

            return(panels
                   .Select(panel => panel.Date)
                   .Concat(edges
                           .Select(edge => edge.Date))
                   .Select(dateTime => TimeHelper.CoerceDateTimeIntoUtcOffset(dateTime, _locationInfo.UtcOffset)));
        }
        private Vertical CreateEdgeVertical(Edge edge, VerticalType verticalType, MeterCalibration edgeMeter)
        {
            var taglinePosition = edge.Tagmark.ToNullableDouble() ?? 0;
            var depth           = edge.Depth.ToNullableDouble() ?? 0;
            var area            = edge.Area.ToNullableDouble() ?? 0;
            var velocity        = edge.Velocity.ToNullableDouble() ?? 0;
            var discharge       = edge.Discharge.ToNullableDouble() ?? 0;
            var width           = edge.Width.ToNullableDouble() ?? 0;
            var percentFlow     = edge.Flow.ToNullableDouble() ?? 0;

            var velocityObservation = new VelocityObservation
            {
                VelocityObservationMethod = PointVelocityObservationType.Surface,
                MeanVelocity     = velocity,
                DeploymentMethod = DeploymentMethodType.Unspecified,
                MeterCalibration = edgeMeter,
                Observations     =
                {
                    new VelocityDepthObservation
                    {
                        Depth               = depth,
                        Velocity            = velocity,
                        ObservationInterval = 0,
                        RevolutionCount     = 0
                    },
                },
            };

            return(new Vertical
            {
                VerticalType = verticalType,
                MeasurementTime = TimeHelper.CoerceDateTimeIntoUtcOffset(edge.Date, LocationInfo.UtcOffset),
                SequenceNumber = edge.panelId,
                TaglinePosition = taglinePosition,
                SoundedDepth = depth,
                EffectiveDepth = depth,
                MeasurementConditionData = new OpenWaterData(),
                FlowDirection = FlowDirectionType.Normal,
                VelocityObservation = velocityObservation,
                Segment = new Segment
                {
                    Area = area,
                    Discharge = discharge,
                    Width = width,
                    Velocity = velocity,
                    TotalDischargePortion = percentFlow,
                }
            });
        }
        private Vertical CreatePanelVertical(Panel panel, MeterCalibration meter)
        {
            var taglinePosition = panel.Tagmark.ToNullableDouble() ?? 0;
            var soundedDepth    = panel.DepthReading.ToNullableDouble() ?? 0;
            var effectiveDepth  = panel.DepthWithOffset.ToNullableDouble() ?? soundedDepth;
            var velocity        = panel.AverageVelocity.ToNullableDouble() ?? 0;
            var discharge       = panel.Discharge.ToNullableDouble() ?? 0;
            var width           = panel.Width.ToNullableDouble() ?? 0;
            var percentFlow     = panel.Flow.ToNullableDouble() ?? 0;

            var waterSurfaceToBottomOfIce   = panel.IceCovered?.WSToBottomOfIceAdjusted.ToNullableDouble() ?? 0;
            var waterSurfaceToBottomOfSlush = panel.IceCovered?.WaterSurfaceToBottomOfSlush.ToNullableDouble() ?? waterSurfaceToBottomOfIce;

            string comments        = null;
            var    distanceToMeter = panel.Open?.DistanceAboveWeight.ToNullableDouble();

            if (distanceToMeter > soundedDepth)
            {
                comments        = $"Original DistanceToMeter={distanceToMeter:F2} {Units.DistanceUnitId} updated to SoundedDepth={soundedDepth:F2} {Units.DistanceUnitId} by eHSN plugin.";
                distanceToMeter = soundedDepth;
            }

            var measurementCondition = panel.IceCovered != null
                ? (MeasurementConditionData) new IceCoveredData
            {
                IceAssemblyType             = panel.IceCovered.IceAssembly,
                IceThickness                = panel.IceCovered.IceThickness.ToNullableDouble(),
                AboveFooting                = panel.IceCovered.MeterAboveFooting.ToNullableDouble(),
                BelowFooting                = panel.IceCovered.MeterBelowFooting.ToNullableDouble(),
                WaterSurfaceToBottomOfIce   = waterSurfaceToBottomOfIce,
                WaterSurfaceToBottomOfSlush = waterSurfaceToBottomOfSlush,
            }
                : new OpenWaterData
            {
                DistanceToMeter   = distanceToMeter,
                DryLineAngle      = panel.DryAngle.ToNullableDouble() ?? 0,
                DryLineCorrection = panel.DryCorrection.ToNullableDouble(),
                WetLineCorrection = panel.WetCorrection.ToNullableDouble(),
                SuspensionWeight  = panel.Open?.AmountOfWeight
            };

            effectiveDepth = panel.IceCovered?.EffectiveDepth.ToNullableDouble() ?? effectiveDepth;

            var points = panel.PointMeasurements ?? new PointMeasurement[0];

            var fractionalDepths = string.Join("/", points.Select(p => p.SamplingDepthCoefficient));

            if (!PointVelocityTypes.TryGetValue(fractionalDepths, out var pointVelocityObservationType))
            {
                if (!points.Any())
                {
                    pointVelocityObservationType = PointVelocityObservationType.Surface;
                    soundedDepth = effectiveDepth = 0;
                }
                else
                {
                    throw new ArgumentException($"'{fractionalDepths}' is not a supported point velocity observation type");
                }
            }

            var velocityObservation = new VelocityObservation
            {
                VelocityObservationMethod = pointVelocityObservationType,
                MeanVelocity     = velocity,
                DeploymentMethod = GetPanelDeploymentMethod(panel),
                MeterCalibration = meter
            };

            if (!points.Any())
            {
                velocityObservation.Observations.Add(new VelocityDepthObservation
                {
                    Depth               = 0,
                    Velocity            = 0,
                    ObservationInterval = 0,
                    RevolutionCount     = 0
                });
            }
            else
            {
                foreach (var point in points)
                {
                    velocityObservation.Observations.Add(new VelocityDepthObservation
                    {
                        Depth               = point.MeasurementDepth.ToNullableDouble() ?? 0,
                        Velocity            = point.Velocity.ToNullableDouble() ?? 0,
                        ObservationInterval = point.ElapsedTime.ToNullableDouble(),
                        RevolutionCount     = point.Revolutions
                    });
                }
            }

            var vertical = new Vertical
            {
                VerticalType             = VerticalType.MidRiver,
                Comments                 = comments,
                MeasurementTime          = TimeHelper.CoerceDateTimeIntoUtcOffset(panel.Date, LocationInfo.UtcOffset),
                SequenceNumber           = panel.panelId,
                TaglinePosition          = taglinePosition,
                SoundedDepth             = soundedDepth,
                EffectiveDepth           = effectiveDepth,
                MeasurementConditionData = measurementCondition,
                FlowDirection            = panel.ReverseFlow.ToBoolean()
                    ? FlowDirectionType.Reversed
                    : FlowDirectionType.Normal,
                VelocityObservation = velocityObservation,
                Segment             = new Segment
                {
                    Area                  = soundedDepth * width, // We need to infer the area
                    Discharge             = discharge,
                    Width                 = width,
                    Velocity              = velocity,
                    TotalDischargePortion = percentFlow,
                }
            };

            return(vertical);
        }