| | |
| | | using System.Data; |
| | | using DevExpress.Spreadsheet; |
| | | using Yw.Geometry; |
| | | using Yw.Pump; |
| | | using Yw.WinFrmUI.Phart; |
| | | |
| | | namespace HStation.WinFrmUI |
| | | { |
| | |
| | | public PumpChartViewCtrl() |
| | | { |
| | | InitializeComponent(); |
| | | this.barCekEqVisible.Checked = true; |
| | | this.barCekEqulEffVisible.Checked = true; |
| | | this.bar2.Visible = false; |
| | | this.barCekEqulEffVisible.Visibility = DevExpress.XtraBars.BarItemVisibility.Never; |
| | | } |
| | | |
| | | private Yw.Vmo.PhartDiagramExGraphListVmo _vmo = null; |
| | | private Yw.WinFrmUI.Phart.PumpPerformCurveViewModel _model = null; |
| | | private Yw.WinFrmUI.Phart.PumpCurveViewModel _vm = null; |
| | | |
| | | private bool _default_visible = true; |
| | | private bool _power_visible = true; |
| | | private bool _eff_visible = true; |
| | | private bool _equal_eff_visible = true; |
| | | private bool _split_panel = true; |
| | | private double _nr; |
| | | |
| | | /// <summary> |
| | | /// 数据变更事件 |
| | | /// </summary> |
| | | public event Action<Yw.Vmo.PhartDiagramExGraphListVmo> DataChangedEvent; |
| | | |
| | | /// <summary> |
| | | /// 绑定数据 |
| | | /// </summary> |
| | | public void SetBindingData(Yw.Vmo.PhartDiagramExGraphListVmo vmo, double nr) |
| | | { |
| | | var vm_list = vmo.GetPumpCurveViewMdoelList(); |
| | | if (vm_list==null||!vm_list.Any()) |
| | | _vmo = vmo; |
| | | _nr = nr; |
| | | if (vmo == null) |
| | | { |
| | | this.pumpChart1.Clear(); |
| | | ClearBindingData(); |
| | | return; |
| | | } |
| | | var vm_defult = vm_list.OrderBy(x => x.Hz).LastOrDefault(); |
| | | var vm_ee_list = vmo.GetCurveEqualEViewMdoelList(); |
| | | var vm_list = vmo.GetPumpCurveViewMdoelList(); |
| | | if (vm_list == null || !vm_list.Any()) |
| | | { |
| | | ClearBindingData(); |
| | | return; |
| | | } |
| | | _model = vmo.GetDefaultPumpCurveViewMdoel(); |
| | | if (_model == null) |
| | | return; |
| | | |
| | | var vm = new Yw.WinFrmUI.Phart.PumpViewChartViewModel(); |
| | | var vm_equal_eff_list = vmo.GetCurveEqualEViewMdoelList(); |
| | | var vm_variable_speed_eff_list = vmo.GetVariableSpeedPumpCurveViewMdoelList(); |
| | | |
| | | var vm = new Yw.WinFrmUI.Phart.PumpCurveViewModel(); |
| | | vm.Id = ""; |
| | | vm.ColorQH = Color.Blue; |
| | | vm.ColorQE = Color.Green; |
| | | vm.ColorQP = Color.Red; |
| | | vm.CurveQHName = "QH"; |
| | | vm.CurveQPName = "QP"; |
| | | vm.CurveQH = vm_defult.CurveQH.FeatCurve.GetPointList(50); |
| | | vm.CurveQE = vm_defult.CurveQE?.FeatCurve.GetPointList(50); |
| | | vm.CurveQP = vm_defult.CurveQP?.FeatCurve.GetPointList(50); |
| | | vm.CurveQHName = $"{_nr}rpm({50}hz)"; |
| | | vm.CurveQPName = $"{_nr}rpm({50}hz)"; |
| | | vm.CurveQH = _model.CurveQH.FeatCurve.GetPointList(50); |
| | | vm.CurveQE = _model.CurveQE?.FeatCurve.GetPointList(50); |
| | | vm.CurveQP = _model.CurveQP?.FeatCurve.GetPointList(50); |
| | | vm.PointListQH = vm.CurveQH; |
| | | vm.PointListQE = vm.CurveQE; |
| | | vm.PointListQP = vm.CurveQP; |
| | | vm.Equip = null; |
| | | //vm.VariableSpeedList = new List<Yw.WinFrmUI.Phart.PumpChartVariableSpeedViewModel>(); |
| | | //vm.EqualEffList = new List<Yw.WinFrmUI.Phart.PumpChartEqualEffViewModel>(); |
| | | //vm.EqualPowerList = new List<Yw.WinFrmUI.Phart.PumpChartEqualPowerViewModel>(); |
| | | if (vm_equal_eff_list != null && vm_equal_eff_list.Any()) |
| | | { |
| | | vm.EqualEffList = new(); |
| | | foreach (var item in vm_equal_eff_list) |
| | | { |
| | | var eff_vm = new Yw.WinFrmUI.Phart.PumpCurveEqualEffViewModel(); |
| | | eff_vm.Id = Guid.NewGuid().ToString(); |
| | | eff_vm.CurveName = $"{item.Eff:N1}"; |
| | | eff_vm.Color = Color.DarkGreen; |
| | | eff_vm.CurveEqualEff = item.DefinePoints; |
| | | eff_vm.Tension = item.Tension; |
| | | eff_vm.IsClosed = item.IsClosed; |
| | | vm.EqualEffList.Add(eff_vm); |
| | | } |
| | | } |
| | | |
| | | //for (int eff = 30; eff < 90; eff += 1) |
| | | //{ |
| | | // var ve = new Yw.WinFrmUI.Phart.PumpChartEqualEffViewModel(); |
| | | // //ve.Id = rhs.Id; |
| | | // ve.CurveName = $"{eff}"; |
| | | // ve.Color = Color.ForestGreen; |
| | | // ve.CurveEqualEff = new List<Yw.Geometry.Point2d>(); |
| | | // for (int hz = 30; hz <= 50; hz += 5) |
| | | // { |
| | | // var qe = vm_defult.CurveQE.ToNewByN(50, hz); |
| | | // var q_array = qe.FeatCurve.GetPointsX(eff); |
| | | // if (q_array == null || !q_array.Any()) |
| | | // { |
| | | // continue; |
| | | // } |
| | | // var qh = vm_defult.CurveQH.ToNewByN(50, hz); |
| | | // var flow = q_array.FirstOrDefault(); |
| | | // var head = qh.FeatCurve.GetPointY(flow); |
| | | // ve.CurveEqualEff.Add(new Yw.Geometry.Point2d(flow, head)); |
| | | // } |
| | | // if (!ve.CurveEqualEff.Any()) |
| | | // { |
| | | // continue; |
| | | // } |
| | | if (vm_variable_speed_eff_list != null && vm_variable_speed_eff_list.Any()) |
| | | { |
| | | vm.VariableSpeedList = new(); |
| | | var index = 0; |
| | | foreach (var item in vm_variable_speed_eff_list) |
| | | { |
| | | var vs_color = Yw.WinFrmUI.PhartColorHelper.Get(index); |
| | | var vs_vm = new Yw.WinFrmUI.Phart.PumpCurveVariableSpeedViewModel(); |
| | | vs_vm.Id = Guid.NewGuid().ToString(); |
| | | vs_vm.ColorQH = vs_color; |
| | | vs_vm.ColorQE = vs_color; |
| | | vs_vm.ColorQP = vs_color; |
| | | vs_vm.CurveQHName = $"{item.N}rpm({item.Hz}hz)"; |
| | | vs_vm.CurveQPName = $"{item.N}rpm({item.Hz}hz)"; |
| | | vs_vm.CurveQH = item.CurveQH.FeatCurve.GetPointList(30); |
| | | vs_vm.CurveQE = item.CurveQE.FeatCurve.GetPointList(30); |
| | | vs_vm.CurveQP = item.CurveQP.FeatCurve.GetPointList(30); |
| | | vs_vm.N = item.N; |
| | | vs_vm.Hz = item.Hz; |
| | | vm.VariableSpeedList.Add(vs_vm); |
| | | index++; |
| | | } |
| | | } |
| | | |
| | | // vm.EqualEffList.Add(ve); |
| | | //} |
| | | vm.VariableSpeedList = new List<PumpCurveVariableSpeedViewModel>(); |
| | | vm.EqualEffList = new List<PumpCurveEqualEffViewModel>(); |
| | | vm.EqualPowerList = new(); |
| | | |
| | | //for (int hz = 30; hz < 50; hz += 5) |
| | | //{ |
| | | // var tN = nr * hz / 50; |
| | | // var vs = new Yw.WinFrmUI.Phart.PumpChartVariableSpeedViewModel(); |
| | | // vs.CurveQHName = $"{tN}"; |
| | | // vs.CurveQPName = $"{tN}"; |
| | | // vs.CurveQH = vm_defult.CurveQH.GetPointListByN(nr, tN, 50); |
| | | // vs.CurveQE = vm_defult.CurveQE.GetPointListByN(nr, tN, 50); |
| | | // vs.CurveQP = vm_defult.CurveQP.GetPointListByN(nr, tN, 50); |
| | | // vs.Hz = hz; |
| | | // vs.N = tN; |
| | | // vm.VariableSpeedList.Add(vs); |
| | | //} |
| | | |
| | | this.pumpChart1.SetBindingData(vm); |
| | | SetBindingData(vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 绑定数据 |
| | | /// </summary> |
| | | public void SetBindingData(Yw.WinFrmUI.Phart.PumpCurveViewModel vm, bool default_visible, bool split_panel, bool eff_visible, bool power_visible, bool equal_eff_visible) |
| | | { |
| | | _vm = vm; |
| | | if (vm == null) |
| | | { |
| | | ClearBindingData(); |
| | | return; |
| | | } |
| | | if (vm.VariableSpeedList != null && vm.VariableSpeedList.Any()) |
| | | { |
| | | vm.ColorQH = vm.ColorQE = vm.ColorQP = Color.Black; |
| | | } |
| | | this.pumpViewChart1.SetBindingData(vm, default_visible, split_panel, eff_visible, power_visible, equal_eff_visible); |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 清空绑定数据 |
| | | /// </summary> |
| | | public void ClearBindingData() |
| | | { |
| | | this.pumpChart1.Clear(); |
| | | this.pumpViewChart1.ClearBindingData(); |
| | | } |
| | | |
| | | |
| | | private void SetEquip() |
| | | { |
| | | if (_vm == null) |
| | | return; |
| | | if (!_vm.IsValid()) |
| | | return; |
| | | if (this.barTxtStartHead.EditValue == null) |
| | | return; |
| | | if (this.barTxtPipeHead.EditValue == null) |
| | | return; |
| | | if (this.barTxtPipeQ.EditValue == null) |
| | | return; |
| | | |
| | | var start_head = Convert.ToDouble(this.barTxtStartHead.EditValue); |
| | | var pipe_head = Convert.ToDouble(this.barTxtPipeHead.EditValue); |
| | | var pipe_flow = Convert.ToDouble(this.barTxtPipeQ.EditValue); |
| | | |
| | | var equip_pt = new Yw.Geometry.Point2d(pipe_flow, pipe_head); |
| | | var qh_line = new Yw.Ahart.CurveQH(Yw.Ahart.eFeatType.Cubic, _vm.CurveQH); |
| | | var equip_line = Yw.Pump.PerformParabolaHelper.GetEquipCurvePointListByQH(qh_line, equip_pt, start_head, 30, true, out Yw.Geometry.Point2d sect_pt); |
| | | if (equip_line == null || sect_pt == null) |
| | | { |
| | | XtraMessageBox.Show("计算失败,设计点不合理!"); |
| | | return; |
| | | } |
| | | |
| | | _vm.Equip = new Yw.WinFrmUI.Phart.PumpCurveEquipViewModel(); |
| | | _vm.Equip.Id = Guid.NewGuid().ToString(); |
| | | _vm.Equip.Color = Color.Black; |
| | | _vm.Equip.StartH = start_head; |
| | | _vm.Equip.PipeQ = pipe_flow; |
| | | _vm.Equip.PipeH = pipe_head; |
| | | |
| | | _vm.Equip.SectQ = sect_pt.X; |
| | | _vm.Equip.SectH = sect_pt.Y; |
| | | |
| | | if (_vm.CurveQP != null && _vm.CurveQP.Any()) |
| | | { |
| | | _vm.Equip.SectP = _vm.CurveQP.GetInterPointsY(sect_pt.X)?.FirstOrDefault(); |
| | | } |
| | | |
| | | if (_vm.CurveQE != null && _vm.CurveQE.Any()) |
| | | { |
| | | _vm.Equip.SectE = _vm.CurveQE.GetInterPointsY(sect_pt.X)?.FirstOrDefault(); |
| | | } |
| | | |
| | | _vm.Equip.CurveEquip = equip_line.GetPointList(Yw.Ahart.eFeatType.Cubic); |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | } |
| | | |
| | | private void barCekEffVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | _eff_visible = this.barCekEffVisible.Checked; |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | } |
| | | |
| | | private void barCekPowerVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | _power_visible = this.barCekPowerVisible.Checked; |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | } |
| | | |
| | | private void barCekSplitPanel_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | _split_panel = this.barCekSplitPanel.Checked; |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | } |
| | | |
| | | private void barCekEqVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | if (this.barCekEqVisible.Checked) |
| | | { |
| | | SetEquip(); |
| | | } |
| | | else |
| | | { |
| | | _vm.Equip = null; |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | } |
| | | } |
| | | |
| | | private void barCekEqulEffVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | _equal_eff_visible = this.barCekEqulEffVisible.Checked; |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | } |
| | | |
| | | private void barBtnEqClear_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | this.barTxtStartHead.EditValue = 0; |
| | | this.barTxtPipeQ.EditValue = null; |
| | | this.barTxtPipeHead.EditValue = null; |
| | | |
| | | _vm.Equip = null; |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | } |
| | | |
| | | private void barBtnSetEqPt_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | SetEquip(); |
| | | } |
| | | |
| | | private void barBtnAddVariableSpeedByN_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | AddByN(); |
| | | } |
| | | |
| | | private void barBtnAddVariableSpeedByHz_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | AddByHz(); |
| | | } |
| | | |
| | | private void barBtnAddVariableSpeedByPoint_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | AddByPoint(); |
| | | } |
| | | |
| | | private void barBtnAddEqualEffByEff_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) |
| | | { |
| | | AddByEff(); |
| | | } |
| | | |
| | | private void AddByN() |
| | | { |
| | | if (_vm == null) |
| | | return; |
| | | var dlg = new SetValueDlg(); |
| | | dlg.SetBindingData(_nr); |
| | | dlg.VerifyValueChanged += (speed) => |
| | | { |
| | | var hz = Math.Round(speed / _nr * 50, 1); |
| | | if (hz > 50 || hz < 10) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | var index = _vm.VariableSpeedList?.Count + 1 ?? 1; |
| | | var vs_color = Yw.WinFrmUI.PhartColorHelper.Get(index); |
| | | var vs_vm = new Yw.WinFrmUI.Phart.PumpCurveVariableSpeedViewModel(); |
| | | vs_vm.Id = Guid.NewGuid().ToString(); |
| | | vs_vm.ColorQH = vs_color; |
| | | vs_vm.ColorQE = vs_color; |
| | | vs_vm.ColorQP = vs_color; |
| | | vs_vm.CurveQHName = $"{speed}rpm({hz}hz)"; |
| | | vs_vm.CurveQPName = $"{speed}rpm({hz}hz)"; |
| | | vs_vm.CurveQH = _vm.CurveQH.GetQHPointListByN(50, hz); |
| | | vs_vm.CurveQE = _vm.CurveQE.GetQEPointListByN(50, hz); |
| | | vs_vm.CurveQP = _vm.CurveQP.GetQEPointListByN(50, hz); |
| | | vs_vm.N = speed; |
| | | vs_vm.Hz = hz; |
| | | _vm.VariableSpeedList.Add(vs_vm); |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | |
| | | //this.DataChangedEvent?.Invoke(); |
| | | return true; |
| | | }; |
| | | dlg.ShowDialog(); |
| | | } |
| | | |
| | | private void AddByHz() |
| | | { |
| | | if (_vm == null) |
| | | return; |
| | | var dlg = new SetValueDlg(); |
| | | dlg.VerifyValueChanged += (hz) => |
| | | { |
| | | if (hz > 50 || hz < 10) |
| | | { |
| | | return false; |
| | | } |
| | | var speed = Math.Round(hz / 50 * _nr); |
| | | var index = _vm.VariableSpeedList?.Count + 1 ?? 1; |
| | | var vs_color = Yw.WinFrmUI.PhartColorHelper.Get(index); |
| | | var vs_vm = new Yw.WinFrmUI.Phart.PumpCurveVariableSpeedViewModel(); |
| | | vs_vm.Id = Guid.NewGuid().ToString(); |
| | | vs_vm.ColorQH = vs_color; |
| | | vs_vm.ColorQE = vs_color; |
| | | vs_vm.ColorQP = vs_color; |
| | | vs_vm.CurveQHName = $"{speed}rpm({hz}hz)"; |
| | | vs_vm.CurveQPName = $"{speed}rpm({hz}hz)"; |
| | | vs_vm.CurveQH = _vm.CurveQH.GetQHPointListByN(50, hz); |
| | | vs_vm.CurveQE = _vm.CurveQE.GetQEPointListByN(50, hz); |
| | | vs_vm.CurveQP = _vm.CurveQP.GetQEPointListByN(50, hz); |
| | | vs_vm.N = speed; |
| | | vs_vm.Hz = hz; |
| | | _vm.VariableSpeedList.Add(vs_vm); |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | |
| | | return true; |
| | | }; |
| | | dlg.ShowDialog(); |
| | | } |
| | | |
| | | private void AddByPoint() |
| | | { |
| | | if (_vm == null) |
| | | return; |
| | | if (!_vm.IsValid()) |
| | | return; |
| | | var dlg = new SetPointDlg(); |
| | | dlg.SetBindingData(); |
| | | dlg.VerifyValueChanged += (x, y) => |
| | | { |
| | | var pt = new Yw.Geometry.Point2d(x, y); |
| | | var sect_pt = Yw.Pump.PerformParabolaHelper.GetQHSectPoint(_model.CurveQH, pt); |
| | | if (sect_pt == null || sect_pt.IsZeroPoint()) |
| | | return false; |
| | | |
| | | var speed = sect_pt.Y.CalculateSimuByH(_nr, pt.Y); |
| | | var hz = Math.Round(speed / _nr * 50, 1); |
| | | if (hz > 50 || hz < 20) |
| | | { |
| | | return false; |
| | | } |
| | | speed = Math.Round(speed, 1); |
| | | var index = _vm.VariableSpeedList?.Count + 1 ?? 1; |
| | | var vs_color = Yw.WinFrmUI.PhartColorHelper.Get(index); |
| | | var vs_vm = new Yw.WinFrmUI.Phart.PumpCurveVariableSpeedViewModel(); |
| | | vs_vm.Id = Guid.NewGuid().ToString(); |
| | | vs_vm.ColorQH = vs_color; |
| | | vs_vm.ColorQE = vs_color; |
| | | vs_vm.ColorQP = vs_color; |
| | | vs_vm.CurveQHName = $"{speed}rpm({hz}hz)"; |
| | | vs_vm.CurveQPName = $"{speed}rpm({hz}hz)"; |
| | | vs_vm.CurveQH = _vm.CurveQH.GetQHPointListByN(50, hz); |
| | | vs_vm.CurveQE = _vm.CurveQE.GetQEPointListByN(50, hz); |
| | | vs_vm.CurveQP = _vm.CurveQP.GetQEPointListByN(50, hz); |
| | | vs_vm.N = speed; |
| | | vs_vm.Hz = hz; |
| | | _vm.VariableSpeedList.Add(vs_vm); |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | |
| | | return true; |
| | | }; |
| | | dlg.ShowDialog(); |
| | | } |
| | | |
| | | private void AddByEff() |
| | | { |
| | | if (_vm == null) |
| | | return; |
| | | if (!_vm.IsValid()) |
| | | return; |
| | | if (_vm.CurveQE == null || !_vm.CurveQE.Any()) |
| | | return; |
| | | var dlg = new SetEffDlg(); |
| | | dlg.SetBindingData(); |
| | | dlg.VerifyValueChanged += (eff_double_list) => |
| | | { |
| | | double max_hz = 50; |
| | | double min_hz = 30; |
| | | if (_vm.VariableSpeedList != null && _vm.VariableSpeedList.Any()) |
| | | { |
| | | max_hz = Math.Max(max_hz, _vm.VariableSpeedList.Max(x => x.Hz)); |
| | | min_hz = Math.Min(min_hz, _vm.VariableSpeedList.Min(x => x.Hz)); |
| | | } |
| | | |
| | | foreach (var eff in eff_double_list) |
| | | { |
| | | var eff_list = EqualParaCurveEListHelper.CalcEqualParaCurveE(_model.CurveQH, _model.CurveQE, max_hz, min_hz, eff); |
| | | if (eff_list != null && eff_list.Any()) |
| | | { |
| | | foreach (var item in eff_list) |
| | | { |
| | | var def_pt_list = item.DefinePoints; |
| | | var eff_vm = new Yw.WinFrmUI.Phart.PumpCurveEqualEffViewModel(); |
| | | eff_vm.Id = Guid.NewGuid().ToString(); |
| | | eff_vm.CurveName = $"{item.Eff:N1}"; |
| | | eff_vm.Color = Color.DarkGreen; |
| | | eff_vm.CurveEqualEff = item.DefinePoints; |
| | | eff_vm.Tension = item.Tension; |
| | | eff_vm.IsClosed = item.IsClosed; |
| | | _vm.EqualEffList.Add(eff_vm); |
| | | } |
| | | } |
| | | } |
| | | |
| | | SetBindingData(_vm, _default_visible, _split_panel, _eff_visible, _power_visible, _equal_eff_visible); |
| | | |
| | | return true; |
| | | }; |
| | | dlg.ShowDialog(); |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | return new Yw.Vmo.PhartDiagramExGraphListVmo(); |
| | | } |
| | | |
| | | |
| | | } |
| | | } |