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