// Copyright (C) Stichting Deltares 2016. 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 Core.Common.Gui.Selection; using Core.Common.Utils.Reflection; using Ringtoets.Common.Data.AssessmentSection; using Ringtoets.Common.Data.Calculation; using Ringtoets.Common.Data.FailureMechanism; using Ringtoets.Common.Forms.Helpers; using Ringtoets.HydraRing.Data; using Ringtoets.Piping.Data; using Ringtoets.Piping.Forms.PresentationObjects; using Ringtoets.Piping.Forms.Properties; using Ringtoets.Piping.Service; using CoreCommonControlsResources = Core.Common.Controls.Properties.Resources; namespace Ringtoets.Piping.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 hydraulicBoundaryLocationColumnIndex = 4; private readonly Observer assessmentSectionObserver; private readonly RecursiveObserver pipingInputObserver; private readonly RecursiveObserver pipingCalculationGroupObserver; private readonly RecursiveObserver pipingCalculationObserver; private readonly Observer pipingFailureMechanismObserver; private readonly Observer pipingStochasticSoilModelsObserver; private IAssessmentSection assessmentSection; private CalculationGroup calculationGroup; private PipingFailureMechanism pipingFailureMechanism; private bool updatingDataSource; /// /// Creates a new instance of the class. /// public PipingCalculationsView() { InitializeComponent(); InitializeDataGridView(); InitializeListBox(); pipingStochasticSoilModelsObserver = new Observer(OnStochasticSoilModelsUpdate); pipingFailureMechanismObserver = new Observer(OnPipingFailureMechanismUpdate); assessmentSectionObserver = new Observer(UpdateHydraulicBoundaryLocationsColumn); // 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); } /// /// Gets or sets the piping failure mechanism. /// public PipingFailureMechanism PipingFailureMechanism { get { return pipingFailureMechanism; } set { pipingFailureMechanism = value; pipingStochasticSoilModelsObserver.Observable = pipingFailureMechanism != null ? pipingFailureMechanism.StochasticSoilModels : null; pipingFailureMechanismObserver.Observable = pipingFailureMechanism; UpdateStochasticSoilModelColumn(); UpdateStochasticSoilProfileColumn(); UpdateSectionsListBox(); UpdateGenerateScenariosButtonState(); } } /// /// Gets or sets the assessment section. /// public IAssessmentSection AssessmentSection { get { return assessmentSection; } set { assessmentSection = value; assessmentSectionObserver.Observable = assessmentSection; UpdateHydraulicBoundaryLocationsColumn(); } } /// /// Gets or sets the . /// public IApplicationSelection ApplicationSelection { get; set; } 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) { assessmentSectionObserver.Dispose(); pipingFailureMechanismObserver.Dispose(); pipingInputObserver.Dispose(); pipingCalculationObserver.Dispose(); pipingCalculationGroupObserver.Dispose(); pipingStochasticSoilModelsObserver.Dispose(); if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } private void InitializeDataGridView() { dataGridViewControl.AddCellClickHandler(DataGridViewOnCellClick); dataGridViewControl.AddTextBoxColumn( TypeUtils.GetMemberName(row => row.Name), Resources.PipingCalculation_Name_DisplayName ); dataGridViewControl.AddComboBoxColumn>( TypeUtils.GetMemberName(row => row.StochasticSoilModel), Resources.PipingInput_StochasticSoilModel_DisplayName, null, TypeUtils.GetMemberName>(wrapper => wrapper.This), TypeUtils.GetMemberName>(wrapper => wrapper.DisplayName) ); dataGridViewControl.AddComboBoxColumn>( TypeUtils.GetMemberName(row => row.StochasticSoilProfile), Resources.PipingInput_StochasticSoilProfile_DisplayName, null, TypeUtils.GetMemberName>(wrapper => wrapper.This), TypeUtils.GetMemberName>(wrapper => wrapper.DisplayName) ); dataGridViewControl.AddTextBoxColumn( TypeUtils.GetMemberName(row => row.StochasticSoilProfileProbability), Resources.PipingCalculationsView_InitializeDataGridView_Stochastic_soil_profile_probability ); dataGridViewControl.AddComboBoxColumn>( TypeUtils.GetMemberName(row => row.HydraulicBoundaryLocation), Resources.PipingInput_HydraulicBoundaryLocation_DisplayName, null, TypeUtils.GetMemberName>(wrapper => wrapper.This), TypeUtils.GetMemberName>(wrapper => wrapper.DisplayName) ); var dampingFactorExitHeader = Resources.PipingInput_DampingFactorExit_DisplayName; dampingFactorExitHeader = char.ToLowerInvariant(dampingFactorExitHeader[0]) + dampingFactorExitHeader.Substring(1); dataGridViewControl.AddTextBoxColumn( TypeUtils.GetMemberName(row => row.DampingFactorExitMean), string.Format("{0} {1}", Resources.Probabilistics_Mean_Symbol, dampingFactorExitHeader) ); var phreaticLevelExitHeader = Resources.PipingInput_PhreaticLevelExit_DisplayName; phreaticLevelExitHeader = char.ToLowerInvariant(phreaticLevelExitHeader[0]) + phreaticLevelExitHeader.Substring(1); dataGridViewControl.AddTextBoxColumn( TypeUtils.GetMemberName(row => row.PhreaticLevelExitMean), string.Format("{0} {1}", Resources.Probabilistics_Mean_Symbol, phreaticLevelExitHeader) ); dataGridViewControl.AddTextBoxColumn( TypeUtils.GetMemberName(pcs => pcs.EntryPointL), Resources.PipingInput_EntryPointL_DisplayName ); dataGridViewControl.AddTextBoxColumn( TypeUtils.GetMemberName(pcs => pcs.ExitPointL), Resources.PipingInput_ExitPointL_DisplayName ); UpdateHydraulicBoundaryLocationsColumn(); UpdateStochasticSoilModelColumn(); UpdateStochasticSoilProfileColumn(); } private void InitializeListBox() { listBox.DisplayMember = TypeUtils.GetMemberName(s => s.Name); listBox.SelectedValueChanged += ListBoxOnSelectedValueChanged; } private void UpdateHydraulicBoundaryLocationsColumn() { DataGridViewComboBoxColumn hydraulicBoundaryLocationColumn = (DataGridViewComboBoxColumn) dataGridViewControl.GetColumnFromIndex(hydraulicBoundaryLocationColumnIndex); using (new SuspendDataGridViewColumnResizes(hydraulicBoundaryLocationColumn)) { var hydraulicBoundaryLocations = assessmentSection != null && assessmentSection.HydraulicBoundaryDatabase != null ? assessmentSection.HydraulicBoundaryDatabase.Locations : null; SetItemsOnObjectCollection(hydraulicBoundaryLocationColumn.Items, GetHydraulicBoundaryLocationsDataSource(hydraulicBoundaryLocations).ToArray()); } } private void UpdateStochasticSoilModelColumn() { using (new SuspendDataGridViewColumnResizes(dataGridViewControl.GetColumnFromIndex(stochasticSoilModelColumnIndex))) { foreach (DataGridViewRow dataGridViewRow in dataGridViewControl.Rows) { FillAvailableSoilModelsList(dataGridViewRow); } } } private void UpdateStochasticSoilProfileColumn() { using (new SuspendDataGridViewColumnResizes(dataGridViewControl.GetColumnFromIndex(stochasticSoilProfileColumnIndex))) { foreach (DataGridViewRow dataGridViewRow in dataGridViewControl.Rows) { FillAvailableSoilProfilesList(dataGridViewRow); } } } private void UpdateGenerateScenariosButtonState() { buttonGenerateScenarios.Enabled = pipingFailureMechanism != null && pipingFailureMechanism.SurfaceLines.Any() && pipingFailureMechanism.StochasticSoilModels.Any(); } 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 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 GetSoilModelsForCalculation(PipingCalculation pipingCalculation) { if (pipingFailureMechanism == null) { return Enumerable.Empty(); } return PipingCalculationConfigurationHelper.GetStochasticSoilModelsForSurfaceLine( pipingCalculation.InputParameters.SurfaceLine, pipingFailureMechanism.StochasticSoilModels); } private IEnumerable GetSoilProfilesForCalculation(PipingCalculation pipingCalculation) { if (pipingCalculation.InputParameters.StochasticSoilModel == null) { return Enumerable.Empty(); } return pipingCalculation.InputParameters.StochasticSoilModel.StochasticSoilProfiles; } private static void SetItemsOnObjectCollection(DataGridViewComboBoxCell.ObjectCollection objectCollection, object[] comboBoxItems) { objectCollection.Clear(); objectCollection.AddRange(comboBoxItems); } #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; } var lineSegments = Math2D.ConvertLinePointsToLineSegments(failureMechanismSection.Points); var pipingCalculations = calculationGroup .GetCalculations() .OfType() .Where(pc => pc.IsSurfaceLineIntersectionWithReferenceLineInSection(lineSegments)); updatingDataSource = true; PrefillComboBoxListItemsAtColumnLevel(); var dataSource = pipingCalculations.Select(pc => new PipingCalculationRow(pc)).ToList(); dataGridViewControl.SetDataSource(dataSource); dataGridViewControl.ClearCurrentCell(); UpdateStochasticSoilModelColumn(); UpdateStochasticSoilProfileColumn(); 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) { var 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) { var 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> GetHydraulicBoundaryLocationsDataSource(IEnumerable hydraulicBoundaryLocations = null) { var dataGridViewComboBoxItemWrappers = new List> { new DataGridViewComboBoxItemWrapper(null) }; if (hydraulicBoundaryLocations != null) { dataGridViewComboBoxItemWrappers.AddRange(hydraulicBoundaryLocations.Select(hbl => new DataGridViewComboBoxItemWrapper(hbl))); } return dataGridViewComboBoxItemWrappers; } #endregion #region Prefill combo box list items private void PrefillComboBoxListItemsAtColumnLevel() { DataGridViewComboBoxColumn stochasticSoilModelColumn = (DataGridViewComboBoxColumn) dataGridViewControl.GetColumnFromIndex(stochasticSoilModelColumnIndex); DataGridViewComboBoxColumn stochasticSoilProfileColumn = (DataGridViewComboBoxColumn) dataGridViewControl.GetColumnFromIndex(stochasticSoilProfileColumnIndex); DataGridViewComboBoxColumn hydraulicBoundaryLocationColumn = (DataGridViewComboBoxColumn) dataGridViewControl.GetColumnFromIndex(hydraulicBoundaryLocationColumnIndex); // 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)) { var stochasticSoilModels = pipingFailureMechanism.StochasticSoilModels; SetItemsOnObjectCollection(stochasticSoilModelColumn.Items, GetPrefillStochasticSoilModelsDataSource(stochasticSoilModels).ToArray()); } using (new SuspendDataGridViewColumnResizes(stochasticSoilProfileColumn)) { var pipingSoilProfiles = GetPipingStochasticSoilProfilesFromStochasticSoilModels(); SetItemsOnObjectCollection(stochasticSoilProfileColumn.Items, GetPrefillSoilProfilesDataSource(pipingSoilProfiles).ToArray()); } using (new SuspendDataGridViewColumnResizes(hydraulicBoundaryLocationColumn)) { var hydraulicBoundaryLocations = assessmentSection != null && assessmentSection.HydraulicBoundaryDatabase != null ? assessmentSection.HydraulicBoundaryDatabase.Locations : null; SetItemsOnObjectCollection( hydraulicBoundaryLocationColumn.Items, GetHydraulicBoundaryLocationsDataSource(hydraulicBoundaryLocations).ToArray()); } } private StochasticSoilProfile[] GetPipingStochasticSoilProfilesFromStochasticSoilModels() { return pipingFailureMechanism != null ? pipingFailureMechanism.StochasticSoilModels .SelectMany(ssm => ssm.StochasticSoilProfiles) .Distinct() .ToArray() : null; } #endregion # region Event handling private void DataGridViewOnCellClick(object sender, DataGridViewCellEventArgs e) { if (updatingDataSource) { return; } UpdateApplicationSelection(); } private void ListBoxOnSelectedValueChanged(object sender, EventArgs e) { UpdateDataGridViewDataSource(); } private void OnGenerateScenariosButtonClick(object sender, EventArgs e) { if (calculationGroup == null) { return; } var dialog = new PipingSurfaceLineSelectionDialog(Parent, pipingFailureMechanism.SurfaceLines); dialog.ShowDialog(); var calculationsStructure = PipingCalculationConfigurationHelper.GenerateCalculationItemsStructure( dialog.SelectedItems, pipingFailureMechanism.StochasticSoilModels, pipingFailureMechanism.GeneralInput); foreach (var item in calculationsStructure) { calculationGroup.Children.Add(item); } calculationGroup.NotifyObservers(); } private void OnPipingFailureMechanismUpdate() { UpdateGenerateScenariosButtonState(); UpdateSectionsListBox(); } private void OnStochasticSoilModelsUpdate() { UpdateGenerateScenariosButtonState(); UpdateStochasticSoilModelColumn(); UpdateStochasticSoilProfileColumn(); } private void UpdateSectionsListBox() { listBox.Items.Clear(); if (pipingFailureMechanism != null && pipingFailureMechanism.Sections.Any()) { listBox.Items.AddRange(pipingFailureMechanism.Sections.Cast().ToArray()); listBox.SelectedItem = pipingFailureMechanism.Sections.First(); } } private void UpdateApplicationSelection() { if (ApplicationSelection == null) { return; } PipingInputContext selection = CreateSelectedItemFromCurrentRow(); if ((ApplicationSelection.Selection == null && selection != null) || (ApplicationSelection.Selection != null && !ApplicationSelection.Selection.Equals(selection))) { ApplicationSelection.Selection = selection; } } private PipingInputContext CreateSelectedItemFromCurrentRow() { var currentRow = dataGridViewControl.CurrentRow; var pipingCalculationRow = currentRow != null ? (PipingCalculationRow) currentRow.DataBoundItem : null; PipingInputContext selection = null; if (pipingCalculationRow != null) { selection = new PipingInputContext( pipingCalculationRow.PipingCalculation.InputParameters, pipingCalculationRow.PipingCalculation, pipingFailureMechanism.SurfaceLines, pipingFailureMechanism.StochasticSoilModels, pipingFailureMechanism, assessmentSection); } return selection; } # endregion } }