This commit is contained in:
wxt
2026-02-02 16:18:52 +08:00
parent c212a5cab8
commit 36a43a497a
6 changed files with 387 additions and 830 deletions

View File

@@ -1,279 +1,96 @@
<dx:ThemedWindow
x:Class="jiancaiburanxing.BalanceChartWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts"
Title="温度平衡曲线图"
Height="650"
Width="1000"
WindowStartupLocation="CenterOwner"
Background="White">
<Window x:Class="jiancaiburanxing.BalanceChartWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:oxy="http://oxyplot.org/wpf"
Title="温度平衡曲线图" Height="750" Width="1100"
WindowStartupLocation="CenterScreen"
Loaded="Window_Loaded"
Closing="Window_Closing">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 标题栏 -->
<Border Grid.Row="0"
Background="#2C3E50"
Padding="15"
Margin="0,0,0,5">
<Border Grid.Row="0" Background="#2196F3" Padding="15">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Text="炉内温度平衡监控曲线"
FontSize="18"
FontWeight="Bold"
Foreground="White"/>
<TextBlock Text="实时显示炉内温度变化趋势与目标温度对比"
FontSize="12"
Foreground="#BDC3C7"/>
</StackPanel>
<TextBlock Text="炉内温度平衡曲线"
Foreground="White"
FontSize="20"
FontWeight="Bold"
VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" >
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button x:Name="btnClearData"
Content="清空数据"
Width="100"
Height="35"
Margin="5,0"
Background="#FF5722"
Foreground="White"
FontWeight="Bold"
Click="btnClearData_Click"/>
<dx:SimpleButton x:Name="btnClearData"
Content="清空数据"
Width="100"
Height="30"
FontSize="12"
Background="#E74C3C"
Foreground="White"
CornerRadius="4"
Click="btnClearData_Click"
Margin="10,0,0,0"/>
<dx:SimpleButton x:Name="btnExportData"
Content="导出数据"
Width="100"
Height="30"
FontSize="12"
Background="#3498DB"
Foreground="White"
CornerRadius="4"
Margin="10,0,0,0"/>
<dx:SimpleButton x:Name="btnClose"
Content="关闭"
Width="80"
Height="30"
FontSize="12"
Background="#95A5A6"
Foreground="White"
CornerRadius="4"
Click="btnClose_Click"
Margin="10,0,0,0"/>
<Button x:Name="btnClose"
Content="关闭"
Width="100"
Height="35"
Margin="5,0"
Background="#607D8B"
Foreground="White"
FontWeight="Bold"
Click="btnClose_Click"/>
</StackPanel>
</Grid>
</Border>
<!-- 图表区域 -->
<Border Grid.Row="1"
Margin="10,5,10,5"
BorderBrush="#DDDDDD"
BorderThickness="1"
Background="White">
<!-- 图表区域 -->
<Grid Grid.Row="1" Margin="10">
<oxy:PlotView x:Name="TemperaturePlot"
Background="White"
BorderBrush="#E0E0E0"
BorderThickness="1"/>
</Grid>
<dxc:ChartControl x:Name="temperatureChart">
<!-- 图表标题 -->
<dxc:ChartControl.Titles>
<dxc:Title Content="炉内温度实时监控曲线"
FontSize="14"
FontWeight="Bold"/>
</dxc:ChartControl.Titles>
<!-- 图例 -->
<dxc:ChartControl.Legend>
<dxc:Legend HorizontalAlignment="Right"
VerticalAlignment="Top"
Visibility="Visible"/>
</dxc:ChartControl.Legend>
<!-- XY图表 -->
<dxc:XYDiagram2D>
<!-- X轴 -->
<dxc:XYDiagram2D.AxisX>
<dxc:AxisX2D>
<dxc:AxisX2D.Title>
<dxc:AxisTitle Content="时间 (秒)"/>
</dxc:AxisX2D.Title>
<!--<dxc:AxisX2D.WholeRange>
<dxc:Range MinValue="0" MaxValue="600"/>
</dxc:AxisX2D.WholeRange>-->
<dxc:AxisX2D.Label>
<dxc:AxisLabel TextPattern="{}{V}秒"/>
</dxc:AxisX2D.Label>
</dxc:AxisX2D>
</dxc:XYDiagram2D.AxisX>
<!-- Y轴 -->
<dxc:XYDiagram2D.AxisY>
<dxc:AxisY2D>
<dxc:AxisY2D.Title>
<dxc:AxisTitle Content="温度 (°C)"/>
</dxc:AxisY2D.Title>
<dxc:AxisY2D.WholeRange>
<dxc:Range MinValue="0" MaxValue="800"/>
</dxc:AxisY2D.WholeRange>
<dxc:AxisY2D.Label>
<dxc:AxisLabel TextPattern="{}{V}°C"/>
</dxc:AxisY2D.Label>
</dxc:AxisY2D>
</dxc:XYDiagram2D.AxisY>
<!-- 温度曲线 -->
<dxc:LineSeries2D x:Name="temperatureSeries"
DisplayName="炉内温度"
MarkerVisible="True"
MarkerSize="6"
CrosshairLabelPattern="时间: {A}秒\n温度: {V}°C">
<dxc:LineSeries2D.LineStyle>
<dxc:LineStyle Thickness="2">
</dxc:LineStyle>
</dxc:LineSeries2D.LineStyle>
</dxc:LineSeries2D>
<!-- 目标温度线 -->
<dxc:LineSeries2D x:Name="targetSeries"
DisplayName="目标温度 (750°C)"
MarkerVisible="False"
CrosshairLabelPattern="目标温度: {V}°C">
<dxc:LineSeries2D.LineStyle>
<dxc:LineStyle Thickness="2">
<dxc:LineStyle.DashStyle>
<DashStyle Dashes="4,2"/>
</dxc:LineStyle.DashStyle>
</dxc:LineStyle>
</dxc:LineSeries2D.LineStyle>
</dxc:LineSeries2D>
</dxc:XYDiagram2D>
</dxc:ChartControl>
</Border>
<!-- 主要统计信息 -->
<!-- 统计信息栏 -->
<Border Grid.Row="2"
Background="#F5F7FA"
Padding="15"
Margin="10,0,10,5"
CornerRadius="4"
BorderBrush="#DDDDDD"
BorderThickness="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
Background="#F5F5F5"
BorderBrush="#E0E0E0"
BorderThickness="0,1,0,0"
Padding="10">
<WrapPanel>
<!-- 运行时间 -->
<StackPanel Grid.Column="0">
<TextBlock Text="已运行时间:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtElapsedTime" Text="0秒" FontSize="14" FontWeight="Bold" Foreground="#2C3E50"/>
</StackPanel>
<TextBlock Margin="10,0" Text="运行时间:" VerticalAlignment="Center"/>
<TextBlock x:Name="txtElapsedTime" Text="0秒" FontWeight="Bold" MinWidth="50" VerticalAlignment="Center"/>
<!-- 当前温度 -->
<StackPanel Grid.Column="1">
<TextBlock Text="当前温度:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtCurrentTemp" Text="0°C" FontSize="14" FontWeight="Bold" Foreground="#2C3E50"/>
</StackPanel>
<TextBlock Margin="20,0,10,0" Text="当前温度:" VerticalAlignment="Center"/>
<TextBlock x:Name="txtCurrentTemp" Text="0°C" FontWeight="Bold" Foreground="#2196F3" MinWidth="50" VerticalAlignment="Center"/>
<!-- 温度范围 -->
<StackPanel Grid.Column="2">
<TextBlock Text="温度范围:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtTempRange" Text="0°C" FontSize="14" FontWeight="Bold" Foreground="#2C3E50"/>
</StackPanel>
<!-- 与目标温差 -->
<StackPanel Grid.Column="3">
<TextBlock Text="与目标温差:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtTempDifference" Text="0°C" FontSize="14" FontWeight="Bold" Foreground="#2C3E50"/>
</StackPanel>
<!-- 数据点数 -->
<StackPanel Grid.Column="4">
<TextBlock Text="数据点数:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtDataPoints" Text="0" FontSize="14" FontWeight="Bold" Foreground="#2C3E50"/>
</StackPanel>
</Grid>
</Border>
<!-- 详细统计信息 -->
<Border Grid.Row="3"
Background="#F5F7FA"
Padding="15"
Margin="10,0,10,5"
CornerRadius="4"
BorderBrush="#DDDDDD"
BorderThickness="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 与目标差值 -->
<TextBlock Margin="20,0,10,0" Text="差值:" VerticalAlignment="Center"/>
<TextBlock x:Name="txtTempDifference" Text="0°C" FontWeight="Bold" MinWidth="50" VerticalAlignment="Center"/>
<!-- 最高温度 -->
<StackPanel Grid.Column="0">
<TextBlock Text="最高温度:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtMaxTemp" Text="0°C" FontSize="14" FontWeight="Bold" Foreground="#E74C3C"/>
</StackPanel>
<TextBlock Margin="20,0,10,0" Text="最高:" VerticalAlignment="Center"/>
<TextBlock x:Name="txtMaxTemp" Text="0°C" FontWeight="Bold" Foreground="#F44336" MinWidth="50" VerticalAlignment="Center"/>
<!-- 最低温度 -->
<StackPanel Grid.Column="1">
<TextBlock Text="最低温度:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtMinTemp" Text="0°C" FontSize="14" FontWeight="Bold" Foreground="#3498DB"/>
</StackPanel>
<TextBlock Margin="20,0,10,0" Text="最低:" VerticalAlignment="Center"/>
<TextBlock x:Name="txtMinTemp" Text="0°C" FontWeight="Bold" Foreground="#4CAF50" MinWidth="50" VerticalAlignment="Center"/>
<!-- 平均温度 -->
<StackPanel Grid.Column="2">
<TextBlock Text="平均温度:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtAvgTemp" Text="0°C" FontSize="14" FontWeight="Bold" Foreground="#2C3E50"/>
</StackPanel>
<!-- 温度稳定性 -->
<StackPanel Grid.Column="3">
<TextBlock Text="温度波动:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtTempFluctuation" Text="0°C" FontSize="14" FontWeight="Bold" Foreground="#F39C12"/>
</StackPanel>
<!-- 当前状态 -->
<StackPanel Grid.Column="4">
<TextBlock Text="当前状态:" FontSize="12" Foreground="#666666"/>
<TextBlock x:Name="txtStatus" Text="就绪" FontSize="14" FontWeight="Bold" Foreground="#27AE60"/>
</StackPanel>
</Grid>
<!-- 数据点数 -->
<TextBlock Margin="20,0,10,0" Text="点数:" VerticalAlignment="Center"/>
<TextBlock x:Name="txtDataPoints" Text="0" FontWeight="Bold" MinWidth="30" VerticalAlignment="Center"/>
</WrapPanel>
</Border>
<!-- 状态栏 -->
<StatusBar Grid.Row="4"
Background="#ECF0F1"
Height="25"
Margin="0">
<StatusBarItem>
<TextBlock x:Name="txtStatusBar" Text="就绪" FontSize="11" Foreground="#666666"/>
</StatusBarItem>
<Separator/>
<StatusBarItem HorizontalAlignment="Right">
<TextBlock Text="温度平衡监控系统" FontSize="11" Foreground="#666666" Margin="5,0"/>
</StatusBarItem>
</StatusBar>
</Grid>
</dx:ThemedWindow>
</Window>

