// Copyright (C) Stichting Deltares 2019. All rights reserved.
//
// This file is part of the application DAM - Live.
//
// 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.IO;
using System.Xml.Linq;
using Deltares.Dam.Application.Live.Properties;
using Deltares.Dam.Data;
using Deltares.Dam.Data.Assemblers;
using Deltares.Dam.Data.DamEngineIo;
using Deltares.DamEngine.Interface;
using Deltares.DamEngine.Io;
using Deltares.DamEngine.Io.XmlInput;
using Deltares.DamEngine.Io.XmlOutput;
using Deltares.Standard.Application;
using Deltares.Standard.Logging;
namespace Deltares.Dam.Application.Live
{
public class DamEngineRunner
{
internal const string NoDamxFile = "No .damx file set to load project data from.";
internal const string NoFewsInputFileAvailable = "No FEWS input file available to read the input time series from.";
internal const string NoFewsOutputFileAvailable = "No FEWS output file available to write the result to.";
internal const string ErrorExtractingWorkingFolder = "An error occured while trying to extract the path from the FEWS input file. This error can be solved by setting a working folder";
internal LogHelper Logger = LogHelper.Create();
///
/// Gets or sets the dam project data.
///
///
/// The dam project data.
///
public DamProjectData DamProjectData { get; set; }
///
/// Gets or sets the calculation parameters.
///
///
/// The calculation parameters.
///
public CalculationParameters CalculationParameters { get; set; }
///
/// Gets or sets the output time series collection.
///
///
/// The output time series collection.
///
public TimeSerieCollection OutputTimeSeriesCollection { get; set; }
///
/// Gets or sets the input time series collection.
///
///
/// The input time series collection.
///
public TimeSerieCollection InputTimeSeriesCollection { get; set; }
///
/// Gets or sets the stability working path.
///
///
/// The stability working path.
///
public string StabilityWorkingPath { get; set; }
///
/// Gets or sets the piping working path.
///
///
/// The piping working path.
///
public string PipingWorkingPath { get; set; }
///
/// Gets or sets the stability executable path.
///
///
/// The stability executable path.
///
public string StabilityExePath { get; set; }
///
/// Gets or sets a value indicating whether [use m stab for calculation].
///
///
/// true if [use m stab for calculation]; otherwise, false.
///
public bool UseMStabForCalculation { get; set; }
///
/// Gets or sets the water level offset.
///
///
/// The water level offset.
///
public double WaterLevelOffset { get; set; }
///
/// Gets or sets the working path.
///
///
/// The working path.
///
public string WorkingPath { get; set; }
///
/// Gets or sets the maximum calculation cores.
///
///
/// The maximum calculation cores.
///
public int MaxCalculationCores { get; set; } = 1;
protected internal FileInfo DamXFile { get; set; }
protected internal FileInfo FewsInputFile { get; set; }
protected internal FileInfo FewsOutputFile { get; set; }
protected internal FileInfo ParametersFile { get; set; }
///
/// Gets or sets the filter.
///
///
/// The filter.
///
public string Filter { get; set; }
///
/// Gets a value indicating whether this instance has errors.
///
///
/// true if this instance has errors; otherwise, false.
///
public bool HasErrors
{
get { return Logger.HasLoggedExceptions; }
}
///
/// Initializes a new instance of the class.
///
public DamEngineRunner()
{
// WorkingPath will be extracted from file name if available
StabilityWorkingPath = Settings.Default.StabilityWorkingPath;
PipingWorkingPath = Settings.Default.PipingWorkingPath;
StabilityExePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), Settings.Default.MStabExePath);
UseMStabForCalculation = Settings.Default.UseMStabForCalculation;
WaterLevelOffset = Settings.Default.WaterLevelOffset;
}
///
/// Initializes this instance.
///
///
///
public void Initialize()
{
LoadDataFromFiles();
AdaptDamProjectData();
}
private void AdaptDamProjectData()
{
// Prepare DamProjetData for serializing to Dam Engine
DamProjectData.SelectedLocationJobs.Clear();
foreach (Data.Location location in DamProjectData.Locations)
{
var locationJob = new LocationJob()
{
Location = location,
Run = true
};
DamProjectData.LocationJobs.Add(locationJob);
}
DamProjectData.FillOverallSensorData();
// Add time series to DamProjectData
DamProjectData.InputTimeSerieCollection = InputTimeSeriesCollection;
// Set correct calculation settings
DamProjectData.DamProjectType = DamProjectType.Calamity;
}
private void LoadDataFromFiles()
{
if (DamProjectData == null)
{
if (DamXFile == null)
throw new InvalidOperationException(NoDamxFile);
DamProjectData = DamProject.LoadData(DamXFile.FullName);
WorkingPath = Path.ChangeExtension(DamXFile.FullName, ".Calc");
}
if (InputTimeSeriesCollection == null)
{
if (FewsInputFile == null)
throw new InvalidOperationException(NoFewsInputFileAvailable);
InputTimeSeriesCollection = TimeSerieCollection.LoadFromFile(FewsInputFile);
}
if (OutputTimeSeriesCollection == null)
{
if (FewsOutputFile == null)
throw new InvalidOperationException(NoFewsOutputFileAvailable);
OutputTimeSeriesCollection = InputTimeSeriesCollection.GetShallowCopy();
}
if (CalculationParameters == null && ParametersFile != null)
{
// Read calculation parameters, if available
CalculationParameters = CalculationParameters.LoadFromFile(ParametersFile);
}
}
// Wat te doen bij fouten in DamLive: debug input xml file (InputFile.xml in debug dir DamLive) oppakken en gebruiken in DamEngine via de
// aanstuurtruc van Tom (zie DebuggingTest in IntegrationTests van de Engine) en dan kijken wat daar gebeurt.
///
/// Runs this instance.
///
public void Run()
{
Initialize();
OutputTimeSeriesCollection.Series.Clear();
RunSelectedModels();
WriteResultsToFile(FewsOutputFile.FullName);
}
private void RunSelectedModels()
{
if (CalculationParameters.CalculationModules.StabilityInside)
{
if (!RunStability(FailureMechanismSystemType.StabilityInside))
{
Logger.LogError("During the Stability Inside calculation, (some) errors occured, see the output files.");
}
}
if (CalculationParameters.CalculationModules.StabilityOutside)
{
if (!RunStability(FailureMechanismSystemType.StabilityOutside))
{
Logger.LogError("During the Stability Outside calculation, (some) errors occured, see the output files.");
}
}
if (CalculationParameters.CalculationModules.PipingBligh)
{
DamProjectData.DamProjectCalculationSpecification.CurrentSpecification.PipingModelType = PipingModelType.Bligh;
if (!RunPiping(FailureMechanismSystemType.Piping))
{
Logger.LogError("During the Piping Bligh calculation, (some) errors occured, see the output files.");
}
}
if (CalculationParameters.CalculationModules.PipingSellmeijer)
{
DamProjectData.DamProjectCalculationSpecification.CurrentSpecification.PipingModelType = PipingModelType.Sellmeijer;
if (!RunPiping(FailureMechanismSystemType.Piping))
{
Logger.LogError("During the Piping Sellmeijer calculation, (some) errors occured, see the output files.");
}
}
if (CalculationParameters.CalculationModules.PipingWti)
{
DamProjectData.DamProjectCalculationSpecification.CurrentSpecification.PipingModelType = PipingModelType.Wti2017;
if (!RunPiping(FailureMechanismSystemType.Piping))
{
Logger.LogError("During the Piping Wti calculation, (some) errors occured, see the output files.");
}
}
}
private bool RunPiping(FailureMechanismSystemType failureMechanismSystemType)
{
DamProjectData.DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType = failureMechanismSystemType;
var result = CallDamEngine();
if (DamProjectData.OutputTimeSerieCollection != null)
{
OutputTimeSeriesCollection.Series.AddRange(DamProjectData.OutputTimeSerieCollection.Series);
}
return result;
}
private bool RunStability(FailureMechanismSystemType failureMechanismSystemType)
{
ReadUserSettingsSlipCircleDefinition(CalculationParameters.MStabParameters.SlipCircleDefinition);
DamProjectData.DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType = failureMechanismSystemType;
DamProjectData.DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismeParamatersMStab.MStabParameters =
CalculationParameters.MStabParameters;
foreach (var location in DamProjectData.Locations)
{
location.StabilityZoneType = CalculationParameters.MStabParameters.ZonesType;
}
var result = CallDamEngine();
if (DamProjectData.OutputTimeSerieCollection != null)
{
OutputTimeSeriesCollection.Series.AddRange(DamProjectData.OutputTimeSerieCollection.Series);
}
return result;
}
private bool CallDamEngine()
{
var errorsFound = false;
DamProjectData.MaxCalculationCores = MaxCalculationCores;
try
{
Input input = FillXmlInputFromDamUi.CreateInput(DamProjectData);
#if DEBUG
const string inputFilename = "InputForDebugging.xml";
DamXmlSerialization.SaveInputAsXmlFile(inputFilename, input);
#endif
string inputXml = DamXmlSerialization.SaveInputAsXmlString(input);
var damEngineInterface = new EngineInterface(inputXml);
string validationMessages = damEngineInterface.Validate();
// now the validation messages should be deserialized. If any, they should be passed on to the Validator
// and checked for errors. If errors are found, then no calculation. When no messages or only warnings then
// do calculate. For now, just check length
if (string.IsNullOrEmpty(validationMessages))
{
string outputXml = damEngineInterface.Run();
var output = DamXmlSerialization.LoadOutputFromXmlString(outputXml);
FillDamUiFromXmlOutput.AddOutputToDamProjectData(DamProjectData, output);
//#if DEBUG
string outputFilename = Path.Combine(DamProjectData.ProjectPath, "EngineOutputFile.xml");
DamXmlSerialization.SaveOutputAsXmlFile(outputFilename, output);
//#endif
foreach (var calculationMessage in output.Results.CalculationMessages)
{
switch (calculationMessage.MessageType)
{
case MessageMessageType.Info:
Logger.LogInfo(calculationMessage.Message1);
break;
case MessageMessageType.Warning:
Logger.LogWarning(calculationMessage.Message1);
break;
case MessageMessageType.Error:
Logger.LogError(calculationMessage.Message1);
errorsFound = true;
break;
}
}
}
else
{
// Todo: handle validation messages
Logger.LogError("Validation of input failed.");
Logger.LogError(validationMessages);
errorsFound = true;
}
}
catch (Exception e)
{
LogManager.Add(new LogMessage(LogMessageType.FatalError, typeof(EngineInterface), string.Format("{0}", e.Message)));
}
return !errorsFound;
}
public void WriteResultsToFile(string fileName)
{
if (string.IsNullOrWhiteSpace(fileName))
throw new ArgumentNullException("fileName");
var timeSerieAssembler = new TimeSeriesAssembler();
XDocument doc = timeSerieAssembler.CreateDataTransferDocument(OutputTimeSeriesCollection);
try
{
doc.Save(fileName);
FileWriterUtil.RemoveThreeBytesFromXml(fileName);
}
catch (Exception e)
{
Logger.LogError("Could not export Fews xml file", e);
}
}
///
/// Reads the user settings of the slip circle definition.
///
/// The slip circle definition.
public void ReadUserSettingsSlipCircleDefinition(SlipCircleDefinition slipCircleDefinition)
{
if (slipCircleDefinition == null)
{
slipCircleDefinition = new SlipCircleDefinition();
}
slipCircleDefinition.GridSizeDetermination = Properties.Settings.Default.SlipCircleGridSizeDetermination;
slipCircleDefinition.UpliftVanTangentLinesDefinition = Properties.Settings.Default.SlipCircleUpliftVanTangentLinesDefinition;
slipCircleDefinition.UpliftVanTangentLinesDistance = Properties.Settings.Default.SlipCircleUpliftVanTangentLinesDistance;
slipCircleDefinition.UpliftVanLeftGridVerticalPointCount = Properties.Settings.Default.SlipCircleUpliftVanLeftGridVerticalPointCount;
slipCircleDefinition.UpliftVanLeftGridVerticalPointDistance = Properties.Settings.Default.SlipCircleUpliftVanLeftGridVerticalPointDistance;
slipCircleDefinition.UpliftVanLeftGridHorizontalPointCount = Properties.Settings.Default.SlipCircleUpliftVanLeftGridHorizontalPointCount;
slipCircleDefinition.UpliftVanLeftGridHorizontalPointDistance = Properties.Settings.Default.SlipCircleUpliftVanLeftGridHorizontalPointDistance;
slipCircleDefinition.UpliftVanRightGridVerticalPointCount = Properties.Settings.Default.SlipCircleUpliftVanRightGridVerticalPointCount;
slipCircleDefinition.UpliftVanRightGridVerticalPointDistance = Properties.Settings.Default.SlipCircleUpliftVanRightGridVerticalPointDistance;
slipCircleDefinition.UpliftVanRightGridHorizontalPointCount = Properties.Settings.Default.SlipCircleUpliftVanRightGridHorizontalPointCount;
slipCircleDefinition.UpliftVanRightGridHorizontalPointDistance = Properties.Settings.Default.SlipCircleUpliftVanRightGridHorizontalPointDistance;
slipCircleDefinition.BishopTangentLinesDefinition = Properties.Settings.Default.SlipCircleBishopTangentLinesDefinition;
slipCircleDefinition.BishopTangentLinesDistance = Properties.Settings.Default.SlipCircleBishopTangentLinesDistance;
slipCircleDefinition.BishopGridVerticalPointCount = Properties.Settings.Default.SlipCircleBishopGridVerticalPointCount;
slipCircleDefinition.BishopGridVerticalPointDistance = Properties.Settings.Default.SlipCircleBishopGridVerticalPointDistance;
slipCircleDefinition.BishopGridHorizontalPointCount = Properties.Settings.Default.SlipCircleBishopGridHorizontalPointCount;
slipCircleDefinition.BishopGridHorizontalPointDistance = Properties.Settings.Default.SlipCircleBishopGridHorizontalPointDistance;
}
}
}