Files
GukSang.Jin e8befb8883 更新
2026-02-05 16:13:03 +08:00

395 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using OxyPlot.WindowsForms;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace
{
/// <summary>
/// 图表管理器 - 负责实时曲线图的创建和数据更新
/// 使用统一Y轴显示实时压力和压力设定值便于数据对比
/// </summary>
public class ChartManager
{
private PlotView _plotView;
private PlotModel _plotModel;
private LineSeries _pressureSeries; // 实时压力值地址3130
private LineSeries _pressureSetSeries; // 压力设定值地址2400
private List<DataPoint> _pressureData;
private List<DataPoint> _pressureSetData;
private const int MAX_DATA_POINTS = 3600; // 最多显示3600个数据点1小时
private double _initialMinX = 0; // 记录初始最小X值
private bool _isUserZooming = false; // 用户是否正在缩放
private double _maxPressureValue = 0; // 记录最大压力值用于动态调整Y轴
/// <summary>
/// 初始化图表并添加到指定面板
/// </summary>
public void InitializeChart(Control targetPanel)
{
if (targetPanel == null)
throw new ArgumentNullException(nameof(targetPanel));
// 清除占位文字
targetPanel.Text = null;
// 初始化数据集合
_pressureData = new List<DataPoint>();
_pressureSetData = new List<DataPoint>();
// 创建PlotModel
_plotModel = new PlotModel
{
Title = "",
Background = OxyColors.White,
PlotAreaBorderColor = OxyColor.FromRgb(180, 180, 180),
PlotAreaBorderThickness = new OxyThickness(1.5),
Padding = new OxyThickness(10, 10, 20, 10)
};
// 配置图例 - 放在图表外面顶部
_plotModel.Legends.Add(new OxyPlot.Legends.Legend
{
LegendPosition = OxyPlot.Legends.LegendPosition.TopCenter,
LegendOrientation = OxyPlot.Legends.LegendOrientation.Horizontal,
LegendPlacement = OxyPlot.Legends.LegendPlacement.Outside,
LegendBackground = OxyColor.FromAColor(250, OxyColors.White),
LegendBorder = OxyColor.FromRgb(200, 200, 200),
LegendBorderThickness = 1,
LegendPadding = 10,
LegendMargin = 8,
LegendSymbolLength = 30,
LegendSymbolMargin = 10,
LegendFontSize = 12,
LegendItemSpacing = 20
});
// 配置X轴时间轴 - 使用测试时间总秒数)
var xAxis = new LinearAxis
{
Position = AxisPosition.Bottom,
Title = "测试时间",
TitleFontSize = 13,
TitleFontWeight = OxyPlot.FontWeights.Bold,
FontSize = 11,
MajorGridlineStyle = LineStyle.Solid,
MajorGridlineColor = OxyColor.FromRgb(220, 220, 220),
MajorGridlineThickness = 1,
MinorGridlineStyle = LineStyle.Dot,
MinorGridlineColor = OxyColor.FromRgb(240, 240, 240),
MinorGridlineThickness = 0.5,
Minimum = 0,
Maximum = 60,
IsPanEnabled = true,
IsZoomEnabled = true,
// 格式化X轴标签为HH:MM:SS格式
LabelFormatter = value =>
{
int totalSec = (int)value;
int h = totalSec / 3600;
int m = (totalSec % 3600) / 60;
int s = totalSec % 60;
return $"{h:D2}:{m:D2}:{s:D2}";
}
};
_plotModel.Axes.Add(xAxis);
// 配置Y轴统一显示实时压力和压力设定值
var yAxis = new LinearAxis
{
Position = AxisPosition.Left,
Title = "压力 (PSI)",
TitleFontSize = 13,
TitleFontWeight = OxyPlot.FontWeights.Bold,
FontSize = 11,
MajorGridlineStyle = LineStyle.Solid,
MajorGridlineColor = OxyColor.FromRgb(220, 220, 220),
MajorGridlineThickness = 1,
MinorGridlineStyle = LineStyle.Dot,
MinorGridlineColor = OxyColor.FromRgb(240, 240, 240),
MinorGridlineThickness = 0.5,
StringFormat = "F2",
Minimum = 0,
Maximum = 10, // 初始最大值,会动态调整
Key = "YAxis",
IsPanEnabled = true,
IsZoomEnabled = true
};
_plotModel.Axes.Add(yAxis);
// 添加实时压力曲线 - 使用蓝色实线
_pressureSeries = new LineSeries
{
Title = "实时压力",
Color = OxyColor.FromRgb(30, 144, 255), // 道奇蓝
StrokeThickness = 2.5,
MarkerType = MarkerType.Circle,
MarkerSize = 5,
MarkerFill = OxyColor.FromRgb(30, 144, 255),
MarkerStroke = OxyColors.White,
MarkerStrokeThickness = 1.5,
LineStyle = LineStyle.Solid,
YAxisKey = "YAxis"
};
_plotModel.Series.Add(_pressureSeries);
// 添加压力设定值曲线 - 使用红色虚线
_pressureSetSeries = new LineSeries
{
Title = "压力设定值",
Color = OxyColor.FromRgb(220, 20, 60), // 深红色
StrokeThickness = 2.5,
MarkerType = MarkerType.Square,
MarkerSize = 5,
MarkerFill = OxyColor.FromRgb(220, 20, 60),
MarkerStroke = OxyColors.White,
MarkerStrokeThickness = 1.5,
LineStyle = LineStyle.Dash,
YAxisKey = "YAxis"
};
_plotModel.Series.Add(_pressureSetSeries);
// 创建PlotView控件
_plotView = new PlotView
{
Model = _plotModel,
Dock = DockStyle.Fill,
BackColor = System.Drawing.Color.White
};
// 监听鼠标事件,检测用户交互(缩放和平移)
_plotView.MouseDown += (s, e) =>
{
_isUserZooming = true;
};
_plotView.MouseWheel += (s, e) =>
{
_isUserZooming = true;
};
// 添加双击重置功能
_plotView.MouseDoubleClick += (s, e) =>
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
ResetZoom();
}
};
// 添加到目标面板
targetPanel.Controls.Add(_plotView);
}
/// <summary>
/// 添加新的数据点
/// </summary>
/// <param name="pressure">实时压力值</param>
/// <param name="pressureSetValue">压力设定值(来自"压力设置(PSI)"地址2400</param>
/// <param name="totalSeconds">测试时间(总秒数)</param>
public void AddDataPoint(double pressure, double pressureSetValue, int totalSeconds)
{
if (_pressureSeries == null || _pressureSetSeries == null || _plotModel == null)
return;
// 添加数据点使用测试时间总秒数作为X轴值
_pressureData.Add(new DataPoint(totalSeconds, pressure));
_pressureSetData.Add(new DataPoint(totalSeconds, pressureSetValue));
// 记录初始最小X值第一个数据点的时间
if (_pressureData.Count == 1)
{
_initialMinX = totalSeconds;
}
// 更新最大压力值用于动态调整Y轴
double currentMaxPressure = Math.Max(pressure, pressureSetValue);
if (currentMaxPressure > _maxPressureValue)
{
_maxPressureValue = currentMaxPressure;
}
// 限制数据点数量保持图表流畅保留最近3600个点
if (_pressureData.Count > MAX_DATA_POINTS)
{
_pressureData.RemoveAt(0);
_pressureSetData.RemoveAt(0);
// 更新初始最小X值
if (_pressureData.Count > 0)
{
_initialMinX = _pressureData[0].X;
}
}
// 更新系列数据
_pressureSeries.Points.Clear();
_pressureSeries.Points.AddRange(_pressureData);
_pressureSetSeries.Points.Clear();
_pressureSetSeries.Points.AddRange(_pressureSetData);
// 只有在非用户缩放状态下才自动调整轴范围
if (!_isUserZooming)
{
// 调整X轴范围
if (_pressureData.Count > 0)
{
var xAxis = _plotModel.Axes.FirstOrDefault(a => a.Position == AxisPosition.Bottom);
if (xAxis != null)
{
// 始终从初始时间开始显示
double minX = _initialMinX;
double maxX = _pressureData.Max(p => p.X);
xAxis.Minimum = minX;
xAxis.Maximum = maxX + 5; // 留一点余量
xAxis.Reset();
}
}
// 动态调整Y轴范围 - 比实际最大值大20%,确保曲线美观
var yAxis = _plotModel.Axes.FirstOrDefault(a => a.Key == "YAxis");
if (yAxis != null && _maxPressureValue > 0)
{
// Y轴最大值设置为实际最大值的1.2倍最小为10
double yMax = Math.Max(_maxPressureValue * 1.2, 10);
// 向上取整到合适的刻度
if (yMax < 10)
yMax = Math.Ceiling(yMax);
else if (yMax < 100)
yMax = Math.Ceiling(yMax / 5) * 5;
else
yMax = Math.Ceiling(yMax / 10) * 10;
yAxis.Minimum = 0;
yAxis.Maximum = yMax;
yAxis.Reset();
}
}
// 刷新图表
_plotModel.InvalidatePlot(true);
}
/// <summary>
/// 清除所有数据
/// </summary>
public void ClearData()
{
_pressureData?.Clear();
_pressureSetData?.Clear();
if (_pressureSeries != null)
{
_pressureSeries.Points.Clear();
}
if (_pressureSetSeries != null)
{
_pressureSetSeries.Points.Clear();
}
// 重置X轴范围
var xAxis = _plotModel?.Axes.FirstOrDefault(a => a.Position == AxisPosition.Bottom);
if (xAxis != null)
{
xAxis.Minimum = 0;
xAxis.Maximum = 60;
xAxis.Reset();
}
// 重置Y轴范围
var yAxis = _plotModel?.Axes.FirstOrDefault(a => a.Key == "YAxis");
if (yAxis != null)
{
yAxis.Minimum = 0;
yAxis.Maximum = 10;
yAxis.Reset();
}
// 重置初始最小X值、用户缩放状态和最大压力值
_initialMinX = 0;
_isUserZooming = false;
_maxPressureValue = 0;
_plotModel?.InvalidatePlot(true);
}
/// <summary>
/// 重置缩放状态,恢复自动跟随模式
/// </summary>
public void ResetZoom()
{
_isUserZooming = false;
if (_pressureData.Count > 0)
{
// 重置X轴
var xAxis = _plotModel?.Axes.FirstOrDefault(a => a.Position == AxisPosition.Bottom);
if (xAxis != null)
{
double minX = _initialMinX;
double maxX = _pressureData.Max(p => p.X);
xAxis.Minimum = minX;
xAxis.Maximum = maxX + 5;
xAxis.Reset();
}
// 重置Y轴
var yAxis = _plotModel?.Axes.FirstOrDefault(a => a.Key == "YAxis");
if (yAxis != null && _maxPressureValue > 0)
{
double yMax = Math.Max(_maxPressureValue * 1.2, 10);
if (yMax < 10)
yMax = Math.Ceiling(yMax);
else if (yMax < 100)
yMax = Math.Ceiling(yMax / 5) * 5;
else
yMax = Math.Ceiling(yMax / 10) * 10;
yAxis.Minimum = 0;
yAxis.Maximum = yMax;
yAxis.Reset();
}
}
_plotModel?.InvalidatePlot(true);
}
/// <summary>
/// 更新图表配置(例如切换温度模式时)
/// </summary>
/// <param name="isHighTemperatureMode">是否为高温模式</param>
public void UpdateChartMode(bool isHighTemperatureMode)
{
if (_plotModel == null || _plotModel.Series.Count < 2)
return;
// 可以根据模式调整显示样式(如果需要)
_plotModel.InvalidatePlot(true);
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
_plotView?.Dispose();
_plotModel = null;
_pressureSeries = null;
_pressureSetSeries = null;
_pressureData = null;
_pressureSetData = null;
}
}
}