// Copyright (C) Stichting Deltares 2024. 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.Xml.Serialization;
using Deltares.Dam.Data.IO;
using Deltares.Dam.Data.Sensors;
using Deltares.Standard;
using Deltares.Standard.Attributes;
using Deltares.Standard.EventPublisher;
using Deltares.Standard.Language;
using Deltares.Standard.Logging;
using Deltares.Standard.Project;
using Deltares.Standard.Validation;
namespace Deltares.Dam.Data;
public class DamProjectData : Project, IDomain, IDisposable
{
public readonly double MissValStabilitySafetyFactor = -1.0;
private readonly List jobs = new List();
private WaterBoard waterBoard;
private WaterBoardJob waterBoardJob;
private DamProjectType damProjectType = DamProjectType.Calamity;
private string damDataSourceFileName = "";
private List designCalculations;
///
/// Constructor
///
public DamProjectData()
{
VersionInfo = new VersionInfo();
waterBoard = new WaterBoard();
waterBoardJob = null;
DamProjectCalculationSpecification = new DamProjectCalculationSpecification();
}
/// Gets the version information.
/// The version information.
public VersionInfo VersionInfo { get; set; }
///
/// Gets or sets the project working path.
///
///
/// The project working path.
///
public string ProjectPath
{
get
{
return DamProject.ProjectMap;
}
}
///
/// Gets or sets the calculation map.
///
///
/// The calculation map.
///
public string CalculationMap
{
get
{
return DamProject.CalculationMap;
}
}
///
/// Gets or sets the maximum calculation cores.
///
///
/// The maximum calculation cores.
///
public int MaxCalculationCores { get; set; } = 1;
///
/// Toplevel object to hold waterboard data
///
public virtual WaterBoard WaterBoard
{
get
{
return waterBoard;
}
set
{
waterBoard = value;
if (waterBoardJob != null && waterBoardJob.Subject != value)
{
waterBoardJob = null;
}
}
}
///
/// calculation specification for the project
///
/// Composite relationship
[Validate]
public DamProjectCalculationSpecification DamProjectCalculationSpecification { get; set; }
[Child]
public virtual DamJob WaterBoardJob
{
get
{
if (waterBoardJob == null)
{
waterBoardJob = new WaterBoardJob(waterBoard);
waterBoardJob.Subject = waterBoard;
foreach (Dike dike in waterBoard.Dikes)
{
CompositeJob dikeJob = new DikeJob(dike);
waterBoardJob.Jobs.Add(dikeJob);
foreach (Location location in dike.Locations)
{
var locationJob = new LocationJob(location);
dikeJob.Jobs.Add(locationJob);
}
}
}
return waterBoardJob;
}
set
{
waterBoardJob = value as WaterBoardJob;
}
}
[ReadOnly(true)]
[Label("Calculation type")]
[Description("Indicates the purpose of the calculations")]
public virtual DamProjectType DamProjectType
{
get => damProjectType;
set
{
DataEventPublisher.BeforeChange(this, "DamProjectType");
bool modified = damProjectType != value;
damProjectType = value;
Location.DamProjectType = value;
DamFailureMechanismeCalculationSpecification.DamProjectType = value;
if (modified)
{
LocationJob.DamProjectType = damProjectType;
DataEventPublisher.DataListModified(LocationJobs);
}
DataEventPublisher.AfterChange(this, "DamProjectType");
}
}
[Label("DAM datasource file")]
[Description("Indicates which \".defx\" import definition file to use")]
public virtual string DamDataSourceFileName
{
get
{
return damDataSourceFileName;
}
set
{
DataEventPublisher.BeforeChange(this, "DamDataSourceFileName");
damDataSourceFileName = value;
DataEventPublisher.AfterChange(this, "DamDataSourceFileName");
}
}
[XmlIgnore] public List CalculationMessages { get; set; }
[XmlIgnore]
[ReadOnly(true)]
public List DesignCalculations
{
get
{
if (designCalculations == null)
{
UpdateDesignCalculations();
}
return designCalculations;
}
set
{
designCalculations = value;
}
}
[XmlIgnore]
[ReadOnly(true)]
[Browsable(false)]
public List LocationJobs
{
get
{
if (jobs.Count == 0)
{
var waterboardJob = WaterBoardJob as CompositeJob;
foreach (CompositeJob dijkJob in waterboardJob.Jobs)
{
foreach (LocationJob locationJob in dijkJob.Jobs)
{
jobs.Add(locationJob);
}
}
}
return jobs;
}
}
[XmlIgnore]
[ReadOnly(true)]
[Browsable(false)]
[Validate]
public List SelectedLocationJobs
{
get
{
var selectedLocationJobs = new List();
foreach (LocationJob locationJob in LocationJobs)
{
if (locationJob.Run.HasValue && locationJob.Run.Value)
{
selectedLocationJobs.Add(locationJob);
}
}
return selectedLocationJobs;
}
}
[XmlIgnore]
[Browsable(false)]
[ReadOnly(true)]
[Label("Locations")]
public List Locations
{
get
{
return WaterBoard.Locations;
}
}
[Browsable(false)] public List DataSources { get; set; } = new List();
[Browsable(false)] public string DataSourceEsriProjection { get; set; } = null;
///
/// Gets or sets the sensor data.
///
///
/// The sensor data.
///
public SensorData SensorData { get; set; }
///
/// Gets or sets the input time serie collection.
///
///
/// The input time serie collection.
///
public TimeSerieCollection InputTimeSerieCollection { get; set; } = null;
///
/// Gets or sets the output time serie collection.
///
///
/// The output time serie collection.
///
public TimeSerieCollection OutputTimeSerieCollection { get; set; } = null;
///
/// Clear all results
///
public void ClearResults()
{
if (designCalculations != null)
{
designCalculations.Clear();
}
foreach (LocationJob locationJob in LocationJobs)
{
locationJob.LocationResult = new LocationResult();
foreach (Scenario scenario in locationJob.Location.Scenarios)
{
scenario.CalculationResults.Clear();
}
}
}
///
/// Updates the design calculations.
///
public void UpdateDesignCalculations()
{
if (designCalculations == null)
{
designCalculations = new List();
}
else
{
designCalculations.Clear();
}
List scenarios = CreateScenarioListForCalculation();
foreach (Scenario scenario in scenarios)
{
if (scenario.CalculationResults != null && scenario.CalculationResults.Count > 0)
{
designCalculations.AddRange(scenario.CalculationResults);
}
}
DataEventPublisher.DataListModified(designCalculations);
}
public CalculationResult Validate()
{
try
{
//Todo validation has to extended, handling the actual messages instead of just CalculationResult too.
foreach (Dike dike in WaterBoard.Dikes)
{
dike.Validate();
}
return CalculationResult.Succeeded;
}
catch
{
return CalculationResult.InvalidInputData;
}
}
public LocationJob GetLocationJob(Location location)
{
foreach (LocationJob locationJob in LocationJobs)
{
if (locationJob.Location == location)
{
return locationJob;
}
}
return null;
}
public LocationJob GetFirstLocationJobWithDesignResults()
{
foreach (LocationJob locationJob in LocationJobs)
{
if (locationJob.HasDesignScenarioResults)
{
return locationJob;
}
}
return null;
}
public void TransferOutputTimeSerieToLocationJobs()
{
if (OutputTimeSerieCollection == null)
{
return;
}
foreach (LocationJob locationJob in LocationJobs)
{
TimeSerie timeSerie = OutputTimeSerieCollection.Series.FirstOrDefault(serie => serie.LocationId == locationJob.Location.Name);
if (timeSerie != null)
{
if (DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType == FailureMechanismSystemType.Piping)
{
locationJob.LocationResult.PipingTimeSerie = timeSerie;
}
else
{
locationJob.LocationResult.StabilityTimeSerie = timeSerie;
}
}
}
}
public LocationJob GetFirstLocationJobWithCalamityResults()
{
foreach (LocationJob locationJob in LocationJobs)
{
if (locationJob.HasLocationResults)
{
return locationJob;
}
}
return null;
}
[Validate]
public ValidationResult[] ValidateEnoughLocationJobs()
{
if (SelectedLocationJobs.Count == 0)
{
return new[]
{
new ValidationResult(ValidationResultType.Error,
LocalizationManager.GetTranslatedText(this, "NoLocationsSelected"),
this)
};
}
return new ValidationResult[0];
}
public void DeleteResults()
{
LogManager.Messages.Clear();
if (waterBoardJob != null && waterBoardJob.Jobs != null)
{
waterBoardJob.Jobs.Clear();
}
waterBoardJob = null;
if (jobs != null)
{
jobs.Clear();
}
// Delete calculationresults in scenarios
List scenarios = CreateScenarioListForDeletion();
foreach (Scenario scenario in scenarios)
{
scenario.ClearResults();
scenario.ClearErrors();
}
if (designCalculations != null)
{
designCalculations.Clear();
}
}
public bool HasResults()
{
bool hasResults = waterBoard.Dikes.Count > 0 && waterBoardJob != null && waterBoardJob.Jobs.Count > 0;
if (hasResults)
{
hasResults = GetNumberOfCalculatedJobs() > 0;
}
return hasResults;
}
///
/// Validates the geometry adaption setting.
///
///
[Validate]
public ValidationResult[] ValidateGeometryAdaptionSetting()
{
if (!IsDesignWithGeometryAdaptionAllowed())
{
return new[]
{
new ValidationResult(ValidationResultType.Error, LocalizationManager.GetTranslatedText(this, "DesignAndAdaptionNotAllowedForStabilityOutside"),
this, "DamCalculationSpecifications", "DamCalculationSpecifications", null)
};
}
return new ValidationResult[0];
}
public void FillOverallSensorData()
{
if (SensorData != null)
{
SensorData.Sensors.Clear();
SensorData.SensorGroups.Clear();
SensorData.SensorLocations.Clear();
}
else
{
SensorData = new SensorData();
}
foreach (Location location in Locations)
{
SensorLocation sd = location.SensorLocation;
foreach (Sensor sensor in sd.Sensors)
{
if (SensorData.GetSensorById(sensor.ID) == null)
{
SensorData.Sensors.Add(sensor);
}
}
if (SensorData.GetGroupById(sd.Group.ID) == null && sd.Group.ID != 0)
{
sd.Group.PickSensors = SensorData.Sensors;
SensorData.SensorGroups.Add(sd.Group);
}
if (SensorData.GetSensorLocationByLocationName(sd.LocationName) == null)
{
SensorData.SensorLocations.Add(sd);
}
}
}
public override bool IsVisible(string property)
{
switch (property)
{
case "SensorData": return damProjectType == DamProjectType.DamLiveConfiguration;
case "SchematizationFactors": return false;
//Bka: for now (release 1.4.1), do not show SchematizationFactors return this.HasResults() && this.DamProjectType == Data.DamProjectType.Assessment;
case "DesignCalculations": return HasResults() && DamProjectType == DamProjectType.Design;
case "DamProjectCalculationSpecification":
return DamProjectCalculationSpecification.DamCalculationSpecifications.Count > 0;
default: return true;
}
}
public override string ToString()
{
return WaterBoard != null ? WaterBoard.Name : "";
}
public void Dispose()
{
WaterBoard.Dispose();
DamProjectCalculationSpecification.Dispose();
}
public ICollection GetDomain(string property)
{
switch (property)
{
default: return null;
}
}
private List CreateScenarioListForDeletion()
{
var scenarios = new List();
List locations = LocationJobs.Select(x => x.Location).ToList();
foreach (Location location in locations)
{
if (location.Scenarios != null)
{
scenarios.AddRange(location.Scenarios);
}
}
return scenarios;
}
private List CreateScenarioListForCalculation()
{
var scenarios = new List();
List locations = SelectedLocationJobs.Select(x => x.Location).ToList();
foreach (Location location in locations)
{
if (location.Scenarios != null)
{
scenarios.AddRange(location.Scenarios);
}
}
return scenarios;
}
private int GetNumberOfCalculatedJobs()
{
var numberOfCalculatedJobs = 0;
foreach (LocationJob locationJob in LocationJobs)
{
if ((locationJob.Result != JobResult.NoRun) && locationJob.Run != null && locationJob.Run.Value)
{
foreach (Dike dike in WaterBoard.Dikes)
{
if (dike.Locations.Contains(locationJob.Location))
{
dike.UpdateLocation(locationJob.Location);
break;
}
}
numberOfCalculatedJobs++;
}
}
return numberOfCalculatedJobs;
}
///
/// Check if design with geometry adaption is allowed
///
///
private bool IsDesignWithGeometryAdaptionAllowed()
{
bool isAdoption = DamProjectType == DamProjectType.Design && DamProjectCalculationSpecification.SelectedAnalysisType != AnalysisType.NoAdaption;
bool isStabilityOutside = DamProjectCalculationSpecification.DamCalculationSpecifications.Any(specification => specification.FailureMechanismSystemType == FailureMechanismSystemType.StabilityOutside);
return (!isAdoption || !isStabilityOutside);
}
}