// 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.Linq; using System.Windows.Forms; using Core.Common.Base; using Core.Common.Base.Geometry; using Core.Common.Controls.DataGrid; using Core.Common.Controls.Views; using Ringtoets.Common.Data.AssessmentSection; using Ringtoets.Common.Data.Calculation; using Ringtoets.Common.Data.FailureMechanism; using Ringtoets.Common.Data.Hydraulics; using Ringtoets.Common.Forms.ChangeHandlers; using Ringtoets.Common.Forms.Helpers; using Ringtoets.Common.Forms.PresentationObjects; using Ringtoets.MacroStabilityInwards.Data; using Ringtoets.MacroStabilityInwards.Forms.PresentationObjects; using Ringtoets.MacroStabilityInwards.Forms.Properties; using Ringtoets.MacroStabilityInwards.Primitives; using Ringtoets.MacroStabilityInwards.Service; namespace Ringtoets.MacroStabilityInwards.Forms.Views { /// /// This class is a view for configuring piping calculations. /// public partial class PipingCalculationsView : UserControl, ISelectionProvider { private const int stochasticSoilModelColumnIndex = 1; private const int stochasticSoilProfileColumnIndex = 2; private const int selectableHydraulicBoundaryLocationColumnIndex = 4; private readonly Observer assessmentSectionObserver; private readonly RecursiveObserver pipingInputObserver; private readonly RecursiveObserver pipingCalculationGroupObserver; private readonly RecursiveObserver pipingCalculationObserver; private readonly Observer failureMechanismObserver; private readonly RecursiveObserver pipingSurfaceLineObserver; private readonly Observer pipingStochasticSoilModelsObserver; private readonly RecursiveObserver stochasticSoilProfileObserver; private IAssessmentSection assessmentSection; private CalculationGroup calculationGroup; private MacroStabilityInwardsFailureMechanism macroStabilityInwardsFailureMechanism; private bool updatingDataSource; public event EventHandler SelectionChanged; /// /// Creates a new instance of . /// public PipingCalculationsView() { InitializeComponent(); InitializeDataGridView(); InitializeListBox(); failureMechanismObserver = new Observer(OnFailureMechanismUpdate); assessmentSectionObserver = new Observer(UpdateSelectableHydraulicBoundaryLocationsColumn); // The concat is needed to observe the input of calculations in child groups. pipingInputObserver = new RecursiveObserver(UpdateDataGridViewDataSource, pcg => pcg.Children.Concat(pcg.Children.OfType().Select(pc => pc.InputParameters))); pipingCalculationGroupObserver = new RecursiveObserver(UpdateDataGridViewDataSource, pcg => pcg.Children); pipingCalculationObserver = new RecursiveObserver(dataGridViewControl.RefreshDataGridView, pcg => pcg.Children); pipingSurfaceLineObserver = new RecursiveObserver(UpdateDataGridViewDataSource, rpslc => rpslc); pipingStochasticSoilModelsObserver = new Observer(OnStochasticSoilModelsUpdate); stochasticSoilProfileObserver = new RecursiveObserver(dataGridViewControl.RefreshDataGridView, ssmc => ssmc.SelectMany(ssm => ssm.StochasticSoilProfiles)); } /// /// Gets or sets the piping failure mechanism. /// public MacroStabilityInwardsFailureMechanism MacroStabilityInwardsFailureMechanism { get { return macroStabilityInwardsFailureMechanism; } set { macroStabilityInwardsFailureMechanism = value; pipingStochasticSoilModelsObserver.Observable = macroStabilityInwardsFailureMechanism?.StochasticSoilModels; failureMechanismObserver.Observable = macroStabilityInwardsFailureMechanism; pipingSurfaceLineObserver.Observable = macroStabilityInwardsFailureMechanism?.SurfaceLines; stochasticSoilProfileObserver.Observable = macroStabilityInwardsFailureMechanism?.StochasticSoilModels; UpdateStochasticSoilModelColumn(); UpdateStochasticSoilProfileColumn(); UpdateSelectableHydraulicBoundaryLocationsColumn(); UpdateSectionsListBox(); UpdateGenerateScenariosButtonState(); } } /// /// Gets or sets the assessment section. /// public IAssessmentSection AssessmentSection { get { return assessmentSection; } set { assessmentSection = value; assessmentSectionObserver.Observable = assessmentSection; UpdateSelectableHydraulicBoundaryLocationsColumn(); } } public object Data { get { return calculationGroup; } set { calculationGroup = value as CalculationGroup; if (calculationGroup != null) { UpdateDataGridViewDataSource(); pipingInputObserver.Observable = calculationGroup; pipingCalculationObserver.Observable = calculationGroup; pipingCalculationGroupObserver.Observable = calculationGroup; } else { dataGridViewControl.SetDataSource(null); pipingInputObserver.Observable = null; pipingCalculationObserver.Observable = null; pipingCalculationGroupObserver.Observable = null; } } } public object Selection { get { return CreateSelectedItemFromCurrentRow(); } } protected override void OnLoad(EventArgs e) { // Necessary to correctly load the content of the dropdown lists of the comboboxes... UpdateDataGridViewDataSource(); base.OnLoad(e); } protected override void Dispose(bool disposing) { if (disposing) { dataGridViewControl.RemoveCellFormattingHandler(OnCellFormatting); assessmentSectionObserver.Dispose(); failureMechanismObserver.Dispose(); pipingInputObserver.Dispose(); pipingCalculationObserver.Dispose(); pipingSurfaceLineObserver.Dispose(); pipingCalculationGroupObserver.Dispose(); stochasticSoilProfileObserver.Dispose(); pipingStochasticSoilModelsObserver.Dispose(); components?.Dispose(); } base.Dispose(disposing); } private void InitializeDataGridView() { dataGridViewControl.AddCurrentCellChangedHandler(DataGridViewOnCurrentCellChanged); dataGridViewControl.AddCellFormattingHandler(OnCellFormatting); dataGridViewControl.AddTextBoxColumn( nameof(PipingCalculationRow.Name), Resources.PipingCalculation_Name_DisplayName); dataGridViewControl.AddComboBoxColumn>( nameof(PipingCalculationRow.StochasticSoilModel), Resources.PipingInput_StochasticSoilModel_DisplayName, null, nameof(DataGridViewComboBoxItemWrapper.This), nameof(DataGridViewComboBoxItemWrapper.DisplayName)); dataGridViewControl.AddComboBoxColumn>( nameof(PipingCalculationRow.StochasticSoilProfile), Resources.PipingInput_StochasticSoilProfile_DisplayName, null, nameof(DataGridViewComboBoxItemWrapper.This), nameof(DataGridViewComboBoxItemWrapper.DisplayName)); dataGridViewControl.AddTextBoxColumn( nameof(PipingCalculationRow.StochasticSoilProfileProbability), Resources.PipingCalculationsView_InitializeDataGridView_Stochastic_soil_profile_probability); dataGridViewControl.AddComboBoxColumn>( nameof(PipingCalculationRow.SelectableHydraulicBoundaryLocation), Resources.PipingInput_HydraulicBoundaryLocation_DisplayName, null, nameof(DataGridViewComboBoxItemWrapper.This), nameof(DataGridViewComboBoxItemWrapper.DisplayName)); string dampingFactorExitHeader = Resources.PipingInput_DampingFactorExit_DisplayName; dampingFactorExitHeader = char.ToLowerInvariant(dampingFactorExitHeader[0]) + dampingFactorExitHeader.Substring(1); dataGridViewControl.AddTextBoxColumn( nameof(PipingCalculationRow.DampingFactorExitMean), $"{Resources.Probabilistics_Mean_Symbol} {dampingFactorExitHeader}"); string phreaticLevelExitHeader = Resources.PipingInput_PhreaticLevelExit_DisplayName; phreaticLevelExitHeader = char.ToLowerInvariant(phreaticLevelExitHeader[0]) + phreaticLevelExitHeader.Substring(1); dataGridViewControl.AddTextBoxColumn( nameof(PipingCalculationRow.PhreaticLevelExitMean), $"{Resources.Probabilistics_Mean_Symbol} {phreaticLevelExitHeader}"); dataGridViewControl.AddTextBoxColumn( nameof(PipingCalculationRow.EntryPointL), Resources.PipingInput_EntryPointL_DisplayName); dataGridViewControl.AddTextBoxColumn( nameof(PipingCalculationRow.ExitPointL), Resources.PipingInput_ExitPointL_DisplayName); UpdateStochasticSoilModelColumn(); UpdateStochasticSoilProfileColumn(); UpdateSelectableHydraulicBoundaryLocationsColumn(); } private void InitializeListBox() { listBox.DisplayMember = nameof(FailureMechanismSection.Name); listBox.SelectedValueChanged += ListBoxOnSelectedValueChanged; } private void UpdateGenerateScenariosButtonState() { buttonGenerateScenarios.Enabled = macroStabilityInwardsFailureMechanism != null && macroStabilityInwardsFailureMechanism.SurfaceLines.Any() && macroStabilityInwardsFailureMechanism.StochasticSoilModels.Any(); } private static void SetItemsOnObjectCollection(DataGridViewComboBoxCell.ObjectCollection objectCollection, object[] comboBoxItems) { objectCollection.Clear(); objectCollection.AddRange(comboBoxItems); } private PipingInputContext CreateSelectedItemFromCurrentRow() { DataGridViewRow currentRow = dataGridViewControl.CurrentRow; var pipingCalculationRow = (PipingCalculationRow) currentRow?.DataBoundItem; PipingInputContext selection = null; if (pipingCalculationRow != null) { selection = new PipingInputContext( pipingCalculationRow.PipingCalculation.InputParameters, pipingCalculationRow.PipingCalculation, macroStabilityInwardsFailureMechanism.SurfaceLines, macroStabilityInwardsFailureMechanism.StochasticSoilModels, macroStabilityInwardsFailureMechanism, assessmentSection); } return selection; } private static IEnumerable GetSelectableHydraulicBoundaryLocations( IEnumerable hydraulicBoundaryLocations, RingtoetsPipingSurfaceLine surfaceLine) { Point2D referencePoint = surfaceLine?.ReferenceLineIntersectionWorldPoint; return SelectableHydraulicBoundaryLocationHelper.GetSortedSelectableHydraulicBoundaryLocations( hydraulicBoundaryLocations, referencePoint); } #region Data sources private void UpdateDataGridViewDataSource() { // Skip changes coming from the view itself if (dataGridViewControl.IsCurrentCellInEditMode) { updatingDataSource = true; UpdateStochasticSoilProfileColumn(); updatingDataSource = false; dataGridViewControl.AutoResizeColumns(); return; } var failureMechanismSection = listBox.SelectedItem as FailureMechanismSection; if (failureMechanismSection == null || calculationGroup == null) { dataGridViewControl.SetDataSource(null); return; } IEnumerable lineSegments = Math2D.ConvertLinePointsToLineSegments(failureMechanismSection.Points); IEnumerable pipingCalculations = calculationGroup .GetCalculations() .OfType() .Where(pc => pc.IsSurfaceLineIntersectionWithReferenceLineInSection(lineSegments)); updatingDataSource = true; PrefillComboBoxListItemsAtColumnLevel(); List dataSource = pipingCalculations.Select(pc => new PipingCalculationRow(pc, new ObservablePropertyChangeHandler(pc, pc.InputParameters))).ToList(); dataGridViewControl.SetDataSource(dataSource); dataGridViewControl.ClearCurrentCell(); UpdateStochasticSoilModelColumn(); UpdateStochasticSoilProfileColumn(); UpdateSelectableHydraulicBoundaryLocationsColumn(); updatingDataSource = false; } private static IEnumerable> GetPrefillStochasticSoilModelsDataSource(IEnumerable stochasticSoilModels) { yield return new DataGridViewComboBoxItemWrapper(null); foreach (StochasticSoilModel stochasticSoilModel in stochasticSoilModels) { yield return new DataGridViewComboBoxItemWrapper(stochasticSoilModel); } } private static IEnumerable> GetStochasticSoilModelsDataSource(IEnumerable stochasticSoilModels) { StochasticSoilModel[] stochasticSoilModelsArray = stochasticSoilModels.ToArray(); if (stochasticSoilModelsArray.Length != 1) { yield return new DataGridViewComboBoxItemWrapper(null); } foreach (StochasticSoilModel stochasticSoilModel in stochasticSoilModelsArray) { yield return new DataGridViewComboBoxItemWrapper(stochasticSoilModel); } } private static IEnumerable> GetPrefillSoilProfilesDataSource(IEnumerable stochasticSoilProfiles) { yield return new DataGridViewComboBoxItemWrapper(null); foreach (StochasticSoilProfile stochasticSoilProfile in stochasticSoilProfiles) { yield return new DataGridViewComboBoxItemWrapper(stochasticSoilProfile); } } private static IEnumerable> GetSoilProfilesDataSource(IEnumerable stochasticSoilProfiles) { StochasticSoilProfile[] stochasticSoilProfilesArray = stochasticSoilProfiles.ToArray(); if (stochasticSoilProfilesArray.Length != 1) { yield return new DataGridViewComboBoxItemWrapper(null); } foreach (StochasticSoilProfile stochasticSoilProfile in stochasticSoilProfilesArray) { yield return new DataGridViewComboBoxItemWrapper(stochasticSoilProfile); } } private static List> GetSelectableHydraulicBoundaryLocationsDataSource(IEnumerable selectableHydraulicBoundaryLocations = null) { var dataGridViewComboBoxItemWrappers = new List> { new DataGridViewComboBoxItemWrapper(null) }; if (selectableHydraulicBoundaryLocations != null) { dataGridViewComboBoxItemWrappers.AddRange(selectableHydraulicBoundaryLocations.Select(hbl => new DataGridViewComboBoxItemWrapper(hbl))); } return dataGridViewComboBoxItemWrappers; } #endregion #region Update combo box list items #region Update Selectable Hydraulic Boundary Locations Column private void UpdateSelectableHydraulicBoundaryLocationsColumn() { var column = (DataGridViewComboBoxColumn) dataGridViewControl.GetColumnFromIndex(selectableHydraulicBoundaryLocationColumnIndex); using (new SuspendDataGridViewColumnResizes(column)) { foreach (DataGridViewRow dataGridViewRow in dataGridViewControl.Rows) { FillAvailableSelectableHydraulicBoundaryLocationsList(dataGridViewRow); } } } private void FillAvailableSelectableHydraulicBoundaryLocationsList(DataGridViewRow dataGridViewRow) { var rowData = (PipingCalculationRow) dataGridViewRow.DataBoundItem; IEnumerable locations = GetSelectableHydraulicBoundaryLocationsForCalculation(rowData.PipingCalculation); var cell = (DataGridViewComboBoxCell) dataGridViewRow.Cells[selectableHydraulicBoundaryLocationColumnIndex]; DataGridViewComboBoxItemWrapper[] dataGridViewComboBoxItemWrappers = GetSelectableHydraulicBoundaryLocationsDataSource(locations).ToArray(); SetItemsOnObjectCollection(cell.Items, dataGridViewComboBoxItemWrappers); } private IEnumerable GetSelectableHydraulicBoundaryLocationsForCalculation(PipingCalculation pipingCalculation) { if (assessmentSection?.HydraulicBoundaryDatabase == null || pipingCalculation.InputParameters.UseAssessmentLevelManualInput) { return Enumerable.Empty(); } return GetSelectableHydraulicBoundaryLocations(assessmentSection.HydraulicBoundaryDatabase.Locations, pipingCalculation.InputParameters.SurfaceLine); } #endregion #region Update Stochastic Soil Model Column private void UpdateStochasticSoilModelColumn() { using (new SuspendDataGridViewColumnResizes(dataGridViewControl.GetColumnFromIndex(stochasticSoilModelColumnIndex))) { foreach (DataGridViewRow dataGridViewRow in dataGridViewControl.Rows) { FillAvailableSoilModelsList(dataGridViewRow); } } } private void FillAvailableSoilModelsList(DataGridViewRow dataGridViewRow) { var rowData = (PipingCalculationRow) dataGridViewRow.DataBoundItem; IEnumerable stochasticSoilModels = GetSoilModelsForCalculation(rowData.PipingCalculation); var cell = (DataGridViewComboBoxCell) dataGridViewRow.Cells[stochasticSoilModelColumnIndex]; SetItemsOnObjectCollection(cell.Items, GetStochasticSoilModelsDataSource(stochasticSoilModels).ToArray()); } private IEnumerable GetSoilModelsForCalculation(PipingCalculation pipingCalculation) { if (macroStabilityInwardsFailureMechanism == null) { return Enumerable.Empty(); } return PipingCalculationConfigurationHelper.GetStochasticSoilModelsForSurfaceLine( pipingCalculation.InputParameters.SurfaceLine, macroStabilityInwardsFailureMechanism.StochasticSoilModels); } #endregion #region Update Stochastic Soil Profile Column private void UpdateStochasticSoilProfileColumn() { using (new SuspendDataGridViewColumnResizes(dataGridViewControl.GetColumnFromIndex(stochasticSoilProfileColumnIndex))) { foreach (DataGridViewRow dataGridViewRow in dataGridViewControl.Rows) { FillAvailableSoilProfilesList(dataGridViewRow); } } } private void FillAvailableSoilProfilesList(DataGridViewRow dataGridViewRow) { var rowData = (PipingCalculationRow) dataGridViewRow.DataBoundItem; PipingInputService.SyncStochasticSoilProfileWithStochasticSoilModel(rowData.PipingCalculation.InputParameters); IEnumerable stochasticSoilProfiles = GetSoilProfilesForCalculation(rowData.PipingCalculation); var cell = (DataGridViewComboBoxCell) dataGridViewRow.Cells[stochasticSoilProfileColumnIndex]; SetItemsOnObjectCollection(cell.Items, GetSoilProfilesDataSource(stochasticSoilProfiles).ToArray()); } private IEnumerable GetSoilProfilesForCalculation(PipingCalculation pipingCalculation) { if (pipingCalculation.InputParameters.StochasticSoilModel == null) { return Enumerable.Empty(); } return pipingCalculation.InputParameters.StochasticSoilModel.StochasticSoilProfiles; } #endregion #endregion #region Prefill combo box list items private void PrefillComboBoxListItemsAtColumnLevel() { var stochasticSoilModelColumn = (DataGridViewComboBoxColumn) dataGridViewControl.GetColumnFromIndex(stochasticSoilModelColumnIndex); var stochasticSoilProfileColumn = (DataGridViewComboBoxColumn) dataGridViewControl.GetColumnFromIndex(stochasticSoilProfileColumnIndex); var selectableHydraulicBoundaryLocationColumn = (DataGridViewComboBoxColumn) dataGridViewControl.GetColumnFromIndex(selectableHydraulicBoundaryLocationColumnIndex); // Need to prefill for all possible data in order to guarantee 'combo box' columns // do not generate errors when their cell value is not present in the list of available // items. using (new SuspendDataGridViewColumnResizes(stochasticSoilModelColumn)) { StochasticSoilModelCollection stochasticSoilModels = macroStabilityInwardsFailureMechanism.StochasticSoilModels; SetItemsOnObjectCollection(stochasticSoilModelColumn.Items, GetPrefillStochasticSoilModelsDataSource(stochasticSoilModels).ToArray()); } using (new SuspendDataGridViewColumnResizes(stochasticSoilProfileColumn)) { StochasticSoilProfile[] pipingSoilProfiles = GetPipingStochasticSoilProfilesFromStochasticSoilModels(); SetItemsOnObjectCollection(stochasticSoilProfileColumn.Items, GetPrefillSoilProfilesDataSource(pipingSoilProfiles).ToArray()); } using (new SuspendDataGridViewColumnResizes(selectableHydraulicBoundaryLocationColumn)) { SetItemsOnObjectCollection(selectableHydraulicBoundaryLocationColumn.Items, GetSelectableHydraulicBoundaryLocationsDataSource(GetSelectableHydraulicBoundaryLocationsFromFailureMechanism()).ToArray()); } } private IEnumerable GetSelectableHydraulicBoundaryLocationsFromFailureMechanism() { if (assessmentSection?.HydraulicBoundaryDatabase == null) { return null; } List hydraulicBoundaryLocations = assessmentSection.HydraulicBoundaryDatabase.Locations; List selectableHydraulicBoundaryLocations = hydraulicBoundaryLocations.Select(hbl => new SelectableHydraulicBoundaryLocation(hbl, null)).ToList(); if (MacroStabilityInwardsFailureMechanism == null || !MacroStabilityInwardsFailureMechanism.SurfaceLines.Any()) { return selectableHydraulicBoundaryLocations; } foreach (RingtoetsPipingSurfaceLine surfaceLine in MacroStabilityInwardsFailureMechanism.SurfaceLines) { selectableHydraulicBoundaryLocations.AddRange(GetSelectableHydraulicBoundaryLocations(hydraulicBoundaryLocations, surfaceLine)); } return selectableHydraulicBoundaryLocations; } private StochasticSoilProfile[] GetPipingStochasticSoilProfilesFromStochasticSoilModels() { return macroStabilityInwardsFailureMechanism?.StochasticSoilModels .SelectMany(ssm => ssm.StochasticSoilProfiles) .Distinct() .ToArray(); } #endregion #region Event handling private void DataGridViewOnCurrentCellChanged(object sender, EventArgs e) { if (!updatingDataSource) { OnSelectionChanged(); } } private void ListBoxOnSelectedValueChanged(object sender, EventArgs e) { UpdateDataGridViewDataSource(); OnSelectionChanged(); } private void OnGenerateScenariosButtonClick(object sender, EventArgs e) { if (calculationGroup == null) { return; } var dialog = new PipingSurfaceLineSelectionDialog(Parent, macroStabilityInwardsFailureMechanism.SurfaceLines); dialog.ShowDialog(); IEnumerable calculationsStructure = PipingCalculationConfigurationHelper.GenerateCalculationItemsStructure( dialog.SelectedItems, macroStabilityInwardsFailureMechanism.StochasticSoilModels, macroStabilityInwardsFailureMechanism.GeneralInput); foreach (ICalculationBase item in calculationsStructure) { calculationGroup.Children.Add(item); } calculationGroup.NotifyObservers(); } private void OnFailureMechanismUpdate() { UpdateGenerateScenariosButtonState(); UpdateSectionsListBox(); } private void OnStochasticSoilModelsUpdate() { UpdateGenerateScenariosButtonState(); UpdateStochasticSoilModelColumn(); UpdateStochasticSoilProfileColumn(); } private void UpdateSectionsListBox() { listBox.Items.Clear(); if (macroStabilityInwardsFailureMechanism != null && macroStabilityInwardsFailureMechanism.Sections.Any()) { listBox.Items.AddRange(macroStabilityInwardsFailureMechanism.Sections.Cast().ToArray()); listBox.SelectedItem = macroStabilityInwardsFailureMechanism.Sections.First(); } } private void OnSelectionChanged() { SelectionChanged?.Invoke(this, new EventArgs()); } private void OnCellFormatting(object sender, DataGridViewCellFormattingEventArgs eventArgs) { if (eventArgs.ColumnIndex == selectableHydraulicBoundaryLocationColumnIndex) { DataGridViewRow dataGridViewRow = dataGridViewControl.GetRowFromIndex(eventArgs.RowIndex); var dataItem = dataGridViewRow.DataBoundItem as PipingCalculationRow; if (dataItem != null && dataItem.PipingCalculation.InputParameters.UseAssessmentLevelManualInput) { dataGridViewRow.ReadOnly = true; } else if (dataGridViewRow.ReadOnly) { dataGridViewRow.ReadOnly = false; } } } #endregion } }