// Copyright (C) Stichting Deltares 2018. All rights reserved. // // This file is part of the application DAM - UI. // // DAM - UI is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Xml.Serialization; using Deltares.Dam.Data.Sensors.Specifications; using Deltares.Geotechnics.SurfaceLines; using Deltares.Standard; using Deltares.Standard.Attributes; using Deltares.Standard.IO.Xml; using Deltares.Standard.Reflection; using Deltares.Standard.Specifications; using Deltares.Standard.Specifications.Extensions; using Validator = Deltares.Standard.Specifications.Validator; using XmlSerializer = Deltares.Standard.IO.Xml.XmlSerializer; namespace Deltares.Dam.Data.Sensors { /// /// Association class for sensor data and a location /// [Serializable] public class SensorLocation: IDomain { #region Business Rules /// /// Specication to test if the value of the candidate is valid /// internal class IgnoreOrLocationData : PredicateSpecification { public IgnoreOrLocationData() : base(x => x == DataSourceTypeSensors.Ignore || x == DataSourceTypeSensors.LocationData) { Description = "Only Ignore or LocationData value allowed for this property."; } } /// /// Specication to test if the value of the candidate is valid /// internal class SensorOrLocationData : PredicateSpecification { public SensorOrLocationData() : base(x => x == DataSourceTypeSensors.Sensor || x == DataSourceTypeSensors.LocationData) { Description = "Only Sensor or LocationData value allowed for this property."; } } #endregion /// /// Constructor is needed for XML deserialization /// [Browsable(false)] public SensorLocation() { PL1WaterLevelAtRiver = DataSourceTypeSensors.LocationData; PL1WaterLevelAtPolder = DataSourceTypeSensors.LocationData; } private Group sensorGroup; /// /// Gets the PL line offset below dike top at river. /// [Browsable(false)] public double PLLineOffsetBelowDikeTopAtRiver { get { return Location.PlLineOffsetBelowDikeTopAtRiver; } } /// /// Gets the PL line offset below dike top at polder. /// [Browsable(false)] public double PLLineOffsetBelowDikeTopAtPolder { get { return Location.PLLineOffsetDryBelowDikeTopAtPolder; } } /// /// Gets the PL line offset below dike toe at polder. /// [Browsable(false)] public double PLLineOffsetBelowDikeToeAtPolder { get { return Location.PlLineOffsetBelowDikeToeAtPolder; } } /// /// Gets the PL line offset dry below shoulder base inside. /// [Browsable(false)] public double PLLineOffsetDryBelowShoulderBaseInside { get { return Location.PlLineOffsetBelowShoulderBaseInside; } } /// /// Gets or sets the DAM location. /// /// /// The location. /// [XmlIgnore] [Specification(typeof(NotNullSpecification))] [Browsable(false)] public Location Location { get; set; } /// /// Gets the location Name. /// /// /// If the Name is empty it could indicate that there is no reference /// to a valid location /// [XmlIgnore] [PropertyOrder(1, 1)] public string LocationName { get { return (Location != null) ? Location.Name : string.Empty; } } /// /// Gets or sets the sensor group. /// /// /// The group. /// [Specification(typeof(NotNullSpecification))] [PropertyOrder(1, 5)] public Group Group { get { return sensorGroup; } set { sensorGroup = value; } } public void ResetGroupID(int id) { sensorGroup.ID = id; } /// /// Gets the group ID. /// /// /// If the ID has value -1 then the there is no valid reference (null) /// or the group is transient. /// [XmlIgnore] [Browsable(false)] public int GroupID { get { if (sensorGroup == null) return -1; return sensorGroup.ID; } } /// /// Gets the sensor selection. /// [XmlIgnore] [Browsable(false)] public IEnumerable Sensors { get { if (sensorGroup == null) return new Sensor[0]; return sensorGroup.Selection; } } /// /// Gets or sets the PL3 data source type. /// /// /// The PL3. /// [Specification(typeof(SensorOrLocationData))] [PropertyOrder(1, 12)] public DataSourceTypeSensors PL3 { get; set; } /// /// Gets or sets the PL4 data source type. /// /// /// The PL4. /// [Specification(typeof(SensorOrLocationData))] [PropertyOrder(1, 13)] public DataSourceTypeSensors PL4 { get; set; } /// /// Gets or sets the outer water level data source type. /// /// /// The outer water level. /// [Specification(typeof(SensorOrLocationData))] [PropertyOrder(1, 6)] public DataSourceTypeSensors PL1WaterLevelAtRiver { get; set; } /// /// Gets or sets the PL1 PL line offset below dike top at river data source type. /// /// /// Data source type for PL1 PLLine offset below dike top at river. /// [Specification(typeof(IgnoreOrLocationData))] [PropertyOrder(1, 7)] public DataSourceTypeSensors PL1PLLineOffsetBelowDikeTopAtRiver { get; set; } /// /// Gets or sets the PL1 PL line offset below dike top at polder data source type /// /// /// The PL1 PL line offset below dike top at polder. /// [Specification(typeof(IgnoreOrLocationData))] [PropertyOrder(1, 8)] public DataSourceTypeSensors PL1PLLineOffsetBelowDikeTopAtPolder { get; set; } /// /// Gets or sets the PL1 PL line offset below shoulder base inside data source type /// /// /// The PL1 PL line offset below shoulder base inside. /// [Specification(typeof(IgnoreOrLocationData))] [PropertyOrder(1, 9)] public DataSourceTypeSensors PL1PLLineOffsetBelowShoulderBaseInside { get; set; } /// /// Gets or sets the PL1 PL line offset below dike toe at polder data source type. /// /// /// The PL1 PL line offset below dike toe at polder. /// [Specification(typeof(IgnoreOrLocationData))] [PropertyOrder(1, 10)] public DataSourceTypeSensors PL1PLLineOffsetBelowDikeToeAtPolder { get; set; } /// /// Gets or sets the PL1 polder level data source type. /// /// /// The PL1 polder level. /// [Specification(typeof(SensorOrLocationData))] [PropertyOrder(1, 11)] public DataSourceTypeSensors PL1WaterLevelAtPolder { get; set; } /// /// Gets the sensor count. /// [Browsable(false)] public int SensorCount { get { return sensorGroup == null ? 0 : Group.SensorCount; } } [Browsable(false)] public SurfaceLine2 SurfaceLine { get { return Location.SurfaceLine2; } } [Browsable(false)] public double RiverLevel { get { return Location.RiverLevel; } } [Browsable(false)] public double PolderLevel { get { return Location.PolderLevel; } } [Browsable(false)] public double? HeadPl3 { get { return Location.HeadPl3; } } [Browsable(false)] public double? HeadPl4 { get { return Location.HeadPl4; } } [PropertyOrder(1, 3)] public string Alias { get; set; } // ToDo Tom/Kin Sun Waarvoor nodig? Wordt nergens gebruikt. Kan gewoon weg!? [Browsable(false)] public string Profile { get; //{ return SoilProfile2D } ; set; } [Browsable(false)] public string MStabFile { get; //{ return SoilProfile2D } ; set; } /// /// Gets the PiezometricHead type sensors sorted by relative location along profile. /// /// Type of the pl line. /// internal SortedDictionary GetSensorsSortedByRelativeLocationAlongProfile(PLLineType plLineType) { var calculatedRelativeLocations = BuildRelativeLocationTable(); var table = new SortedDictionary(); // leave out the water level and polder level sensors var candidateSensors = Sensors.GetBySpecification(new PiezometricHeadSensorSpecification()); foreach (var sensor in candidateSensors) { if (sensor.PLLineMappings.Contains(plLineType)) { double relativeLocation = sensor.RelativeLocationSpecified ? sensor.RelativeLocation : calculatedRelativeLocations[sensor]; if (table.ContainsKey(relativeLocation)) { throw new InvalidOperationException( "Error creating lookup table with sensors sorted by relative location along profile. The x-location " + relativeLocation + " already exists. Sensor " + sensor); } table.Add(relativeLocation, sensor); } } return table; } /// /// Builds the relative location table. /// /// internal IDictionary BuildRelativeLocationTable() { var surfaceLevelOutside = SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelOutside); if (surfaceLevelOutside == null) throw new InvalidOperationException("No SurfaceLevelOutside point on surface line defined"); double startPoint = surfaceLevelOutside.X; var dict = new Dictionary(); foreach (Sensor sensor in Sensors) { double relativeLocation = startPoint + (Location.XRdDikeLine - sensor.XRd); dict.Add(sensor, relativeLocation); } return dict; } internal static class MemberNames { internal static readonly string OffsetBelowDikeToeAtPolder = StaticReflection.GetMemberName(x => x.PL1PLLineOffsetBelowDikeToeAtPolder); internal static readonly string OffsetBelowDikeTopAtPolder = StaticReflection.GetMemberName(x => x.PL1PLLineOffsetBelowDikeTopAtPolder); internal static readonly string OffsetBelowDikeTopAtRiver = StaticReflection.GetMemberName(x => x.PL1PLLineOffsetBelowDikeTopAtRiver); internal static readonly string OffsetBelowShoulderBaseInside = StaticReflection.GetMemberName(x => x.PL1PLLineOffsetBelowShoulderBaseInside); internal static readonly string WaterLevelAtRiver = StaticReflection.GetMemberName(x => x.PL1WaterLevelAtRiver); internal static readonly string PolderLevel = StaticReflection.GetMemberName(x => x.PL1WaterLevelAtPolder); internal static readonly string PL3 = StaticReflection.GetMemberName(x => x.PL3); internal static readonly string PL4 = StaticReflection.GetMemberName(x => x.PL4); } public double? GetValue(Expression> expression, IDictionary sensorValues, Sensor sensor) { if (sensorValues == null) throw new ArgumentNullException("sensorValues"); if (sensor == null) throw new ArgumentNullException("sensor"); if (!sensorValues.ContainsKey(sensor)) throw new ArgumentException("The given sensor is not an item/key in the table of sensor values"); var memberName = StaticReflection.GetMemberName(expression); if (memberName == MemberNames.OffsetBelowDikeToeAtPolder) { if (PL1PLLineOffsetBelowDikeToeAtPolder == DataSourceTypeSensors.LocationData) return Location.PlLineOffsetBelowDikeToeAtPolder; } if (memberName == MemberNames.OffsetBelowDikeTopAtPolder) { if (PL1PLLineOffsetBelowDikeTopAtPolder == DataSourceTypeSensors.LocationData) return Location.PlLineOffsetBelowDikeTopAtPolder; } if (memberName == MemberNames.OffsetBelowDikeTopAtRiver) { if (PL1PLLineOffsetBelowDikeTopAtRiver == DataSourceTypeSensors.LocationData) return Location.PlLineOffsetBelowDikeTopAtRiver; } if (memberName == MemberNames.OffsetBelowShoulderBaseInside) { if (PL1PLLineOffsetBelowShoulderBaseInside == DataSourceTypeSensors.LocationData) return Location.PlLineOffsetBelowShoulderBaseInside; } if (memberName == MemberNames.WaterLevelAtRiver) { if (PL1WaterLevelAtRiver == DataSourceTypeSensors.LocationData) return Location.RiverLevel; if (PL1WaterLevelAtRiver == DataSourceTypeSensors.Sensor) return sensorValues[sensor]; } if (memberName == MemberNames.PolderLevel) { if (PL1WaterLevelAtPolder == DataSourceTypeSensors.LocationData) return Location.PolderLevel; if (PL1WaterLevelAtPolder == DataSourceTypeSensors.Sensor) return sensorValues[sensor]; } if (memberName == MemberNames.PL3) { if (PL3 == DataSourceTypeSensors.LocationData) return Location.HeadPl3; if (PL3 == DataSourceTypeSensors.Sensor) return sensorValues[sensor]; } if (memberName == MemberNames.PL4) { if (PL4 == DataSourceTypeSensors.LocationData) return Location.HeadPl4; if (PL4 == DataSourceTypeSensors.Sensor) return sensorValues[sensor]; } return null; } /// /// Determines whether this instance is valid. /// /// /// true if this instance is valid; otherwise, false. /// public bool IsValid() { return !Validator.Validate(this).Any(); } /// /// Serializes this instance. /// /// public string Serialize() { var xmlSerializer = new XmlSerializer(); return xmlSerializer.SerializeToString(this); } /// /// Deserializes the specified XML. /// /// The XML. /// public static SensorLocation Deserialize(string xml) { var xmlDeserializer = new XmlDeserializer(); return (SensorLocation)xmlDeserializer.XmlDeserializeFromString(xml, typeof(SensorLocation)); } /// /// Gets or sets the GetGroups function (injectable list, for UI purposes). /// /// /// The list of available Groups. /// [XmlIgnore] [Browsable(false)] public static Func> GetGroups { get; set; } private ICollection GetGroupsDomain() { if (GetGroups == null) { return null; } return GetGroups(this).ToList(); } /// /// Gets the domain for properties in the UI. /// /// The property. /// public ICollection GetDomain(string property) { switch (property) { case "Group": return GetGroupsDomain(); default: return null; } } } }