View File

@@ -1,676 +1,379 @@
using DevExpress.Xpf.Charts;
using DevExpress.Xpf.Core;
using DevExpress.Xpf.Core.Native;
using OxyPlot;
using OxyPlot.Series;
using OxyPlot.Axes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace jiancaiburanxing
{
/// <summary>
/// BalanceChartWindow.xaml 的交互逻辑
/// </summary>
public partial class BalanceChartWindow : ThemedWindow
{
// 存储温度数据
private List<Tuple<double, double>> temperatureData = new List<Tuple<double, double>>();
public partial class BalanceChartWindow : Window
{
// 温度获取函数
private Func<double> _getFurnaceTemperature;
// 定时器用于实时更新
// 定时器
private DispatcherTimer _updateTimer;
// 已运行时间(秒)
private const int COLLECTION_DURATION = 600; // 10分钟
// 已运行时间
private int _elapsedSeconds = 0;
// 开始时间戳
private DateTime _startTime;
public BalanceChartWindow(Func<double> getFurnaceTemperature)
{
InitializeComponent();
Loaded += BalanceChartWindow_Loaded;
Unloaded += BalanceChartWindow_Unloaded;
_getFurnaceTemperature = getFurnaceTemperature;
}
// 目标温度
private const double TARGET_TEMPERATURE = 750.0;
private const double TOLERANCE = 5.0;
private void BalanceChartWindow_Loaded(object sender, RoutedEventArgs e)
{
try
// 数据存储
private List<DataPoint> temperatureData = new List<DataPoint>();
// PlotModel
private PlotModel plotModel;
public BalanceChartWindow(Func<double> getFurnaceTemperature)
{
InitializeChart();
StartDataCollection();
InitializeComponent();
_getFurnaceTemperature = getFurnaceTemperature;
// 初始化图表
InitializePlot();
}
catch (Exception ex)
private void InitializePlot()
{
MessageBox.Show($"初始化图表时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
plotModel = new PlotModel
{
Title = "温度波动曲线",
TitleFontSize = 16,
TitleFontWeight = OxyPlot.FontWeights.Bold,
PlotAreaBorderColor = OxyColors.LightGray,
PlotAreaBorderThickness = new OxyThickness(1)
};
// 设置背景色
plotModel.Background = OxyColors.White;
plotModel.PlotAreaBackground = OxyColors.White;
// X轴时间轴- 更关注近期的波动
var timeAxis = new LinearAxis
{
Position = AxisPosition.Bottom,
Title = "时间 (秒)",
TitleFontSize = 12,
AxislineColor = OxyColors.Black,
MinorGridlineColor = OxyColor.FromArgb(40, 200, 200, 200),
MajorGridlineColor = OxyColor.FromArgb(80, 150, 150, 150),
//Minimum = 0,
//Maximum = 300, // 初始5分钟更关注短期波动
//MajorStep = 60,
//MinorStep = 10
Minimum = 0,
Maximum = 600, // 改为10分钟
MajorStep = 60, // 每分钟一个主刻度
MinorStep = 10 // 每10秒一个次刻度
};
// Y轴温度轴- 重点显示750±20°C范围
var tempAxis = new LinearAxis
{
Position = AxisPosition.Left,
Title = "温度 (°C)",
TitleFontSize = 12,
AxislineColor = OxyColors.Black,
MinorGridlineColor = OxyColor.FromArgb(40, 200, 200, 200),
MajorGridlineColor = OxyColor.FromArgb(80, 150, 150, 150),
Minimum = 730, // 750-20
Maximum = 770, // 750+20
MajorStep = 10,
MinorStep = 2 // 更细的网格,便于观察波动
};
// 添加坐标轴
plotModel.Axes.Add(timeAxis);
plotModel.Axes.Add(tempAxis);
// 1. 先添加目标区域(作为背景)
var targetArea = new AreaSeries
{
Color = OxyColor.FromArgb(30, 76, 175, 80), // 浅绿色区域
Fill = OxyColor.FromArgb(30, 76, 175, 80),
StrokeThickness = 0
};
// 设置目标区域范围
targetArea.Points.Add(new DataPoint(0, TARGET_TEMPERATURE + TOLERANCE));
targetArea.Points.Add(new DataPoint(10000, TARGET_TEMPERATURE + TOLERANCE)); // 足够大的X值
targetArea.Points2.Add(new DataPoint(0, TARGET_TEMPERATURE - TOLERANCE));
targetArea.Points2.Add(new DataPoint(10000, TARGET_TEMPERATURE - TOLERANCE));
// 2. 添加目标线
var targetLine = new LineSeries
{
Color = OxyColors.Green,
StrokeThickness = 1.5,
LineStyle = LineStyle.Dash
};
targetLine.Points.Add(new DataPoint(0, TARGET_TEMPERATURE));
targetLine.Points.Add(new DataPoint(10000, TARGET_TEMPERATURE));
// 3. 添加温度曲线
var temperatureSeries = new LineSeries
{
Title = "炉内温度",
Color = OxyColors.Blue,
StrokeThickness = 2,
MarkerType = MarkerType.None, // 不要数据点标记,保持曲线简洁
CanTrackerInterpolatePoints = true // 允许鼠标悬停时显示插值点
};
// 添加到图表(注意顺序:背景在最下面)
plotModel.Series.Add(targetArea);
plotModel.Series.Add(targetLine);
plotModel.Series.Add(temperatureSeries);
// 绑定到PlotView
TemperaturePlot.Model = plotModel;
}
}
private void BalanceChartWindow_Unloaded(object sender, RoutedEventArgs e)
{
StopDataCollection();
}
private void InitializeChart()
private void AddTemperatureData(double timeInSeconds, double temperature)
{
try
{
// 初始化图表配置
if (temperatureChart.Diagram is XYDiagram2D diagram)
{
// 设置X轴 - 时间(秒)
if (diagram.AxisX is AxisX2D axisX)
{
// 清除可能在XAML中设置的范围
axisX.WholeRange = null;
axisX.VisualRange = null;
// 添加数据点
temperatureData.Add(new DataPoint(timeInSeconds, temperature));
// 设置标题
if (axisX.Title == null)
{
axisX.Title = new AxisTitle();
}
axisX.Title.Content = "时间 (秒)";
// 更新图表
UpdateChart();
// 使用正确的 Range 类
axisX.WholeRange = new DevExpress.Xpf.Charts.Range
{
MinValue = 0,
MaxValue = 600 // 默认显示10分钟600秒
};
// 设置可视范围(可选)
axisX.VisualRange = new DevExpress.Xpf.Charts.Range
{
MinValue = 0,
MaxValue = 600
};
// 设置网格线
axisX.GridLinesVisible = true;
axisX.GridLinesMinorVisible = false;
// 设置标签格式 - 修正这里
//axisX.Label = new AxisLabel
//{
// TextPattern = "{V:F0}秒" // 添加格式化,显示整数
//};
axisX.Label = new AxisLabel();
axisX.Label.TextPattern = "{V:F0}秒";
}
// 设置Y轴 - 温度
if (diagram.AxisY is AxisY2D axisY)
{
// 清除可能在XAML中设置的范围
axisY.WholeRange = null;
axisY.VisualRange = null;
// 设置标题
if (axisY.Title == null)
{
axisY.Title = new AxisTitle();
}
axisY.Title.Content = "温度 (°C)";
// 设置范围
axisY.WholeRange = new DevExpress.Xpf.Charts.Range
{
MinValue = 0,
MaxValue = 800
};
axisY.VisualRange = new DevExpress.Xpf.Charts.Range
{
MinValue = 0,
MaxValue = 800
};
// 设置网格线
axisY.GridLinesVisible = true;
axisY.GridLinesMinorVisible = false;
// 设置标签格式
//axisY.Label = new AxisLabel
//{
// TextPattern = "{V:F0}°C"
//};
axisY.Label = new AxisLabel();
axisY.Label.TextPattern = "{V:F0}°C";
}
}
// 更新统计信息
UpdateStatistics();
}
catch (Exception ex)
{
MessageBox.Show($"配置图表时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
Console.WriteLine($"添加数据时出错: {ex.Message}");
}
}
private void UpdateChart()
{
if (plotModel == null || temperatureData.Count == 0)
return;
// 获取温度曲线系列
var temperatureSeries = plotModel.Series[2] as LineSeries;
if (temperatureSeries == null)
return;
// 更新数据点限制最多显示1000个点避免性能问题
temperatureSeries.Points.Clear();
if (temperatureData.Count <= 1000)
{
// 数据量少,显示所有点
foreach (var point in temperatureData)
{
temperatureSeries.Points.Add(point);
}
}
else
{
// 数据量大只显示最近1000个点
var recentData = temperatureData.Skip(temperatureData.Count - 1000);
foreach (var point in recentData)
{
temperatureSeries.Points.Add(point);
}
}
// 自动调整X轴范围显示最近的数据
//var xAxis = plotModel.Axes[0] as LinearAxis;
//if (xAxis != null && temperatureData.Count > 0)
//{
// double maxTime = temperatureData.Last().X;
// // 保持显示最近300秒的数据
// xAxis.Minimum = Math.Max(0, maxTime - 300);
// xAxis.Maximum = Math.Max(300, maxTime);
//}
// 刷新图表
plotModel.InvalidatePlot(true);
}
private void StartDataCollection()
{
try
{
// 清空现有数据
temperatureData.Clear();
ClearAllSeries();
// 重置时间
_elapsedSeconds = 0;
_startTime = DateTime.Now;
txtElapsedTime.Text = "0秒";
// 添加750°C参考线
AddReferenceLine();
// 设置温度曲线样式
InitializeTemperatureSeries();
// 初始化定时器
InitializeUpdateTimer();
// 添加第一个数据点
AddInitialDataPoint();
// 更新统计信息
UpdateStatistics();
// 启动定时器
_updateTimer?.Start();
UpdateStatus("数据收集已开始");
}
catch (Exception ex)
{
MessageBox.Show($"开始数据收集时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void StopDataCollection()
{
try
{
_updateTimer?.Stop();
_updateTimer = null;
UpdateStatus("数据收集已停止");
}
catch (Exception ex)
{
Debug.WriteLine($"停止数据收集时出错: {ex.Message}");
}
}
private void InitializeUpdateTimer()
{
try
{
_updateTimer = new DispatcherTimer();
_updateTimer.Interval = TimeSpan.FromSeconds(1); // 每秒更新一次
_updateTimer.Tick += UpdateTimer_Tick;
}
catch (Exception ex)
{
MessageBox.Show($"设置定时器时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void UpdateTimer_Tick(object sender, EventArgs e)
{
try
{
// 更新时间
_elapsedSeconds++;
//TimeSpan elapsedTimeSpan = DateTime.Now - _startTime;
txtElapsedTime.Text = $"{_elapsedSeconds}秒 ";
// 获取当前温度
if (_getFurnaceTemperature != null)
{
double currentTemp = _getFurnaceTemperature();
// 如果温度有效大于0则添加到图表
if (currentTemp > 0)
{
AddTemperatureDataPoint(_elapsedSeconds, currentTemp);
}
else
{
Debug.WriteLine($"获取的温度无效: {currentTemp}");
}
}
else
{
Debug.WriteLine("温度获取函数为空");
}
// 如果需要调整X轴范围
AdjustXAxisRange();
}
catch (Exception ex)
{
Debug.WriteLine($"更新图表数据时出错: {ex.Message}");
}
}
private void AddInitialDataPoint()
{
try
{
if (_getFurnaceTemperature != null)
{
double initialTemp = _getFurnaceTemperature();
if (initialTemp > 0)
{
AddTemperatureDataPoint(0, initialTemp);
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"添加初始数据点时出错: {ex.Message}");
}
}
private void AddTemperatureDataPoint(double timeInSeconds, double temperature)
{
try
{
// 确保temperatureSeries存在
if (temperatureSeries == null)
{
Debug.WriteLine("temperatureSeries为空无法添加数据点");
return;
}
// 添加数据点到图表
AddDataPoint(temperatureSeries, timeInSeconds, temperature);
// 保存到数据列表用于统计
temperatureData.Add(new Tuple<double, double>(timeInSeconds, temperature));
// 更新统计信息
UpdateStatistics();
}
catch (Exception ex)
{
Debug.WriteLine($"添加温度数据点时出错: {ex.Message}");
}
}
private void AddReferenceLine()
{
try
{
// 确保targetSeries存在
if (targetSeries == null)
{
Debug.WriteLine("targetSeries为空无法添加参考线");
return;
}
// 清除现有数据点
ClearSeriesPoints(targetSeries);
// 添加750°C参考线
// 起点时间0秒温度750°C
// 终点当前最大时间或预设值温度750°C
double xMax = _elapsedSeconds > 0 ? _elapsedSeconds : 600;
AddDataPoint(targetSeries, 0, 750);
AddDataPoint(targetSeries, xMax, 750);
// 设置参考线样式
targetSeries.Brush = new SolidColorBrush(Color.FromRgb(231, 76, 60)); // 红色
if (targetSeries.LineStyle == null)
{
targetSeries.LineStyle = new LineStyle();
}
targetSeries.LineStyle.Thickness = 2;
targetSeries.LineStyle.DashStyle = new DashStyle(new double[] { 4, 2 }, 0);
// 设置参考线标签
targetSeries.DisplayName = "目标温度 (750°C)";
}
catch (Exception ex)
{
MessageBox.Show($"添加参考线时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void InitializeTemperatureSeries()
{
try
{
if (temperatureSeries == null) return;
// 设置温度曲线样式
temperatureSeries.Brush = new SolidColorBrush(Color.FromRgb(52, 152, 219)); // 蓝色
if (temperatureSeries.LineStyle == null)
{
temperatureSeries.LineStyle = new LineStyle();
}
temperatureSeries.LineStyle.Thickness = 2;
// 设置显示名称
temperatureSeries.DisplayName = "炉内温度";
// 设置悬停提示
temperatureSeries.CrosshairLabelPattern = "时间: {A}秒\n温度: {V}°C";
}
catch (Exception ex)
{
MessageBox.Show($"初始化温度曲线时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void AdjustXAxisRange()
{
try
{
if (temperatureChart.Diagram is XYDiagram2D diagram &&
diagram.AxisX is AxisX2D axisX)
// 清空数据
temperatureData.Clear();
_elapsedSeconds = 0;
// 更新显示
txtElapsedTime.Text = "0秒";
UpdateStatistics();
// 初始化定时器
_updateTimer = new DispatcherTimer();
_updateTimer.Interval = TimeSpan.FromSeconds(1);
_updateTimer.Tick += UpdateTimer_Tick;
// 添加初始数据点
AddInitialDataPoint();
// 启动定时器
_updateTimer.Start();
}
catch (Exception ex)
{
MessageBox.Show($"启动数据收集失败: {ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void UpdateTimer_Tick(object sender, EventArgs e)
{
if (_elapsedSeconds >= COLLECTION_DURATION)
{
_updateTimer?.Stop();
return;
}
try
{
// 更新时间
_elapsedSeconds++;
txtElapsedTime.Text = $"{_elapsedSeconds}秒";
// 获取当前温度
if (_getFurnaceTemperature != null)
{
// 获取当前X轴最大值
double currentMaxValue = 600; // 默认值
double currentTemp = _getFurnaceTemperature();
if (axisX.WholeRange != null)
{
try
{
// 直接使用 MinValue 和 MaxValue 属性
currentMaxValue = (double)axisX.WholeRange.MaxValue;
}
catch
{
// 如果获取失败,使用默认值
currentMaxValue = 600;
}
}
// 如果时间超过了当前X轴范围的80%,自动扩展
if (_elapsedSeconds > currentMaxValue * 0.8)
{
// 每次扩展300秒5分钟
double newMax = currentMaxValue + 300;
// 更新 WholeRange
axisX.WholeRange = new DevExpress.Xpf.Charts.Range
{
MinValue = 0,
MaxValue = newMax
};
// 同时更新 VisualRange 以保持显示一致
axisX.VisualRange = new DevExpress.Xpf.Charts.Range
{
MinValue = 0,
MaxValue = newMax
};
// 同时更新参考线
UpdateReferenceLine(newMax);
Debug.WriteLine($"X轴范围已扩展到: 0 - {newMax}秒");
}
// 添加数据点即使温度为0也记录代表没有数据
AddTemperatureData(_elapsedSeconds, currentTemp);
}
}
catch (Exception ex)
{
Debug.WriteLine($"调整X轴范围时出错: {ex.Message}");
Console.WriteLine($"定时器更新失败: {ex.Message}");
}
}
private void UpdateReferenceLine(double xMax)
private void AddInitialDataPoint()
{
try
{
if (targetSeries != null)
if (_getFurnaceTemperature != null)
{
// 清除现有数据点
targetSeries.Points.Clear();
// 添加新的参考线点
AddDataPoint(targetSeries, 0, 750);
AddDataPoint(targetSeries, xMax, 750);
double initialTemp = _getFurnaceTemperature();
AddTemperatureData(0, initialTemp);
}
}
catch (Exception ex)
{
Debug.WriteLine($"更新参考线时出错: {ex.Message}");
Console.WriteLine($"添加初始数据点失败: {ex.Message}");
}
}
private void ClearAllSeries()
{
ClearSeriesPoints(temperatureSeries);
ClearSeriesPoints(targetSeries);
}
private void ClearSeriesPoints(LineSeries2D series)
{
try
{
if (series != null && series.Points != null)
{
series.Points.Clear();
}
}
catch (Exception ex)
{
Debug.WriteLine($"清除数据点时出错: {ex.Message}");
}
}
private void AddDataPoint(LineSeries2D series, double time, double temperature)
{
try
{
if (series == null || series.Points == null)
{
Debug.WriteLine("series或series.Points为空");
return;
}
// 添加数据点
series.Points.Add(new SeriesPoint(time, temperature));
}
catch (Exception ex)
{
Debug.WriteLine($"添加数据点时出错: {ex.Message}");
}
}
private void UpdateStatistics()
{
try
private void UpdateStatistics()
{
if (temperatureData == null || temperatureData.Count == 0)
{
txtMaxTemp.Text = "0°C";
txtAvgTemp.Text = "0°C";
txtCurrentTemp.Text = "0°C";
txtDataPoints.Text = "0";
txtTempDifference.Text = "0°C";
ResetStatistics();
return;
}
double maxTemp = 0;
double sumTemp = 0;
int validPoints = 0;
double currentTemp = 0;
double minTemp = double.MaxValue;
foreach (var point in temperatureData)
try
{
try
{
double temp = point.Item2;
if (temp > maxTemp) maxTemp = temp;
if (temp < minTemp) minTemp = temp;
sumTemp += temp;
validPoints++;
// 最后一个数据点是当前温度
if (point == temperatureData.Last())
{
currentTemp = temp;
}
}
catch
{
// 忽略错误的数据点
}
}
// 获取当前温度(最后一个数据点)
double currentTemp = temperatureData.Last().Y;
if (validPoints > 0)
{
double avgTemp = sumTemp / validPoints;
double temperatureRange = maxTemp - minTemp;
// 计算统计信息
double maxTemp = temperatureData.Max(p => p.Y);
double minTemp = temperatureData.Min(p => p.Y);
double difference = currentTemp - TARGET_TEMPERATURE;
// 更新显示
txtCurrentTemp.Text = $"{currentTemp:F1}°C";
txtMaxTemp.Text = $"{maxTemp:F1}°C";
txtMinTemp.Text = $"{minTemp:F1}°C";
txtAvgTemp.Text = $"{avgTemp:F1}°C";
txtCurrentTemp.Text = $"{currentTemp:F1}°C";
txtDataPoints.Text = validPoints.ToString();
txtTempRange.Text = $"{temperatureRange:F1}°C";
// 显示与目标温度的差值
double difference = currentTemp - 750;
txtDataPoints.Text = temperatureData.Count.ToString();
txtTempDifference.Text = $"{(difference >= 0 ? "+" : "")}{difference:F1}°C";
// 根据差值设置颜色
if (Math.Abs(difference) <= 5)
if (Math.Abs(difference) <= TOLERANCE)
{
txtTempDifference.Foreground = Brushes.Green;
txtTempDifference.Foreground = System.Windows.Media.Brushes.Green;
}
else if (Math.Abs(difference) <= 15)
else if (Math.Abs(difference) <= TOLERANCE * 2)
{
txtTempDifference.Foreground = Brushes.Orange;
txtTempDifference.Foreground = System.Windows.Media.Brushes.Orange;
}
else
{
txtTempDifference.Foreground = Brushes.Red;
txtTempDifference.Foreground = System.Windows.Media.Brushes.Red;
}
}
else
catch (Exception)
{
ResetStatistics();
}
}
catch (Exception ex)
{
Debug.WriteLine($"更新统计信息时出错: {ex.Message}");
ResetStatistics();
}
}
private void ResetStatistics()
{
txtMaxTemp.Text = "0°C";
txtMinTemp.Text = "0°C";
txtAvgTemp.Text = "0°C";
txtCurrentTemp.Text = "0°C";
txtDataPoints.Text = "0";
txtTempRange.Text = "0°C";
txtTempDifference.Text = "0°C";
txtTempDifference.Foreground = Brushes.Black;
}
private void UpdateStatus(string message)
{
try
private void ResetStatistics()
{
if (txtStatus != null)
{
txtStatus.Text = $"{DateTime.Now:HH:mm:ss} - {message}";
}
txtCurrentTemp.Text = "0°C";
txtMaxTemp.Text = "0°C";
txtMinTemp.Text = "0°C";
txtDataPoints.Text = "0";
txtTempDifference.Text = "0°C";
txtTempDifference.Foreground = System.Windows.Media.Brushes.Black;
}
catch (Exception ex)
{
Debug.WriteLine($"更新状态时出错: {ex.Message}");
}
}
#region
#region
private void Window_Loaded(object sender, RoutedEventArgs e)
{
StartDataCollection();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_updateTimer?.Stop();
_updateTimer = null;
}
private void btnClearData_Click(object sender, RoutedEventArgs e)
{
try
var result = MessageBox.Show("确定要清空所有数据吗?", "确认",
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
var result = MessageBox.Show("确定要清空所有数据吗?", "确认",
MessageBoxButton.YesNo, MessageBoxImage.Question);
// 停止定时器
_updateTimer?.Stop();
if (result == MessageBoxResult.Yes)
{
// 停止定时器
StopDataCollection();
// 清空数据
temperatureData.Clear();
_elapsedSeconds = 0;
// 清空数据
temperatureData.Clear();
ClearAllSeries();
// 重置统计
ResetStatistics();
txtElapsedTime.Text = "0秒";
// 重置统计信息
ResetStatistics();
// 重置时间和按钮状态
_elapsedSeconds = 0;
txtElapsedTime.Text = "0秒";
UpdateStatus("数据已清空");
MessageBox.Show("数据已清空", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"清空数据时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
// 重新开始
_updateTimer?.Start();
}
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
try
{
StopDataCollection();
_updateTimer?.Stop();
this.Close();
}
catch (Exception ex)
{
MessageBox.Show($"关闭窗口时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
#region
// 窗口关闭时清理资源
protected override void OnClosed(EventArgs e)
{
try
{
StopDataCollection();
temperatureData.Clear();
_getFurnaceTemperature = null;
}
catch
{
// 忽略清理时的错误
}
base.OnClosed(e);
}
#endregion
}
}

View File

@@ -149,6 +149,22 @@
</StackPanel>
</GroupBox>
<GroupBox Header="炉内温度设定" Padding="15"
BorderBrush="#4A90E2" BorderThickness="1"
Background="White" FontSize="16" FontWeight="SemiBold"
Margin="0,20,0,0">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
<TextBlock Text="炉内温度设定(℃)" Width="150" VerticalAlignment="Center"
FontSize="15" FontWeight="Normal"/>
<TextBox Name="txtFurnaceTempSet" Width="120" Height="28" Padding="5,3,5,3"
FontSize="15" VerticalContentAlignment="Center"
BorderBrush="#CCCCCC" BorderThickness="1" GotFocus="txtFurnaceTempSet_GotFocus"/>
</StackPanel>
</StackPanel>
</GroupBox>
<Border Margin="0,30,0,0" Padding="15"
Background="Transparent">
<Button x:Name="btnReturn"

View File

@@ -143,15 +143,15 @@ namespace 建材不燃性试验炉
ReadAndUpdateRegister(306, 2, value =>
Dispatcher.Invoke(() => txtOutputVoltageAnalog.Text = value.ToString("F2")));
// 炉内温度相关 (D1226, D1230)
ReadAndUpdateRegister(1226, 2, value =>
// 炉内温度相关 (D1228, D1230)
ReadAndUpdateRegister(1228, 2, value =>
Dispatcher.Invoke(() => txtFurnaceTempCoefficient.Text = value.ToString("F2")));
ReadAndUpdateRegister(1230, 2, value =>
Dispatcher.Invoke(() => txtFurnaceTempDisplay.Text = value.ToString("F2")));
// 样品内温度相关 (D1276, D1280)
ReadAndUpdateRegister(1276, 2, value =>
ReadAndUpdateRegister(1278, 2, value =>
Dispatcher.Invoke(() => txtSampleInnerTempCoefficient.Text = value.ToString("F2")));
ReadAndUpdateRegister(1280, 2, value =>
@@ -177,6 +177,10 @@ namespace 建材不燃性试验炉
ReadAndUpdateRegister(1430, 2, value =>
Dispatcher.Invoke(() => txtCurrentDisplay.Text = value.ToString("F2")));
//炉内温度设定
ReadAndUpdateRegister(350, 2, value =>
Dispatcher.Invoke(() => txtFurnaceTempSet.Text = value.ToString("F2")));
}
catch (InvalidOperationException ioex)
{
@@ -265,24 +269,24 @@ namespace 建材不燃性试验炉
}, TaskScheduler.FromCurrentSynchronizationContext());
}
//炉内温度系数D1226
//炉内温度系数D1228
private void txtFurnaceTempCoefficient_GotFocus(object sender, RoutedEventArgs e)
{
_isManualInput = true;
Task.Delay(100).ContinueWith(_ =>
{
ma?.WriteToPLCForNew(txtFurnaceTempCoefficient.Text.Trim(), 1226, Function.DataType.);
ma?.WriteToPLCForNew(txtFurnaceTempCoefficient.Text.Trim(), 1228, Function.DataType.);
_isManualInput = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
//样品内温度系数D1276
//样品内温度系数D1278
private void txtSampleInnerTempCoefficient_GotFocus(object sender, RoutedEventArgs e)
{
_isManualInput = true;
Task.Delay(100).ContinueWith(_ =>
{
ma?.WriteToPLCForNew(txtSampleInnerTempCoefficient.Text.Trim(), 1276, Function.DataType.);
ma?.WriteToPLCForNew(txtSampleInnerTempCoefficient.Text.Trim(), 1278, Function.DataType.);
_isManualInput = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
@@ -320,6 +324,19 @@ namespace 建材不燃性试验炉
}, TaskScheduler.FromCurrentSynchronizationContext());
}
//炉内温度设定
private void txtFurnaceTempSet_GotFocus(object sender, RoutedEventArgs e)
{
_isManualInput = true;
Task.Delay(100).ContinueWith(_ =>
{
ma?.WriteToPLCForNew(txtFurnaceTempSet.Text.Trim(), 350, Function.DataType.);
_isManualInput = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
#endregion
@@ -363,6 +380,8 @@ namespace 建材不燃性试验炉
windowInstance.Show();
}
#endregion
}

View File

@@ -192,8 +192,8 @@
<!-- 主内容区域 -->
<Grid Margin="20,80,20,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="0.8*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<!-- 左侧控制面板 -->
@@ -451,7 +451,7 @@
Height="35"
FontSize="13"
Background="#2980B9"
Margin="105,0,0,4"
Margin="5,0,0,4"
Click="btnStartBalanceCheck_Click"/>
<!-- 第二行:两个按钮水平排列 -->

View File

@@ -12,8 +12,10 @@
<PackageReference Include="DevExpress.Wpf.Core" Version="22.2.*-*" />
<PackageReference Include="devexpress.wpf.layoutcontrol" Version="22.2.3" />
<PackageReference Include="DevExpress.Wpf.Themes.All" Version="22.2.*-*" />
<PackageReference Include="DotNetProjects.WpfToolkit.Input" Version="6.1.94" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="NModbus" Version="3.0.81" />
<PackageReference Include="OxyPlot.Wpf" Version="2.2.0" />
<PackageReference Include="SunnyUI" Version="3.9.1" />
</ItemGroup>