using DevExpress.Utils;
|
using DevExpress.XtraCharts;
|
using SqlSugar;
|
using System.Text;
|
|
namespace Yw.WinFrmUI.Phart
|
{
|
/// <summary>
|
/// 泵图表
|
/// </summary>
|
public partial class PumpChart : DevExpress.XtraEditors.XtraUserControl
|
{
|
public PumpChart()
|
{
|
InitializeComponent();
|
InitialChart();
|
this.chartControl1.RuntimeHitTesting = true;
|
}
|
|
#region Private Variable
|
|
private XYDiagram _diagram;
|
private XYDiagramDefaultPane _default_pane;
|
private XYDiagramPane _bottom_pane;
|
|
private AxisX _axis_x_flow;
|
private AxisY _axis_y_head;
|
private SecondaryAxisY _axis_y_eff, _axis_y_power;
|
|
private ConstantLine _const_line_x;
|
private ConstantLine _const_line_y;
|
private TextAnnotation _const_txt_annotation;
|
|
//private bool _eff_visible = true;
|
//private bool _power_visible = true;
|
//private bool _equip_visible = true;
|
//private bool _equivalent_eff_visible = true;
|
//private bool _equivalent_power_visible = true;
|
|
|
private bool _initial_data = false;
|
private PumpCoordinate _coordinate = null;
|
|
private PumpChartViewModel _vm = null;
|
private XYDiagramPaneBase _bottom_pane_use;
|
|
|
/// <summary>
|
/// 初始化图表
|
/// </summary>
|
private void InitialChart()
|
{
|
this.chartControl1.SetChartDisplay();
|
|
_diagram = (XYDiagram)this.chartControl1.Diagram;
|
_default_pane = _diagram.DefaultPane;
|
_bottom_pane = (XYDiagramPane)_diagram.FindPaneByName("BottomPanel");
|
|
_axis_x_flow = _diagram.AxisX;
|
_axis_x_flow.SetAxisXQDisplay();
|
_axis_y_head = _diagram.AxisY;
|
_axis_y_head.SetAxisYQHDisplay();
|
_axis_y_eff = _diagram.SecondaryAxesY.GetAxisByName("AxisYEff");
|
_axis_y_eff.SetSecondaryAxisYQEDisplay();
|
_axis_y_eff.Alignment = AxisAlignment.Far;
|
_axis_y_power = _diagram.SecondaryAxesY.GetAxisByName("AxisYPower");
|
_axis_y_power.SetSecondaryAxisYQPDisplay();
|
|
_const_line_x = (ConstantLine)_diagram.AxisX.ConstantLines.GetElementByName("ConstantLineX");
|
_const_line_x.SetWorkPointLineDisplay();
|
|
_const_line_y = (ConstantLine)_diagram.AxisY.ConstantLines.GetElementByName("ConstantLineY");
|
_const_line_y.SetWorkPointLineDisplay();
|
|
_axis_x_flow.Visibility = DefaultBoolean.False;
|
_axis_x_flow.GridLines.Visible = false;
|
_axis_y_head.Visibility = DefaultBoolean.False;
|
_axis_y_head.GridLines.Visible = false;
|
_axis_y_eff.Visibility = DefaultBoolean.False;
|
_axis_y_eff.GridLines.Visible = false;
|
_axis_y_power.Visibility = DefaultBoolean.False;
|
_axis_y_power.GridLines.Visible = false;
|
|
_const_line_x.Visible = false;
|
|
}
|
|
|
#endregion
|
|
#region Set
|
|
|
/// <summary>
|
/// 设置图表
|
/// </summary>
|
private void SetChart(PumpChartViewModel vm, bool rated_curve_show = true, bool split_panel = false)
|
{
|
_vm = vm;
|
this.chartControl1.BeginInit();
|
this.chartControl1.Series.Clear();
|
this.chartControl1.AnnotationRepository.Clear();
|
this.chartControl1.Legend.CustomItems.Clear();
|
if (vm == null)
|
{
|
this.chartControl1.EndInit();
|
return;
|
}
|
|
_bottom_pane_use = split_panel ? _default_pane : _bottom_pane;
|
|
|
if (rated_curve_show)
|
AddRated(vm);
|
|
AddEquip(vm.Equip);
|
AddVariableSpeedList(vm.VariableSpeedList);
|
AddEquivEffList(vm.EquivEffList);
|
AddEquivPowerList(vm.EquivPowerList);
|
this.chartControl1.EndInit();
|
}
|
|
private void AddRated(PumpChartViewModel vm)
|
{
|
var id = vm.Id;
|
var caption_qh = vm.CurveQHName;
|
var caption_qp = vm.CurveQPName;
|
var color_qh = vm.ColorQH ?? PumpChartDisplay.CurveColorQH;
|
var color_qe = vm.ColorQE ?? PumpChartDisplay.CurveColorQE;
|
var color_qp = vm.ColorQP ?? PumpChartDisplay.CurveColorQP;
|
|
var qh = vm.CurveQH;
|
var qe = vm.CurveQE;
|
var qp = vm.CurveQP;
|
|
AddLineSeries(id, color_qh, _axis_x_flow, _axis_y_head, qh);
|
if (string.IsNullOrEmpty(caption_qh))
|
AddAnnotation(id, caption_qh, color_qh, _default_pane, qh.Last());
|
|
if (qe != null && qe.Any())
|
{
|
AddLineSeries(id, color_qe, _axis_x_flow, _axis_y_eff, qe);
|
}
|
|
if (qp != null && qp.Any())
|
{
|
AddLineSeries(id, color_qp, _axis_x_flow, _axis_y_power, qp);
|
if (string.IsNullOrEmpty(caption_qp))
|
{
|
AddAnnotation(id, caption_qp, color_qp, _bottom_pane_use, qp.Last());
|
}
|
}
|
}
|
|
private void AddEquip(PumpChartEquipViewModel vm)
|
{
|
if (vm == null)
|
return;
|
var id = vm.Id;
|
var caption = vm.CurveName;
|
var color = vm.Color ?? Color.Black;
|
|
var equip = vm.CurveEquip;
|
var equip_pt = new Yw.Geometry.Point2d(vm.PipeQ, vm.PipeH);
|
|
AddLineSeries(id, color, _axis_x_flow, _axis_y_head, equip, DashStyle.DashDotDot);
|
AddPointSeries(id, color, _axis_x_flow, _axis_y_head, new List<Geometry.Point2d>() { equip_pt });
|
if (!string.IsNullOrEmpty(caption))
|
AddAnnotation(id, caption, color, _default_pane, equip.Last());
|
}
|
|
private void AddVariableSpeedList(List<PumpChartVariableSpeedViewModel> vm_list)
|
{
|
if (vm_list == null || vm_list.Any())
|
return;
|
|
foreach (var vm in vm_list)
|
{
|
if (!vm.IsValid())
|
continue;
|
|
var id = vm.Id;
|
var caption_qh = vm.CurveQHName;
|
var caption_qp = vm.CurveQPName;
|
var color_qh = vm.ColorQH ?? PumpChartDisplay.CurveColorQH;
|
var color_qe = vm.ColorQE ?? PumpChartDisplay.CurveColorQE;
|
var color_qp = vm.ColorQP ?? PumpChartDisplay.CurveColorQP;
|
|
var qh = vm.CurveQH;
|
var qe = vm.CurveQE;
|
var qp = vm.CurveQP;
|
|
AddLineSeries(id, color_qh, _axis_x_flow, _axis_y_head, qh);
|
if (string.IsNullOrEmpty(caption_qh))
|
AddAnnotation(id, caption_qh, color_qh, _default_pane, qh.Last());
|
|
if (qe != null && qe.Any())
|
{
|
AddLineSeries(id, color_qe, _axis_x_flow, _axis_y_eff, qe);
|
}
|
|
if (qp != null && qp.Any())
|
{
|
AddLineSeries(id, color_qp, _axis_x_flow, _axis_y_power, qp);
|
if (string.IsNullOrEmpty(caption_qp))
|
{
|
AddAnnotation(id, caption_qp, color_qp, _bottom_pane_use, qp.Last());
|
}
|
}
|
}
|
|
}
|
|
private void AddEquivEffList(List<PumpChartEquivEffViewModel> vm_list)
|
{
|
if (vm_list == null || vm_list.Any())
|
return;
|
|
foreach (var vm in vm_list)
|
{
|
if (!vm.IsValid())
|
continue;
|
|
var id = vm.Id;
|
var caption = vm.CurveName;
|
var color = vm.Color ?? Color.Green;
|
|
var equif_eff = vm.CurveEquivEff;
|
|
AddLineSeries(id, color, _axis_x_flow, _axis_y_head, equif_eff);
|
if (!string.IsNullOrEmpty(caption))
|
AddAnnotation(id, caption, color, _default_pane, equif_eff.Last());
|
}
|
}
|
|
private void AddEquivPowerList(List<PumpChartEquivPowerViewModel> vm_list)
|
{
|
if (vm_list == null || vm_list.Any())
|
return;
|
|
foreach (var vm in vm_list)
|
{
|
if (!vm.IsValid())
|
continue;
|
|
var id = vm.Id;
|
var caption = vm.CurveName;
|
var color = vm.Color ?? Color.Green;
|
|
var equif_eff = vm.CurveEquivPower;
|
|
AddLineSeries(id, color, _axis_x_flow, _axis_y_head, equif_eff);
|
if (!string.IsNullOrEmpty(caption))
|
AddAnnotation(id, caption, color, _default_pane, equif_eff.Last());
|
}
|
}
|
|
private void AddPointSeries(string id, Color color, AxisXBase axis_x, AxisYBase axis_y, List<Yw.Geometry.Point2d> pt_list)
|
{
|
if (pt_list == null || !pt_list.Any())
|
return;
|
|
var view = new DevExpress.XtraCharts.PointSeriesView();
|
view.PointMarkerOptions.Size = 10;
|
view.PointMarkerOptions.Kind = MarkerKind.Circle;
|
view.PointMarkerOptions.BorderColor = color;
|
view.Color = color;
|
view.AxisX = axis_x;
|
view.AxisY = axis_y;
|
view.EmptyPointOptions.Color = Color.Transparent;
|
|
var series_pt_list = pt_list.Select(x => x.ToSeriesPoint()).ToArray();
|
var series = new DevExpress.XtraCharts.Series();
|
series.Tag = id;
|
series.ShowInLegend = false;
|
series.ArgumentScaleType = DevExpress.XtraCharts.ScaleType.Numerical;
|
series.LabelsVisibility = DevExpress.Utils.DefaultBoolean.False;
|
series.CrosshairEnabled = DefaultBoolean.False;
|
series.SeriesPointsSorting = SortingMode.None;
|
series.Visible = true;
|
series.View = view;
|
series.Points.AddRange(series_pt_list);
|
this.chartControl1.Series.Add(series);
|
}
|
|
private void AddLineSeries(string id, Color color, AxisXBase axis_x, AxisYBase axis_y, List<Yw.Geometry.Point2d> pt_list, DashStyle dash= DashStyle.Solid)
|
{
|
if (pt_list == null || !pt_list.Any())
|
return;
|
|
var view = new DevExpress.XtraCharts.LineSeriesView();
|
view.LineStyle.DashStyle = dash;
|
view.LineStyle.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
|
view.LineStyle.Thickness = 2;
|
view.Color = color;
|
view.EnableAntialiasing = DefaultBoolean.True;
|
view.MarkerVisibility = DefaultBoolean.False;
|
view.AxisX = axis_x;
|
view.AxisY = axis_y;
|
view.EmptyPointOptions.Color = Color.Transparent;
|
|
var series_pt_list = pt_list.Select(x => x.ToSeriesPoint()).ToArray();
|
var series = new DevExpress.XtraCharts.Series();
|
series.Tag = id;
|
series.ShowInLegend = false;
|
series.ArgumentScaleType = DevExpress.XtraCharts.ScaleType.Numerical;
|
series.LabelsVisibility = DevExpress.Utils.DefaultBoolean.False;
|
series.CrosshairEnabled = DefaultBoolean.False;
|
series.SeriesPointsSorting = SortingMode.None;
|
series.Visible = true;
|
series.View = view;
|
series.Points.AddRange(series_pt_list);
|
|
|
this.chartControl1.Series.Add(series);
|
}
|
|
private void AddAnnotation(string id, string caption, Color color, XYDiagramPaneBase pane, Yw.Geometry.Point2d pt)
|
{
|
if (pt == null)
|
return;
|
var anchor_pt = pt;
|
var pane_anchor_pt = new DevExpress.XtraCharts.PaneAnchorPoint();
|
pane_anchor_pt.Pane = pane;
|
pane_anchor_pt.AxisXCoordinate.AxisValue = anchor_pt.X;
|
pane_anchor_pt.AxisYCoordinate.AxisValue = anchor_pt.Y;
|
|
var relative_position = new DevExpress.XtraCharts.RelativePosition();
|
relative_position.Angle = -50;
|
relative_position.ConnectorLength = 10;
|
|
var text_annotation = new TextAnnotation();
|
text_annotation.Border.Visibility = DefaultBoolean.False;
|
text_annotation.AnchorPoint = pane_anchor_pt;
|
text_annotation.AutoHeight = true;
|
text_annotation.AutoWidth = true;
|
text_annotation.BackColor = System.Drawing.Color.Transparent;
|
text_annotation.Border.Color = color;
|
text_annotation.ConnectorStyle = DevExpress.XtraCharts.AnnotationConnectorStyle.Line;
|
text_annotation.Tag = id;
|
text_annotation.Padding.Bottom = 1;
|
text_annotation.Padding.Left = 1;
|
text_annotation.Padding.Right = 1;
|
text_annotation.Padding.Top = 1;
|
text_annotation.RuntimeMoving = true;
|
text_annotation.RuntimeAnchoring = false;
|
text_annotation.RuntimeResizing = false;
|
text_annotation.RuntimeRotation = false;
|
text_annotation.Text = caption;
|
text_annotation.TextColor = color;
|
text_annotation.ShapePosition = relative_position;
|
text_annotation.Visible = true;
|
|
this.chartControl1.AnnotationRepository.Add(text_annotation);
|
}
|
|
|
//private void SetAxis(PumpChartViewModel vm)
|
//{
|
// double minQ, maxQ;
|
// double maxH = 0, minH = 10000;
|
// double maxE = 0, minE = 0;
|
// double maxP = 0, minP = 1000;
|
// if (vm == null)
|
// {
|
|
// }
|
|
//}
|
|
#endregion
|
|
#region Calc
|
|
private double _minQ, _maxQ;
|
private double _maxH = 0, _minH = 10000;
|
private double _maxE = 0, _minE = 0;
|
private double _maxP = 0, _minP = 1000;
|
|
/// <summary>
|
/// 计算坐标
|
/// </summary>
|
private void CalcCoordinate()
|
{
|
if (_vm_list == null || !_vm_list.Any())
|
{
|
//设置成白板坐标
|
_coordinate = new PumpCoordinate();
|
_coordinate.GridNumberX = 30;
|
_coordinate.GridNumberY = 16;
|
//显示的坐标线号
|
_coordinate.StartLineNoH = 10;
|
_coordinate.EndLineNoH = 15;
|
_coordinate.StartLineNoE = 0;
|
_coordinate.EndLineNoE = 10;
|
_coordinate.StartLineNoP = 2;
|
_coordinate.EndLineNoP = 9;
|
//坐标最小值和间隔
|
_coordinate.CoordMinQ = 0; _coordinate.CoordSpaceQ = 1000;
|
_coordinate.CoordMinH = 10; _coordinate.CoordSpaceH = 100;
|
_coordinate.CoordMinE = 0; _coordinate.CoordSpaceE = 100;
|
_coordinate.CoordMinP = 10; _coordinate.CoordSpaceP = 100;
|
return;
|
}
|
|
_maxQ = 0; _minQ = 10000;
|
_maxH = 0; _minH = 10000;
|
_maxE = 0; _minE = 0;
|
_maxP = 0; _minP = 1000;
|
|
double _scaleMinH = 1, _scaleMaxH = 1;
|
|
|
foreach (var vm in _vm_list)
|
{
|
var qh_pt_list = vm.CurveQH.GetPointList();
|
if (vm.CurrentCurveQH != null)
|
qh_pt_list.AddRange(vm.CurrentCurveQH.GetPointList());
|
|
var xxx = qh_pt_list.Select(x => x.X);
|
var yyy = qh_pt_list.Select(x => x.Y);
|
|
_minQ = Math.Min(_minQ, xxx.Min());
|
_maxQ = Math.Max(_maxQ, xxx.Max());
|
|
_minH = Math.Min(_minH, yyy.Min());
|
_maxH = Math.Max(_maxH, yyy.Max());
|
}
|
|
|
foreach (PumpChartViewModel vm in _vm_list)
|
{
|
var qe_pt_list = vm.CurveQE.GetPointList();
|
if (vm.CurrentCurveQE != null)
|
qe_pt_list.AddRange(vm.CurrentCurveQE.GetPointList());
|
|
var yyy = qe_pt_list.Select(x => x.Y);
|
_minE = Math.Max(_minE, yyy.Min());
|
_maxE = Math.Max(_maxE, yyy.Max());
|
}
|
|
foreach (PumpChartViewModel vm in _vm_list)
|
{
|
var qe_pt_list = vm.CurveQP.GetPointList();
|
if (vm.CurrentCurveQP != null)
|
qe_pt_list.AddRange(vm.CurrentCurveQP.GetPointList());
|
|
var yyy = qe_pt_list.Select(x => x.Y);
|
_minP = Math.Min(_minP, yyy.Min());
|
_maxP = Math.Max(_maxP, yyy.Max());
|
}
|
|
if (_vm != null)
|
{
|
{
|
var qh_pt_list = _vm.CurveQH.GetPointList();
|
|
var xxx = qh_pt_list.Select(x => x.X);
|
var yyy = qh_pt_list.Select(x => x.Y);
|
|
_minQ = Math.Min(_minQ, xxx.Min());
|
_maxQ = Math.Max(_maxQ, xxx.Max());
|
|
_minH = Math.Min(_minH, yyy.Min());
|
_maxH = Math.Max(_maxH, yyy.Max());
|
}
|
|
if (_vm.CurveQE != null)
|
{
|
var qe_pt_list = _vm.CurveQE.GetPointList();
|
var yyy = qe_pt_list.Select(x => x.Y);
|
_minE = Math.Max(_minE, yyy.Min());
|
_maxE = Math.Max(_maxE, yyy.Max());
|
}
|
|
if (_vm.CurveQP != null)
|
{
|
var qp_pt_list = _vm.CurveQP.GetPointList();
|
var yyy = qp_pt_list.Select(x => x.Y);
|
_minP = Math.Min(_minP, yyy.Min());
|
_maxP = Math.Max(_maxP, yyy.Max());
|
}
|
}
|
|
_coordinate = PumpCoordinate.CalcCoordinate(_minQ,
|
_maxQ,
|
_minH * _scaleMinH,
|
_maxH * _scaleMaxH,
|
_minE,
|
_maxE,
|
_minP,
|
_maxP);
|
|
if (_coordinate == null)
|
return;
|
|
if (_coordinate.CoordMinQ + _coordinate.CoordSpaceQ * this._coordinate.GridNumberX < _maxQ * 1.05)
|
{
|
_coordinate.GridNumberX++;
|
}
|
}
|
|
/// <summary>
|
/// 计算图表轴
|
/// </summary>
|
private void CalcChartAxis()
|
{
|
_axis_x_flow.Visibility = DefaultBoolean.False;
|
_axis_x_flow.GridLines.Visible = false;
|
_axis_y_head.Visibility = DefaultBoolean.False;
|
_axis_y_head.GridLines.Visible = false;
|
_axis_y_eff.Visibility = DefaultBoolean.False;
|
_axis_y_eff.GridLines.Visible = false;
|
_axis_y_power.Visibility = DefaultBoolean.False;
|
_axis_y_power.GridLines.Visible = false;
|
|
_const_line_x.Visible = false;
|
|
_bottom_pane.Visibility = ChartElementVisibility.Hidden;
|
|
|
_bottom_pane.Visibility = _power_visible ? ChartElementVisibility.Visible : ChartElementVisibility.Hidden;
|
|
//计算刻度 Q
|
var axisQLabels = new List<CustomAxisLabel>();
|
var disQ = _coordinate.CoordMinQ;
|
for (int i = 0; i < _coordinate.GridNumberX + 1; i++)
|
{
|
axisQLabels.Add(new CustomAxisLabel(disQ.ToString("N0"), disQ));
|
disQ = disQ + _coordinate.CoordSpaceQ;
|
}
|
|
_axis_x_flow.CustomLabels.Clear();
|
_axis_x_flow.CustomLabels.AddRange(axisQLabels.ToArray());
|
_axis_x_flow.Visibility = DefaultBoolean.True;
|
_axis_x_flow.GridLines.Visible = true;
|
|
|
//计算刻度
|
var axis_head_labels = new List<CustomAxisLabel>();
|
var display_head = _coordinate.CoordMinH + _coordinate.CoordSpaceH * _coordinate.StartLineNoH;
|
for (int i = _coordinate.StartLineNoH; i < _coordinate.EndLineNoH + 1; i++)
|
{
|
axis_head_labels.Add(new CustomAxisLabel(display_head.ToString(), display_head));
|
display_head = display_head + _coordinate.CoordSpaceH;
|
}
|
|
_axis_y_head.CustomLabels.Clear();
|
_axis_y_head.CustomLabels.AddRange(axis_head_labels.ToArray());
|
_axis_y_head.Visibility = DefaultBoolean.True;
|
_axis_y_head.GridLines.Visible = true;
|
|
//效率
|
if (_maxE > _minE && _eff_visible)
|
{
|
//计算刻度
|
var labels = new List<CustomAxisLabel>();
|
var display_eff = _coordinate.CoordMinE + _coordinate.CoordSpaceE * _coordinate.StartLineNoE;
|
for (int i = _coordinate.StartLineNoE; i < _coordinate.EndLineNoE + 1; i++)
|
{
|
labels.Add(new CustomAxisLabel(display_eff.ToString(), display_eff));
|
display_eff = display_eff + _coordinate.CoordSpaceE;
|
}
|
|
_axis_y_eff.CustomLabels.Clear();
|
_axis_y_eff.CustomLabels.AddRange(labels.ToArray());
|
_axis_y_eff.Visibility = DefaultBoolean.True;
|
_axis_y_eff.GridLines.Visible = true;
|
}
|
|
//功率
|
if (_maxP > _minP && _power_visible)
|
{
|
//计算刻度
|
var labels = new List<CustomAxisLabel>();
|
double display_power = _coordinate.CoordMinP + _coordinate.CoordSpaceP * _coordinate.StartLineNoP;
|
for (int i = _coordinate.StartLineNoP; i < _coordinate.EndLineNoP + 1; i++)
|
{
|
labels.Add(new CustomAxisLabel(display_power.ToString(), display_power));
|
display_power = display_power + _coordinate.CoordSpaceP;
|
}
|
|
_axis_y_power.CustomLabels.Clear();
|
_axis_y_power.CustomLabels.AddRange(labels.ToArray());
|
_axis_y_power.Visibility = DefaultBoolean.True;
|
_axis_y_power.GridLines.Visible = true;
|
}
|
|
var min_flow = _coordinate.CoordMinQ;
|
var max_flow = _coordinate.DispMaxQ();
|
_axis_x_flow.SetAxisRange(min_flow, max_flow);
|
|
var grid_count_head = _coordinate.EndLineNoH - _coordinate.StartLineNoH;
|
var grid_count_eff = _coordinate.EndLineNoE - _coordinate.StartLineNoE;
|
int grid_count_up = Math.Max(grid_count_head, grid_count_eff);
|
if (_eff_visible)
|
{
|
grid_count_up += 2;//多两条
|
}
|
|
var max_axis_head = _coordinate.CoordMinH + _coordinate.EndLineNoH * _coordinate.CoordSpaceH;
|
var min_axis_head = max_axis_head - grid_count_up * _coordinate.CoordSpaceH;
|
_axis_y_head.SetAxisRange(min_axis_head, max_axis_head);
|
|
var min_axis_eff = _coordinate.CoordMinE + _coordinate.StartLineNoE * _coordinate.CoordSpaceE;
|
var max_axis_eff = min_axis_eff + grid_count_up * _coordinate.CoordSpaceE;
|
_axis_y_eff.SetAxisRange(min_axis_eff, max_axis_eff);
|
|
var grid_count_power = _coordinate.EndLineNoP - _coordinate.StartLineNoP;
|
var min_axis_power = _coordinate.CoordMinP + _coordinate.StartLineNoP * _coordinate.CoordSpaceP;
|
var max_axis_power = min_axis_power + grid_count_power * _coordinate.CoordSpaceP;
|
_axis_y_power.SetAxisRange(min_axis_power, max_axis_power);
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
}
|
}
|