// 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.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using Deltares.Dam.Data; using Deltares.Dam.Data.DamEngineIo; using Deltares.Dam.Data.DataPlugins; using Deltares.Dam.Data.DataPlugins.Configuration; using Deltares.Dam.Data.IO; using Deltares.Dam.Data.License; using Deltares.Dam.Data.Sensors; using Deltares.Dam.Data.UISupport; using Deltares.Dam.Forms.Properties; using Deltares.DamEngine.Data.Standard; using Deltares.DamEngine.Interface; using Deltares.DamEngine.Io; using Deltares.DamEngine.Io.XmlInput; using Deltares.DamEngine.Io.XmlOutput; using Deltares.Geotechnics.Mechanisms; using Deltares.Geotechnics.SurfaceLines; using Deltares.Standard; using Deltares.Standard.Attributes; using Deltares.Standard.Calculate; using Deltares.Standard.EventPublisher; using Deltares.Standard.EventPublisher.Enum; using Deltares.Standard.Forms; using Deltares.Standard.Forms.DExpress; using Deltares.Standard.Forms.Maps; using Deltares.Standard.IO.Xml; using Deltares.Standard.Language; using Deltares.Standard.Logging; using Deltares.Standard.Specifications; using DevExpress.XtraBars; using DevExpress.XtraEditors.Controls; using DevExpress.XtraEditors.Repository; using DevExpress.XtraTreeList.Nodes; using Location = Deltares.Dam.Data.Location; using ProgressDelegate = Deltares.DamEngine.Data.Standard.Calculation.ProgressDelegate; using Sensor = Deltares.Dam.Data.Sensors.Sensor; using SensorLocation = Deltares.Dam.Data.Sensors.SensorLocation; using Soil = Deltares.Geotechnics.Soils.Soil; using TimeSerie = Deltares.Dam.Data.TimeSerie; namespace Deltares.Dam.Forms { public class DamPlugin : IMainFormPlugin, IVisibleEnabled, IVisibleEnabledProvider, IDisposable { private readonly DamProject damProject = new DamProject(); private readonly LocationJobSymbol locationJobSymbol = new LocationJobSymbol(); private readonly Panel panel = new Panel(); private readonly Panel projectPanel = new Panel(); private readonly Panel materialPanel = new Panel(); private readonly Panel locationJobPanel = new Panel(); private readonly Panel locationJobSymbolPanel = new Panel(); private readonly Panel calculationSpecificationPanel = new Panel(); private readonly Panel stabilityKernelTypeSpecificationPanel = new Panel(); private readonly Panel sensorPanel = new Panel(); private readonly GridViewControl locationsControl = new GridViewControl { Name = "Locations", ShowToolbar = true, HideUnusedColumns = true }; private readonly GridViewControl calculationsControl = new GridViewControl { Name = "Calculations", ShowToolbar = true, HideUnusedColumns = true }; private readonly LocationChart locationChart = new LocationChart(); private readonly GridViewControl designCalculationsControl = new GridViewControl { Name = "DesignCalculations", ShowToolbar = true, HideUnusedColumns = true, SendSelectionChanged = true }; private readonly GridViewControl sensorControl = new GridViewControl { Name = "Sensors", ShowToolbar = true, HideUnusedColumns = true, SendSelectionChanged = false // Set to true when custom property editor is ready }; private readonly GridViewControl sensorGroupControl = new GridViewControl { Name = "SensorGroups", ShowToolbar = true, HideUnusedColumns = true, SendSelectionChanged = false // Set to true when custom property editor is ready }; private readonly GridViewControl sensorProfileControl = new GridViewControl { Name = "SensorLocations", ShowToolbar = true, HideUnusedColumns = true, SendSelectionChanged = true // To add/activate custom property editor when ready, see DataEventPublisher_OnSelectionChanged }; private readonly DesignCalculationPropertyControl designCalculationPropertyControl = new DesignCalculationPropertyControl(); private readonly LocationPropertyControl locationPropertyControl = new LocationPropertyControl(); private readonly DamSurfaceLineControl damSurfaceLineControl = new DamSurfaceLineControl(); private readonly DamProjectCalculationSpecificationPropertyControl damProjectCalculationSpecificationPropertyControl = new DamProjectCalculationSpecificationPropertyControl(); private readonly WaterBoardPropertyControl waterBoardPropertyControl = new WaterBoardPropertyControl(); private readonly BarButtonItem showCalculationOptionsItem = new BarButtonItem(); private readonly MapEditor mapControl = new MapEditor(); private MainForm mainForm; private SpatialEditor spatialEditor; private TreeViewControl damNavigator; private DamMapEditor mapEditor; private DamGeometryEditor geometryEditor; private GridViewControl materialsTable; private ProgressDelegate progressDelegate; private bool initial = true; private DAMNewProjectData damNewProjectData; private CsvExportData lastDesignResult; private LocationResult lastLocationResult; private LocationJob currentLocationJob; public int MaxCalculationCores { get { if (mainForm == null) { return 1; } if (damProject != null && damProject.DamProjectData != null) { damProject.DamProjectData.MaxCalculationCores = mainForm.MaxCalculationCores; } return mainForm.MaxCalculationCores; } set { if (damProject != null && damProject.DamProjectData != null && damProject.DamProjectData.MaxCalculationCores != value) { damProject.DamProjectData.MaxCalculationCores = value; } if (mainForm != null) { mainForm.MaxCalculationCores = value; } } } /// /// Gets or sets a value indicating whether this instance is chart visible. /// Only needed as a place holder to be able to switch the visibility of the main chart tab. /// The actual value itself is never (to be) used. /// /// /// true if this instance is chart visible; otherwise, false. /// public bool IsChartVisible { get; set; } [Browsable(false)] public bool StabilityKernelTypeSpecificationsVisible { get; set; } [Label("Options")] public void ShowCalculationOptions() { DataEventPublisher.SelectionChanged(damProject.DamProjectData.WaterBoardJob); FormsSupport.MakeTreeVisible(damProjectCalculationSpecificationPropertyControl); } public void Dispose() { damProject.Dispose(); } public void Configure(MainForm mainForm) { ConfigureSoilUserFilters(); this.mainForm = mainForm; spatialEditor = mainForm.GeometryEditor.SpatialEditor; mainForm.UseTemplateDialog = true; mainForm.AllowMultiCoreSelection = true; DamProject.ProjectWorkingPathLocation = ProjectPathLocation.InProjectMap; RegisterFileHandlers(); RegisterUsedControls(); SetApplicationIcon(); ConfigureControls(); RegisterCalculation(); ConfigureMenu(); BindSupport.Assign(panel, this); BindSupport.Assign(locationJobSymbolPanel, locationJobSymbol); DataEventPublisher.OnAfterChange += DataEventPublisher_OnAfterChange; DataEventPublisher.OnSelectionChanged += DataEventPublisher_OnSelectionChanged; LocalizationManager.CultureChanged += (sender, args) => UpdateNavigatorTopItem(); // for backward compatibility XmlHandler.RegisterObjectHandler(new FailureMechanismeParamatersMStabXmlHandler()); // This is to force the version type (Alpha, Pre-Alpha etc.) to be updated mainForm.ReloadManager.ApplyCurrentUiCultureToAll(); MaxCalculationCores = Math.Max(Environment.ProcessorCount - 1, 1); } public void Assign(object source) { var projectData = source as DamProjectData; if (projectData == null) { return; } initial = false; // var args = Environment.GetCommandLineArgs(); // foreach (string arg in args) // { // if (arg.ToLower().Equals("-wti")) // { // projectData.DamProjectCalculationSpecification.VisibleEnabledProvider = this; // StabilityKernelTypeSpecificationsVisible = true; // } // } // For easy testing purposes, make sure kernel selection option is true projectData.DamProjectCalculationSpecification.VisibleEnabledProvider = this; // For now, make sure stability kernel selection is not possible. Enable this when 2019 is fully available. StabilityKernelTypeSpecificationsVisible = false; Context.CurrentContext = new DamContext { Wti = StabilityKernelTypeSpecificationsVisible }; // get data lastDesignResult = null; lastLocationResult = null; currentLocationJob = null; DataSourceManager.DataSources = projectData.DataSources; DataSourceManager.Active = true; DataSourceManager.CurrentSource = DataSourceSystemType.User; DataSourceManager.StartListening(); BindSupport.Assign(damNavigator, projectData); BindSupport.Assign(projectPanel, projectData); Dike sd = projectData.WaterBoard.SelectedDike; projectData.WaterBoard.SelectedDike = sd; BindSupport.Assign(materialPanel, projectData.WaterBoard.SelectedDike); BindSupport.Assign(sensorPanel, projectData); BindSupport.Assign(calculationSpecificationPanel, projectData.DamProjectCalculationSpecification); BindSupport.Assign(locationJobPanel, currentLocationJob); if (projectData.SensorData != null) { SensorLocation.GetGroups = projectData.SensorData.GetGroups; } // TODO This seems awkward: what is the purpose of assigning DamProjectData to locationJobSymbol, all it uses projectdata for is to fire events... locationJobSymbol.DamProjectData = projectData; damProject.DamProjectData = projectData; mapEditor.Project = projectData; // Force update for Log DataEventPublisher.DataListModified(LogManager.Messages); RealTimeBackgroundValidator.Instance.Register(projectData); lastDesignResult = null; lastLocationResult = null; // Read the Waterlevel Timeseries damProject.ImportWaterLevelTimeSeries(); locationJobSymbol.Update(projectData.LocationJobs); if (projectData.DamProjectType == DamProjectType.DamLiveConfiguration) { if (projectData.SensorData != null) { projectData.SensorData.UpdatePickSensorsForGroups(); } } } public bool IsVisible(string property) { switch (property) { case "IsChartVisible": return damProject.DamProjectData.DamProjectType != DamProjectType.Design; } return true; } public bool IsEnabled(string property) { switch (property) { case "ExecuteSurfacelinesExport": return damProject != null && damProject.DamProjectData != null && damProject.DamProjectData.DamProjectType == DamProjectType.Design && damProject.DamProjectData.LocationJobs != null && damProject.DamProjectData.LocationJobs.Count > 0 && lastDesignResult != null; case "ClearResults": return damProject != null && damProject.DamProjectData != null && damProject.DamProjectData.HasResults(); case "ShowCalculationOptions": return damProject != null && damProject.DamProjectData != null && damProject.DamProjectData.LocationJobs != null && (damProject.DamProjectData.DamProjectType == DamProjectType.Calamity || damProject.DamProjectData.DamProjectType == DamProjectType.Design) && damProject.DamProjectData.LocationJobs.Count > 0; default: return true; } } public bool IsVisible(object source, string property) { if (source is WaterBoard) { switch (property) { case "SelectedDike": return StabilityKernelTypeSpecificationsVisible; default: return true; } } return true; } public bool IsEnabled(object source, string property) { return true; } /// /// Make sure that the top item of the Navigator ("Waterschap") after translation is displayed properly. /// private void UpdateNavigatorTopItem() { TreeListNode node = damNavigator.TreeList.Nodes.FirstNode; if (node == null) { return; } var wb = TreeViewControl.GetInnerDataObject(node.Tag) as WaterBoardJob; if (null != wb) { node.SetValue(0, wb.Name); } } private void SetApplicationIcon() { try { var icon = new Icon(typeof(DamPlugin).Assembly.GetManifestResourceStream( "Deltares.Dam.Forms.Resources.DAM_icon.ico")); mainForm.UseIcon(icon); } catch (Exception) { // Kill exception for Windows XP if load fails // Todo: Solve exception; see MWDAM-391 } } private void RegisterUsedControls() { mainForm.UseNavigation(); mainForm.UseSpatialEditor(); mainForm.UseProperties(); mainForm.UseOutput(); mainForm.UseImage(); mainForm.UseTables(); mainForm.UseCharts(); mainForm.UseValidation(); mainForm.AddControl(mapControl); // mainForm.UseReport(); For now, do not use report [MWDAM-412]. } private void ConfigureControls() { ConfigureMapEditor(); geometryEditor = new DamGeometryEditor(mainForm.GeometryEditor); ConfigureNavigation(); ConfigurePropertyGrid(); ConfigureChart(); ConfigureTable(); ConfigureSoilMechanisms(); } private void ConfigureMapEditor() { mapEditor = new DamMapEditor(mapControl, locationJobSymbol); mapControl.EnableSelectButton = true; mapControl.EnableZoomToData = true; mapControl.EnableZoomToSelection = true; mapControl.EnableZoomRectangle = true; } private void RegisterCalculation() { CalculationManager.Instance.Register("DAM", ExecuteCalculation); } private void RegisterFileHandlers() { mainForm.RegisterNewFileHandler(NewProject); mainForm.RegisterOpenFileHandler("damx", "DAMX Files (*.damx)|*.damx", OpenProject); mainForm.RegisterSaveFileHandler("damx", "DAMX Files (*.damx)|*.damx", damProject.SaveXMLProject); } private object OpenProject(string fileName) { object damProjectData = damProject.OpenXMLProject(fileName); if (LogManager.Messages.Count > 0) { string message = LocalizationManager.GetTranslatedText(this, "OpenProjectWarnings"); LocalizedMessageBox.ShowTranslatedText(mainForm, message); } return damProjectData; } private void ConfigurePropertyGrid() { DynamicPropertyControl dpc = mainForm.DynamicPropertyControl; // deregister the ugly property control provided by the Geotechnics plugin dpc.ClearRegistrationsForType(typeof(Soil)); dpc.BuildDelayedPropertyControlForTypes(() => PropertyControlFactory.GetPropertyControl(), typeof(Soil)); dpc.BuildPropertyControlTabForTypes(locationPropertyControl, typeof(Location), typeof(LocationJob)); dpc.BuildPropertyControlTabForTypes(new LocationScenariosControl(), typeof(Location), typeof(LocationJob)); dpc.BuildPropertyControlTabForTypes(damSurfaceLineControl, typeof(SurfaceLine2), typeof(CharacteristicPoint), typeof(LocationJob)); progressDelegate = waterBoardPropertyControl.DoProgress; dpc.BuildPropertyControlTabForTypes(waterBoardPropertyControl, typeof(WaterBoard), typeof(WaterBoardJob), typeof(DamProjectData)); dpc.BuildPropertyControlTabForTypes(new DikePropertyControl(), typeof(Dike), typeof(DikeJob)); dpc.BuildPropertyControlTabForTypes(damProjectCalculationSpecificationPropertyControl, typeof(DamProjectData)); dpc.BuildPropertyControlTabForTypes(designCalculationPropertyControl, typeof(CsvExportData), typeof(LocationJob)); } private void ConfigureTable() { materialsTable = new GridViewControl { Name = "MaterialsGridViewControl", CurrentContext = Context.CurrentContext, AllowedUserColumnFilters = Soil.AllowedUserColumnFilters, AllowInserts = true, HideUnusedColumns = true, AllowUserColumnFilterEdit = true }; mainForm.RegisterTableControl(typeof(Soil), materialsTable, "Materials"); BindSupport.Bind(materialPanel, materialsTable, typeof(Dike), "SoilList.Soils"); BindSupport.Bind(materialPanel, materialsTable.Parent, typeof(Dike), "SoilList.Soils", BindingType.Visibility); mainForm.DynamicTableControl.RegisterTable(typeof(Location), locationsControl, "Locations"); BindSupport.Bind(projectPanel, locationsControl, p => p.Locations); mainForm.DynamicTableControl.RegisterTable(typeof(CsvExportData), designCalculationsControl, "Design calculations"); BindSupport.Bind(projectPanel, designCalculationsControl, p => p.DesignCalculations); BindSupport.Bind(projectPanel, designCalculationsControl.Parent, p => p.DesignCalculations); mainForm.DynamicTableControl.RegisterTable(typeof(Sensor), sensorControl, "Sensor Configuration"); BindSupport.Bind(sensorControl, sensorControl, p => p.Sensors); BindSupport.Bind(sensorPanel, sensorControl, p => p.SensorData, BindingType.Assign); BindSupport.Bind(sensorPanel, sensorControl.Parent, p => p.SensorData, BindingType.Visibility); mainForm.DynamicTableControl.RegisterTable(typeof(Group), sensorGroupControl, "Sensor Group Configuration"); BindSupport.Bind(sensorGroupControl, sensorGroupControl, p => p.SensorGroups); BindSupport.Bind(sensorPanel, sensorGroupControl, p => p.SensorData, BindingType.Assign); BindSupport.Bind(sensorPanel, sensorGroupControl.Parent, p => p.SensorData, BindingType.Visibility); mainForm.DynamicTableControl.RegisterTable(typeof(SensorLocation), sensorProfileControl, "Sensor Location Data"); BindSupport.Bind(sensorProfileControl, sensorProfileControl, p => p.SensorLocations); BindSupport.Bind(sensorPanel, sensorProfileControl, p => p.SensorData, BindingType.Assign); BindSupport.Bind(sensorPanel, sensorProfileControl.Parent, p => p.SensorData, BindingType.Visibility); } private void ConfigureSoilUserFilters() { Soil.AllowedUserColumnFilters = new[] { UserColumnFilters.MacrostabilityWti, UserColumnFilters.PipingWti }; } private void ConfigureSoilMechanisms() { Soil.Mechanisms = new[] { Mechanism.DAM, Mechanism.Piping, Mechanism.PipingUpliftGradient, Mechanism.Stability }; MechanismSupport.Mechanisms = Soil.Mechanisms; } private void ConfigureChart() { //locationChart = new LocationChart(); mainForm.DynamicChartControl.RegisterChart(typeof(TimeSerie), locationChart, "Time Series"); BindSupport.Bind(locationJobPanel, locationChart.Parent, typeof(LocationJob), "WaterLevelTimeSerie", BindingType.Visibility); // Bind the main tab "Chart" to be able to show it only when needed. BindSupport.Bind(panel, locationChart.Parent.Parent.Parent.Parent.Parent, typeof(DamPlugin), "IsChartVisible", BindingType.Visibility); } /// /// Adds the menus and their bind support. /// private void ConfigureMenu() { var clearResultsMenuItem = new BarButtonItem { Caption = "Clear Results", Name = "ClearResultsMenuItem" }; var trackBar = new RepositoryItemTrackBar { Maximum = 100, TickFrequency = 10 }; var sliderBarItem = new BarEditItem { Width = 300, Edit = trackBar }; var dateEdit = new RepositoryItemDateEdit(); var dateBarItem = new BarEditItem { Edit = dateEdit, Width = 80 }; var timeEdit = new RepositoryItemTimeEdit(); var timeBarItem = new BarEditItem { Edit = timeEdit, Width = 100 }; var projectTypeComboBox = new RepositoryItemComboBox { TextEditStyle = TextEditStyles.DisableTextEditor }; var projectTypeBarItem = new BarEditItem { Width = 100, Edit = projectTypeComboBox, PaintStyle = BarItemPaintStyle.Caption }; var designAnalysisComboBox = new RepositoryItemComboBox { TextEditStyle = TextEditStyles.DisableTextEditor }; var designAnalysisBarItem = new BarEditItem { Width = 130, Edit = designAnalysisComboBox, PaintStyle = BarItemPaintStyle.Caption }; mainForm.CalculationMenu.AddItem(clearResultsMenuItem); mainForm.CalculationMenu.AddItem(showCalculationOptionsItem); mainForm.ToolBar.AddItems(new BarItem[] { projectTypeBarItem, sliderBarItem, dateBarItem, timeBarItem, designAnalysisBarItem }); var surfacelineExportItem = new BarButtonItem { Caption = "Surfacelines", Hint = "Export surfacelines's to CSV files", Name = "exportSurfaceLineBarItem" }; mainForm.ExportMenu.AddItem(surfacelineExportItem); mainForm.ExportMenu.Enabled = true; var manualItem = new BarButtonItem { Caption = "Manual", Name = "manualItem" }; // Reorder the help menu bar BarItemLink aboutItem = mainForm.HelpMenu.ItemLinks.First(bi => bi.Item.Name.Equals("HelpAboutItem")); mainForm.HelpMenu.InsertItem(aboutItem, manualItem); // Remove the link: Tools->License BarItemLink licenseItemLink = mainForm.ToolsMenu.ItemLinks.First(il => il.Item.Name.Equals("LicenseBarButton")); mainForm.ToolsMenu.ItemLinks.Remove(licenseItemLink); BindSupport.Bind(panel, surfacelineExportItem, x => x.ExecuteSurfacelinesExport()); BindSupport.Bind(panel, clearResultsMenuItem, x => x.ClearResults()); BindSupport.Bind(panel, showCalculationOptionsItem, dp => dp.ShowCalculationOptions()); BindSupport.Bind(panel, manualItem, dp => GetUserManual()); BindSupport.Bind(projectPanel, projectTypeBarItem, x => x.DamProjectType, BindingType.LabelAndValue); BindSupport.Bind(calculationSpecificationPanel, designAnalysisBarItem, x => DamProjectCalculationSpecification.SelectedAnalysisType, BindingType.LabelAndValue); BindSupport.Bind(locationJobSymbolPanel, sliderBarItem, x => x.CurrentView); BindSupport.Bind(locationJobSymbolPanel, dateBarItem, x => x.CurrentDateTime); BindSupport.Bind(locationJobSymbolPanel, timeBarItem, x => x.CurrentDateTime); } /// /// Gets the user manual. /// private static void GetUserManual() { try { using var p = new Process(); p.StartInfo.FileName = @"Manual\DAM UI - User manual.pdf"; p.StartInfo.UseShellExecute = true; p.Start(); } catch (Win32Exception noBrowser) { if (noBrowser.ErrorCode == -2147467259) { MessageBox.Show(noBrowser.Message); } } catch (Exception error) { MessageBox.Show(error.Message); } } /// /// Export the redesigned surfacelines to a user selected folder /// [Label("Surfacelines")] private void ExecuteSurfacelinesExport() { var folderBrowserDialog = new FolderBrowserDialog(); folderBrowserDialog.SelectedPath = Path.GetDirectoryName(damProject.ProjectFileName); if (String.IsNullOrEmpty(folderBrowserDialog.SelectedPath)) { folderBrowserDialog.SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); } if (folderBrowserDialog.ShowDialog() == DialogResult.OK) { ExportRedesignedSurfacelines(folderBrowserDialog.SelectedPath); } } [Label("Run")] private void ExecuteCalculation() { if (DamLicense.DamLicenseType == DamLicenseType.None) { LocalizedMessageBox.ShowError(this, "NoLicenseGranted"); return; } DataEventPublisher.InvokeWithoutPublishingEvents(() => { mainForm.Invoke(new TaskDelegate(DataEventPublisher.SelectionChanged), damProject.DamProjectData.WaterBoard); LogManager.Clear(); damProject.DamProjectData.ClearResults(); }); DateTime startTimeCalculation = DateTime.Now; TimeSpan elapsedTime; damProject.DamProjectData.MaxCalculationCores = MaxCalculationCores; string openingMessage = LocalizationManager.GetTranslatedText(this, "CalculationFinished"); string paragraphSepatator = Environment.NewLine + Environment.NewLine; string timeMessage; try { Input input = FillXmlInputFromDamUi.CreateInput(damProject.DamProjectData); #if DEBUG const string inputFilename = "InputFile.xml"; DamXmlSerialization.SaveInputAsXmlFile(inputFilename, input); #endif string inputXml = DamXmlSerialization.SaveInputAsXmlString(input); var damEngineInterface = new EngineInterface(inputXml); damEngineInterface.ProgressDelegate = progressDelegate; 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)) { // Temporarily disable multicore calculation ThrowHelper.ThrowWhenConditionIsTrue( "Multicore is currently not supported. Change number of cores to 1 in Tools menu.", () => damEngineInterface.DamProjectData.MaxCalculationCores > 1); // only if validation is ok, then string outputXml = damEngineInterface.Run(); Output output = DamXmlSerialization.LoadOutputFromXmlString(outputXml); FillDamUiFromXmlOutput.AddOutputToDamProjectData(damProject.DamProjectData, output); elapsedTime = DateTime.Now - startTimeCalculation; timeMessage = String.Format(LocalizationManager.GetTranslatedText(this, "CalculationTime"), elapsedTime.ToString(@"dd\.hh\:mm\:ss")); LocalizedMessageBox.ShowTranslatedText(mainForm,openingMessage + paragraphSepatator + timeMessage); #if DEBUG const string outputFilename = "OutputFile.xml"; DamXmlSerialization.SaveOutputAsXmlFile(outputFilename, output); #endif } else { LogManager.Add(new LogMessage(LogMessageType.Error, typeof(EngineInterface), string.Format("{0}", validationMessages))); } } catch (Exception e) { elapsedTime = DateTime.Now - startTimeCalculation; timeMessage = String.Format(LocalizationManager.GetTranslatedText(this, "CalculationTime"), elapsedTime.ToString(@"dd\.hh\:mm\:ss")); LogManager.Add(new LogMessage(LogMessageType.FatalError, typeof(EngineInterface), string.Format("{0}", e.Message))); openingMessage = LocalizationManager.GetTranslatedText(this, "CalculationFailed"); LocalizedMessageBox.ShowTranslatedText(mainForm, openingMessage + paragraphSepatator + timeMessage + paragraphSepatator + e.Message); } damProject.SaveXMLProject(damProject.ProjectFileName, damProject); damProject.SaveCalculationLogToTextFile(damProject.DamProjectData.CalculationMessages, damProject.ProjectFileName); SetProperControlsAfterCalculation(); } private void SetProperControlsAfterCalculation() { if (damProject.DamProjectData.CalculationMessages != null && damProject.DamProjectData.CalculationMessages.Count > 0) { LogManager.Messages.AddRange(damProject.DamProjectData.CalculationMessages); mainForm.Invoke(new PublisherDelegate(DataEventPublisher.DataListModified), LogManager.Messages, null); } switch (damProject.DamProjectData.DamProjectType) { case DamProjectType.Calamity: { SetProperControlsAfterCalamityCalculation(); break; } case DamProjectType.Design: { SetProperControlsAfterDesignCalculation(); break; } } } private void SetProperControlsAfterDesignCalculation() { LocationJob locationJob = damProject.DamProjectData.GetFirstLocationJobWithDesignResults(); if (locationJob != null) { CsvExportData res = locationJob.GetFirstDesignResult(); if (res != null) { mainForm.SetAsActiveTable(designCalculationsControl); // Selecting the result sets the correct property editor as well as the correct line in the table. try { mainForm.Invoke(new TaskDelegate(DataEventPublisher.SelectionChanged), res); } catch (Exception) { // when all calculations failed but some old result lingers (which it probably shouldn't) you trigger // an event for a non existing designCalculationsControl. For now just ignore this error until it is clear // how DAM should handle "old" results. } } } } private void SetProperControlsAfterCalamityCalculation() { if (damProject.DamProjectData.GetFirstLocationJobWithCalamityResults() != null) { mainForm.SetAsActiveTable(locationsControl); LocationResult res = damProject.DamProjectData.GetFirstLocationJobWithCalamityResults().GetFirstLocationResultWithStabilityTimeSerie(); if (res != null && res.StabilityTimeSerie.GetNearestBasisFileName(locationJobSymbol.CurrentDateTime) != "") { // Make sure the image tab is activated to show the resulting wmf-file (if any) mainForm.Invoke(new TaskDelegate(DataEventPublisher.SelectionChanged), res.StabilityTimeSerie); mainForm.SetAsActiveTabOnMainPanel(mainForm.DynamicImageControl); // Selecting the LocationJob makes sure of proper selected location mainForm.Invoke(new TaskDelegate(DataEventPublisher.SelectionChanged), damProject.DamProjectData.GetFirstLocationJobWithCalamityResults()); // Set chart as active tab as this is the only control that actually displays the results next to the wmf-pictures // displayed on the image tab mainForm.SetAsActiveChart(locationChart); } } } [Label("Clear Results")] private void ClearResults() { DialogResult dialogResult = LocalizedMessageBox.Show(this, "ClearResultsQuestion", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (dialogResult == DialogResult.Yes) { if ((damProject != null) && (damProject.DamProjectData != null)) { DataEventPublisher.InvokeWithoutPublishingEvents(() => { try { damProject.DeleteResults(); lastDesignResult = null; lastLocationResult = null; } catch (Exception exception) { mainForm.Invoke(new ExceptionDelegate(ShowErrorMessageBox), exception); } finally { UpdateForDamApplicationType(); SetProperControlsAfterClearResults(); } }); } } } private void SetProperControlsAfterClearResults() { // update logmessages mainForm.Invoke(new PublisherDelegate(DataEventPublisher.DataListModified), LogManager.Messages, null); // update the data mainForm.Invoke(new TaskDelegate(DataEventPublisher.SelectionChanged), damProject.DamProjectData.LocationJobs.FirstOrDefault()); mainForm.Invoke(new PublisherDelegate(DataEventPublisher.DataListModified), damProject.DamProjectData.LocationJobs, null); mainForm.Invoke(new TaskDelegate(DataEventPublisher.SelectionChanged), damProject.DamProjectData.WaterBoard.Locations.FirstOrDefault()); // For design, extra action is needed to get rid of the calculation property window if (damProject.DamProjectData.DamProjectType == DamProjectType.Design) { SetProperControlsAfterClearDesignResults(); } // Set map for main window mainForm.SetAsActiveTabOnMainPanel(mapControl); // Set the locations table mainForm.SetAsActiveTable(locationsControl); } private void SetProperControlsAfterClearDesignResults() { mainForm.Invoke(new PublisherDelegate(DataEventPublisher.AfterChange), damProject.DamProjectData.LocationJobs.FirstOrDefault(), null); } private void ConfigureNavigation() { damNavigator = new WaterBoardTreeView(); mainForm.NavigatorControl.RegisterResource(typeof(WaterBoard), damNavigator, "Waterboard"); BindSupport.Bind(projectPanel, damNavigator, typeof(DamProjectData), "WaterBoardJob"); } private void DataEventPublisher_OnAfterChange(object sender, PublishEventArgs e) { if (sender is CsvExportData) { if (e.Property != null && e.Property.Equals("ExceptionMessage")) { string exceptionMessage = ((CsvExportData) sender).ExceptionMessage; LocalizedMessageBox.ShowError(this, exceptionMessage); } } else if (sender == locationJobSymbol) { if (e.Property != null && e.Property.Equals("CurrentScenarioType")) { DataEventPublisher.AfterChange(this); } if (e.Property != null && e.Property.Equals("CurrentDateTime")) { if (lastLocationResult != null) { ShowLocationImage(lastLocationResult.StabilityTimeSerie, locationJobSymbol.CurrentDateTime); } } } else if (sender == damProject.DamProjectData) { if (e.Property != null && e.Property.Equals("DamProjectType")) { if (damProject.DamProjectData.WaterBoard != null) { DataEventPublisher.AfterChange(damProject.DamProjectData.WaterBoard); DataEventPublisher.DataListModified(damProject.DamProjectData.Locations); } } if (e.Property != null && e.Property.Equals("MaxCalculationCores")) { mainForm.MaxCalculationCores = Math.Min(Environment.ProcessorCount - 1, damProject.DamProjectData.MaxCalculationCores); } } else if (sender == damProject.DamProjectData.WaterBoard) { try { damProject.ImportWaterLevelTimeSeries(); locationJobSymbol.Update(damProject.DamProjectData.LocationJobs); } catch (TimeSerieSchemaValidationException exception) { LocalizedMessageBox.ShowError(this, exception.Message); } } else if (sender == currentLocationJob && sender != null) { DataEventPublisher.AfterChange(this, "CurrentLocationJob"); } else if (sender == materialsTable) { if (e.Property.Equals("SelectedFilter")) { var damContext = Context.CurrentContext as DamContext; if (damContext != null) { damContext.SoilUserFilter = GetSoilUserFilterFromMaterialTable(); UpdatePropertyControl(); } } } } private void UpdatePropertyControl() { if (mainForm.DynamicPropertyControl != null && mainForm.DynamicPropertyControl.SelectedObject != null) { DataEventPublisher.AfterChange(mainForm.DynamicPropertyControl.SelectedObject); } } private UserColumnFilters? GetSoilUserFilterFromMaterialTable() { string selectedFilterText = materialsTable.SelectedFilter.Text; UserColumnFilters? selectedFilterValue; try { selectedFilterValue = (UserColumnFilters) Enum.Parse(typeof(UserColumnFilters), selectedFilterText); } catch (ArgumentException) { // Filter "All" or not a valid member of the UserColumnFilters enumeration selectedFilterValue = null; } return selectedFilterValue; } private void SelectWaterBoardJob(WaterBoardJob job) { DataEventPublisher.SelectionChanged(damProject.DamProjectData); } private void SelectLocationJob(LocationJob locationJob) { Location location = locationJob.Location; DataEventPublisher.SelectionChanged(location, PropertyEditorReactionType.Ignore); if (damProject.DamProjectData.DamProjectType == DamProjectType.Calamity) { lastLocationResult = locationJob.GetLocationResultWithStabilityTimeSerie(location.Name); if (lastLocationResult == null) { lastLocationResult = locationJob.GetFirstLocationResultWithStabilityTimeSerie(); } if (lastLocationResult != null) { ShowLocationImage(lastLocationResult.StabilityTimeSerie, locationJobSymbol.CurrentDateTime); } else { ShowLocationImage(null, locationJobSymbol.CurrentDateTime); } } if (damProject.DamProjectData.DamProjectType == DamProjectType.Design) { Location designLocation = location; if (lastDesignResult != null) { designLocation = damProject.DamProjectData.Locations.Find(x => x.Name == lastDesignResult.LocationName); } if (lastDesignResult == null || designLocation != location) { lastDesignResult = locationJob.GetFirstDesignResult(); DataEventPublisher.SelectionChanged(lastDesignResult, PropertyEditorReactionType.Ignore); } } DataEventPublisher.AfterChange(this); } private LocationJob FirstMatchingLocationJob(Location location) { // get the first matching location job, if any return damProject.DamProjectData.LocationJobs .FirstOrDefault(job => job.Location == location); } private void SelectFirstMatchingLocationJobIfAny(Location location) { LocationJob locationJob = FirstMatchingLocationJob(location); if (locationJob != null) { DataEventPublisher.SelectionChanged(locationJob); } } private void SelectLocation(Location location) { SelectFirstMatchingLocationJobIfAny(location); } private void SelectDesignResult(CsvExportData result) { lastDesignResult = result; locationJobSymbol.CurrentProfileName = result.ProfileName; locationJobSymbol.CurrentScenarioName = result.ScenarioName; locationJobSymbol.CurrentCalculation = result.StabilityModel.ToString(); Location location = damProject.DamProjectData.Locations.Find(x => x.Name == lastDesignResult.LocationName); if (location != null) { SelectFirstMatchingLocationJobIfAny(location); } DataEventPublisher.AfterChange(this); } private void DataEventPublisher_OnSelectionChanged(object sender, PublishEventArgs e) { Type type = sender == null ? null : sender.GetType(); if (((SelectionEventArgs) e).PropertyEditorReactionType != PropertyEditorReactionType.Ignore) { if (sender is WaterBoardJob) { SelectWaterBoardJob((WaterBoardJob) sender); } else if (sender is LocationJob) { currentLocationJob = (LocationJob) sender; SelectLocationJob(currentLocationJob); BindSupport.Assign(locationJobPanel, currentLocationJob); DataEventPublisher.AfterChange(this, "CurrentLocationJob"); } else if (sender is Location) { SelectLocation((Location) sender); } else if (sender is SensorLocation) { var senderLocation = (SensorLocation) sender; SelectLocation(senderLocation.Location); // Activate any custom property editor for SensorLocation here. } else if (sender is CsvExportData) { SelectDesignResult((CsvExportData) sender); } } } private void ShowImage(string file) { if (!String.IsNullOrEmpty(file)) { mainForm.DynamicImageControl.ZoomablePictureBox.Image = File.Exists(file) ? Image.FromFile(file) : null; } else { mainForm.DynamicImageControl.ZoomablePictureBox.Image = null; } } private void ShowLocationImage(TimeSerie result, DateTime currentTime) { if (result != null) { string path = DamProject.ProjectWorkingPath + result.Entries.First().RelativeCalculationPathName; string file = result.GetNearestBasisFileName(currentTime); file = path + file + ".wmf"; ShowImage(file); } else { mainForm.DynamicImageControl.ZoomablePictureBox.Image = null; } } private void UpdateForDamApplicationType() { Assign(damProject.DamProjectData); } private object NewProject() { if (initial) { initial = false; return new DamProjectData(); } damNewProjectData = new DAMNewProjectData { DamProjectType = DamProjectType.Design, DamDataSourceFileName = damProject.DamProjectData.DamDataSourceFileName }; var dlg = new DamNewProjectDialog { DamNewProjectData = damNewProjectData }; dlg.Visible = false; if (dlg.ShowDialog() == DialogResult.OK) { damProject.ClearProject(); damProject.ProjectFileName = dlg.DamNewProjectData.DamProjectFileName; damProject.DamProjectData.DataSourceEsriProjection = dlg.DamNewProjectData.DataSourceEsriProjection; CalculationManager.Instance.RunCalculation(Import); mainForm.FileName = damProject.ProjectFileName; mainForm.SetMainWindowCaption(damProject.ProjectFileName); } return damProject; } private void Import() { LogManager.Clear(); mainForm.Invoke(new TaskDelegate(DataEventPublisher.SelectionChanged), damProject.DamProjectData.WaterBoard); progressDelegate(0); Application.UseWaitCursor = true; try { LogManager.Clear(); DamProjectType projectType = damNewProjectData.DamProjectType; string fileName = damNewProjectData.DamDataSourceFileName; DataSourceContainer dataSourceContainer = DataSourceContainer.Deserialize(fileName); string damProjectFolder = Path.GetDirectoryName(fileName); dataSourceContainer.DataSourceEsriProjection = dataSourceContainer.DataSourceEsriProjection ?? damNewProjectData.DataSourceEsriProjection; DataEventPublisher.ThreadPublisher = new ThreadPublisher(); // ATTENTION: Do not stop DataEventPublisher because it is needed to determine the source of the data with DataSourceManager DataSourceManager.StartListening(); damProject.Import(damProjectFolder, dataSourceContainer, damNewProjectData.SelectedDikeRingIds, damNewProjectData.DamProjectType, progressDelegate); // Importing data creates a new DamProjectData object so the values set in the dialog have to be reset. damProject.DamProjectData.DamProjectType = projectType; damProject.DamProjectData.DamDataSourceFileName = fileName; if (dataSourceContainer.MapSoilProfile2D != null) { // Make sure the 2D-geometrie map is assigned damProject.AssignGeometry2DMapnameIfNotAssigned(Path.Combine(damProjectFolder, dataSourceContainer.MapSoilProfile2D)); } // If profiles are defined as relative profiles, new absolute profiles will be generated for each location if (dataSourceContainer.IsImportAsRelativeProfiles) { WaterBoardPostProcessRelativeProfiles.CreateAbsoluteProfiles(damProject.DamProjectData.WaterBoard, dataSourceContainer.SoilProfileCharacteristicPointReference); } damProject.DamProjectData.DataSources = DataSourceManager.DataSources; // Read sensor data if DamLive Configuration project if (damNewProjectData.DamProjectType == DamProjectType.DamLiveConfiguration) { if (damProject.DamProjectData.WaterBoard.Dikes.Count != 1) { throw new DataPluginImporterException(String.Format("DamLive Configuration must have exactly 1 dike; this project has {0} dikes", damProject.DamProjectData.WaterBoard.Dikes.Count)); } Dike dike = damProject.DamProjectData.WaterBoard.Dikes.First(); string sensorConfigurationFilename = Path.GetFullPath(damNewProjectData.SensorConfigurationFileName); if (File.Exists(sensorConfigurationFilename)) { SensorImportFromExcelSheet.ReadSensorDataFromExcel(sensorConfigurationFilename, dike); damProject.DamProjectData.FillOverallSensorData(); } } // Save the data damProject.SaveXMLProject(damProject.ProjectFileName, damProject); damProject.SaveCalculationLogToTextFile(damProject.DamProjectData.CalculationMessages, damProject.ProjectFileName); progressDelegate(1); mainForm.Invoke(new EmptyDelegate(UpdateForDamApplicationType)); mainForm.Invoke(new TaskDelegate(DataEventPublisher.SelectionChanged), damProject.DamProjectData.LocationJobs.FirstOrDefault()); DataEventPublisher.TransferBindingsToDefaultPublisher(); } catch (Exception e) { Application.UseWaitCursor = false; mainForm.Invoke(new ExceptionDelegate(ShowImportErrorMessage), e); } finally { Application.UseWaitCursor = false; } } /// /// General error message /// /// /// private void ShowErrorMessageBox(Exception e) { var stringBuilder = new StringBuilder(e.Message + Environment.NewLine); Exception exception = e.InnerException; while (exception != null) { stringBuilder.AppendLine(exception.Message + Environment.NewLine); exception = exception.InnerException; } var args = new object[] { stringBuilder.ToString() }; LocalizedMessageBox.ShowError(this, "ErrorMessage", args); } /// /// Show import error messages /// /// private void ShowImportErrorMessage(Exception e) { var stringBuilder = new StringBuilder(e.Message + Environment.NewLine); Exception exception = e.InnerException; while (exception != null) { stringBuilder.Append(exception.Message + Environment.NewLine); exception = exception.InnerException; } var args = new object[] { Environment.NewLine, damNewProjectData.DamDataSourceFileName, Environment.NewLine + Environment.NewLine, Environment.NewLine, stringBuilder.ToString() }; LocalizedMessageBox.ShowError(this, "ErrorImportingDataSourceFile", args); } /// /// Export the redesigned surfacelines /// /// void ExportRedesignedSurfacelines(string path) { DataEventPublisher.InvokeWithoutPublishingEvents(() => { string baseFilename = Path.Combine(path, Path.GetFileNameWithoutExtension(damProject.ProjectFileName)); List exportListCharacteristicPoints; List exportListSurfaceLines; // Build list (for piping) of redesigsurfacelines and list of surfacelines with charateristic points and write them exportListCharacteristicPoints = new List(); exportListSurfaceLines = new List(); var newlyCreatedSurfaceLines = new List(); foreach (CsvExportData designResult in damProject.DamProjectData.DesignCalculations) { SurfaceLine2 redesignedSurfaceLinePipingGlobal = designResult.CreateRedesignedSurfaceLineGlobal(); if (redesignedSurfaceLinePipingGlobal != null) { newlyCreatedSurfaceLines.Add(redesignedSurfaceLinePipingGlobal); // Identifiers CsvExportSurfaceLineIdentifiers csvExportSurfaceLineIdentifiers = CreateCsvExportSurfaceLineIdentifiers(designResult); // Characteristic points exportListCharacteristicPoints.Add(new CsvExportCharacteristicPoints(csvExportSurfaceLineIdentifiers, redesignedSurfaceLinePipingGlobal)); // Surfaceline exportListSurfaceLines.Add(new CsvExportSurfaceLine(csvExportSurfaceLineIdentifiers, redesignedSurfaceLinePipingGlobal)); } } if (exportListCharacteristicPoints.Count > 0) { WriteSurfacelines(baseFilename + "_Piping", exportListSurfaceLines, exportListCharacteristicPoints); } foreach (SurfaceLine2 newlyCreatedGlobalSurfaceLine in newlyCreatedSurfaceLines) { newlyCreatedGlobalSurfaceLine.Dispose(); } newlyCreatedSurfaceLines.Clear(); // Build list (for stability) of redesigsurfacelines and list of surfacelines with charateristic points and write them exportListCharacteristicPoints = new List(); exportListSurfaceLines = new List(); foreach (CsvExportData designResult in damProject.DamProjectData.DesignCalculations) { SurfaceLine2 globalRedesignedSurfaceLine = designResult.CreateRedesignedSurfaceLineGlobal(); if (globalRedesignedSurfaceLine != null) { newlyCreatedSurfaceLines.Add(globalRedesignedSurfaceLine); // Identifiers CsvExportSurfaceLineIdentifiers csvExportSurfaceLineIdentifiers = CreateCsvExportSurfaceLineIdentifiers(designResult); // Characteristic points exportListCharacteristicPoints.Add(new CsvExportCharacteristicPoints(csvExportSurfaceLineIdentifiers, globalRedesignedSurfaceLine)); // Surfaceline exportListSurfaceLines.Add(new CsvExportSurfaceLine(csvExportSurfaceLineIdentifiers, globalRedesignedSurfaceLine)); } } if (exportListCharacteristicPoints.Count > 0) { WriteSurfacelines(baseFilename + "_Stability", exportListSurfaceLines, exportListCharacteristicPoints); } foreach (SurfaceLine2 newlyCreatedGlobalSurfaceLine in newlyCreatedSurfaceLines) { newlyCreatedGlobalSurfaceLine.Dispose(); } newlyCreatedSurfaceLines.Clear(); }); } /// /// Creates the CSV export surfaceline identifiers, based on the designresult. /// /// The design result. /// private static CsvExportSurfaceLineIdentifiers CreateCsvExportSurfaceLineIdentifiers(CsvExportData designResult) { var csvExportSurfaceLineIdentifiers = new CsvExportSurfaceLineIdentifiers { LocationId = designResult.LocationName, SoilProfileId = designResult.ProfileName, Scenario = designResult.LocationScenarioId, CalculationMechanism = designResult.DamFailureMechanismeCalculation.FailureMechanismSystemType.ToString(), CalculationModel = "" }; switch (designResult.DamFailureMechanismeCalculation.FailureMechanismSystemType) { case FailureMechanismSystemType.StabilityOutside: csvExportSurfaceLineIdentifiers.CalculationModel = designResult.DamFailureMechanismeCalculation.StabilityModelType.ToString(); break; case FailureMechanismSystemType.StabilityInside: csvExportSurfaceLineIdentifiers.CalculationModel = designResult.DamFailureMechanismeCalculation.StabilityModelType.ToString(); break; case FailureMechanismSystemType.Piping: csvExportSurfaceLineIdentifiers.CalculationModel = designResult.DamFailureMechanismeCalculation.PipingModelType.ToString(); break; } return csvExportSurfaceLineIdentifiers; } /// /// Writes the surfacelines. /// /// The base filename. /// The export list of surface lines. /// The export list of characteristic points of the surface lines. private void WriteSurfacelines(string baseFilename, List exportListSurfaceLines, List exportListCharacteristicPoints) { // Export lines with characteristic points string fileNameCharacteristicPoints = baseFilename + "_CharacteristicPoints.csv"; var characteristicPointCsvExporter = new CsvExporter( Path.Combine(baseFilename, fileNameCharacteristicPoints), exportListCharacteristicPoints) { WriteHeaderInFirstLine = true }; characteristicPointCsvExporter.WriteFile(); // Export surfacelines string fileNameSurfaceline = baseFilename + "_Surfacelines.csv"; var surfacelineCsvExporter = new CsvExporter(Path.Combine( baseFilename, fileNameSurfaceline), exportListSurfaceLines) { WriteHeaderInFirstLine = true }; surfacelineCsvExporter.WriteFile(); } } }