// Copyright (C) Stichting Deltares 2017. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets 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.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Security;
using Ringtoets.HydraRing.Calculation.Data;
using Ringtoets.HydraRing.Calculation.Data.Input;
using Ringtoets.HydraRing.Calculation.Data.Output.IllustrationPoints;
using Ringtoets.HydraRing.Calculation.Exceptions;
using Ringtoets.HydraRing.Calculation.Parsers;
using Ringtoets.HydraRing.Calculation.Parsers.IllustrationPoints;
using Ringtoets.HydraRing.Calculation.Properties;
using Ringtoets.HydraRing.Calculation.Services;
namespace Ringtoets.HydraRing.Calculation.Calculator
{
///
/// Base implementation for a calculator which uses Hydra-Ring to perform different calculations.
///
internal abstract class HydraRingCalculatorBase
{
private readonly LastErrorFileParser lastErrorFileParser;
private readonly IllustrationPointsParser illustrationPointsParser;
private readonly string hlcdDirectory;
private Process hydraRingProcess;
///
/// Creates a new instance of with a default Hydra-Ring file parser
/// initialized.
///
/// The directory in which the hydraulic boundary database can be found.
/// Thrown when is null.
protected HydraRingCalculatorBase(string hlcdDirectory)
{
if (hlcdDirectory == null)
{
throw new ArgumentNullException(nameof(hlcdDirectory));
}
this.hlcdDirectory = hlcdDirectory;
lastErrorFileParser = new LastErrorFileParser();
illustrationPointsParser = new IllustrationPointsParser();
}
///
/// Gets the temporary output directory that is generated during the calculation.
///
public string OutputDirectory { get; private set; }
///
/// Gets the content of the last error file generated during the Hydra-Ring calculation.
///
public string LastErrorFileContent { get; private set; }
///
/// Gets the result of the illustration points.
///
public GeneralResult IllustrationPointsResult { get; private set; }
///
/// Gets the error message of the illustration points parser.
///
public string IllustrationPointsParserErrorMessage { get; private set; }
///
/// Cancels any currently running Hydra-Ring calculation.
///
public void Cancel()
{
if (hydraRingProcess != null && !hydraRingProcess.HasExited)
{
hydraRingProcess.StandardInput.WriteLine("b");
}
}
///
/// Gets the parsers that are executed on the output file(s) of Hydra-Ring.
///
/// The parsers to execute.
protected virtual IEnumerable GetParsers()
{
yield break;
}
///
/// Sets the values on the output parameters of the calculation.
///
protected abstract void SetOutputs();
///
/// Performs the actual calculation by running the Hydra-Ring executable.
///
/// The uncertainty type used in the calculation.
/// The object containing input data.
/// Thrown when an error occurs while performing the calculation.
protected void Calculate(HydraRingUncertaintiesType uncertaintiesType,
HydraRingCalculationInput hydraRingCalculationInput)
{
try
{
int sectionId = hydraRingCalculationInput.Section.SectionId;
OutputDirectory = CreateWorkingDirectory();
var hydraRingConfigurationService = new HydraRingConfigurationService(uncertaintiesType);
hydraRingConfigurationService.AddHydraRingCalculationInput(hydraRingCalculationInput);
var hydraRingInitializationService = new HydraRingInitializationService(hydraRingCalculationInput.FailureMechanismType, sectionId, hlcdDirectory, OutputDirectory);
hydraRingInitializationService.WriteInitializationScript();
hydraRingConfigurationService.WriteDatabaseCreationScript(hydraRingInitializationService.DatabaseCreationScriptFilePath);
PerformCalculation(OutputDirectory, hydraRingInitializationService);
ExecuteGenericParsers(hydraRingInitializationService, sectionId);
ExecuteCustomParsers(hydraRingInitializationService.TemporaryWorkingDirectory, sectionId);
}
catch (HydraRingFileParserException e)
{
throw new HydraRingCalculationException(e.Message, e.InnerException);
}
catch (Exception e) when (IsSupportedCalculatedException(e))
{
throw new HydraRingCalculationException(string.Format(Resources.HydraRingCalculatorBase_Calculate_Critical_error_during_calculation_Exception_0,
e.Message),
e.InnerException);
}
}
private static bool IsSupportedCalculatedException(Exception e)
{
return e is SecurityException
|| e is IOException
|| e is UnauthorizedAccessException
|| e is ArgumentException
|| e is NotSupportedException
|| e is Win32Exception;
}
///
/// Executes the generic parsers of the calculation.
///
/// The to get the directory from.
/// The id of the section of the calculation.
/// Thrown when the HydraRing file parser
/// encounters an error while parsing HydraRing output.
/// The is set to null when the
/// encounters an error.
private void ExecuteGenericParsers(HydraRingInitializationService hydraRingInitializationService, int sectionId)
{
lastErrorFileParser.Parse(hydraRingInitializationService.TemporaryWorkingDirectory, sectionId);
LastErrorFileContent = lastErrorFileParser.ErrorFileContent;
try
{
illustrationPointsParser.Parse(hydraRingInitializationService.TemporaryWorkingDirectory, sectionId);
IllustrationPointsResult = illustrationPointsParser.Output;
}
catch (HydraRingFileParserException e)
{
IllustrationPointsParserErrorMessage = e.Message;
IllustrationPointsResult = null;
}
}
///
/// Executes the custom parsers of the calculation.
///
/// The temporary directory of the calculation output files.
/// The id of the section of the calculation.
/// Thrown when the HydraRing file parser
/// encounters an error while parsing HydraRing output.
private void ExecuteCustomParsers(string temporaryWorkingDirectory, int sectionId)
{
foreach (IHydraRingFileParser parser in GetParsers())
{
parser.Parse(temporaryWorkingDirectory, sectionId);
}
SetOutputs();
}
///
/// Performs the calculation by starting a Hydra-Ring process.
///
/// The directory of the process.
/// The .
/// Thrown when there was an error in opening the associated file
/// or the wait setting could not be accessed.
private void PerformCalculation(string workingDirectory, HydraRingInitializationService hydraRingInitializationService)
{
hydraRingProcess = HydraRingProcessFactory.Create(
hydraRingInitializationService.MechanismComputationExeFilePath,
hydraRingInitializationService.IniFilePath,
workingDirectory);
hydraRingProcess.Start();
hydraRingProcess.WaitForExit();
hydraRingProcess.Close();
hydraRingProcess = null;
}
///
/// Creates the working directory of the calculation.
///
/// The created working directory.
/// Thrown when the temporary path can't be accessed due to missing permissions.
/// Thrown when the directory can't be created due to missing
/// the required permissions.
private static string CreateWorkingDirectory()
{
string workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
if (Directory.Exists(workingDirectory))
{
Directory.Delete(workingDirectory, true);
}
Directory.CreateDirectory(workingDirectory);
return workingDirectory;
}
}
}