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.Standard.Application; namespace Deltares.Dam.Application.Live { public class ModelRunner : IModelRunner { 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(); 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 DAM project data used for stability calculations /// public DamProjectData ProjectData { get; set; } public CalculationParameters CalculationParameters { get; set; } public TimeSerieCollection OutputTimeSeriesCollection { get; set; } public TimeSerieCollection InputTimeSeriesCollection { get; set; } public string StabilityWorkingPath { get; set; } public string PipingWorkingPath { get; set; } public string StabilityExePath { get; set; } public bool UseMStabForCalculation { get; set; } public double WaterLevelOffset { get; set; } public string WorkingPath { get; set; } public string Filter { get; set; } public IModelRunner RunnerDelegate { get; set; } public bool HasErrors { get { return Logger.HasLoggedExceptions; } } public ModelRunner() { // WorkingPath will be extracted from file name if available StabilityWorkingPath = Settings.Default.StabilityWorkingPath; PipingWorkingPath = Settings.Default.PipingWorkingPath; StabilityExePath = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), Settings.Default.MStabExePath); UseMStabForCalculation = Settings.Default.UseMStabForCalculation; WaterLevelOffset = Settings.Default.WaterLevelOffset; } /// /// Initializes this instance. Validates input files, deletes former project files, /// creates working folders and prepares the in and output time series collections /// public virtual void Initialize() { if (ProjectData == null) { if (DamXFile == null) throw new InvalidOperationException(NoDamxFile); ProjectData = 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); } } /// /// Creates and set's up the working directories. By default the directory the output file /// will be used if the working path is not set /// public virtual void CreateAndSetWorkingDirectories() { string targetPath; string defaultFolder = FewsOutputFile != null ? FewsOutputFile.DirectoryName : null; if (!string.IsNullOrWhiteSpace(WorkingPath)) { targetPath = WorkingPath; } else { if (defaultFolder == null) { throw new InvalidOperationException(ErrorExtractingWorkingFolder); } targetPath = string.IsNullOrWhiteSpace(defaultFolder) ? "." : defaultFolder; } CreateWorkingDirectory(targetPath); // The following path's are sub folders of the main working path StabilityWorkingPath = Path.Combine(targetPath, StabilityWorkingPath); CreateWorkingDirectory(StabilityWorkingPath); PipingWorkingPath = Path.Combine(targetPath, PipingWorkingPath); CreateWorkingDirectory(PipingWorkingPath); } /// /// Creates the working directory if they not exist. /// /// Name of the directory. protected static void CreateWorkingDirectory(string directoryName) { if (string.IsNullOrWhiteSpace(directoryName)) throw new ArgumentNullException("directoryName"); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } } /// /// Performs all the steps to transform the input time series collection to /// the output time series collection with the calculated values /// public virtual void Run() { Initialize(); CreateAndSetWorkingDirectories(); DeleteFormerProjectFiles(); Process(); WriteResultsToFile(FewsOutputFile.FullName); } /// /// Processes an output time series using the input time series collection /// and the required project data. /// public virtual void Process() { if (ProjectData == null) throw new InvalidOperationException("No DAM project data available."); if (InputTimeSeriesCollection == null) throw new InvalidOperationException("No input time series collection available."); if (OutputTimeSeriesCollection == null) throw new InvalidOperationException("No output time series collection available."); if (RunnerDelegate != null) { RunnerDelegate.WorkingPath = WorkingPath; RunnerDelegate.WaterLevelOffset = WaterLevelOffset; RunnerDelegate.StabilityExePath = StabilityExePath; RunnerDelegate.PipingWorkingPath = PipingWorkingPath; RunnerDelegate.UseMStabForCalculation = UseMStabForCalculation; RunnerDelegate.StabilityWorkingPath = StabilityWorkingPath; RunnerDelegate.CalculationParameters = CalculationParameters; RunnerDelegate.InputTimeSeriesCollection = InputTimeSeriesCollection; RunnerDelegate.OutputTimeSeriesCollection = OutputTimeSeriesCollection; RunnerDelegate.ProjectData = ProjectData; RunnerDelegate.Filter = Filter; RunnerDelegate.Run(); // sync back the processed data OutputTimeSeriesCollection = RunnerDelegate.OutputTimeSeriesCollection; } else { Logger.LogWarning("No model runner set to delegate the processing of the time series to"); } } /// /// Cleanup the working directory /// protected void DeleteFormerProjectFiles() { // remove known files for stability calculation var di = new DirectoryInfo(StabilityWorkingPath); FileInfo[] rgFiles = di.GetFiles("*.st?"); foreach (FileInfo fi in rgFiles) File.Delete(fi.FullName); rgFiles = di.GetFiles("*.sti.xml"); foreach (FileInfo fi in rgFiles) File.Delete(fi.FullName); rgFiles = di.GetFiles("*.wmf"); foreach (FileInfo fi in rgFiles) File.Delete(fi.FullName); rgFiles = di.GetFiles("*.err"); foreach (FileInfo fi in rgFiles) File.Delete(fi.FullName); foreach (FileInfo fi in rgFiles) File.Delete(fi.FullName); // remove known files for piping calculator di = new DirectoryInfo(PipingWorkingPath); rgFiles = di.GetFiles("*" + PipingCalculator.PipingFilenameExtension); foreach (FileInfo fi in rgFiles) File.Delete(fi.FullName); } /// /// Writes all the results in the output collection to the given file /// /// protected 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); } } } }