// 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;
}
}
}
}