暂时提交,等客户那边测试
This commit is contained in:
13
FrictionDataPoint.cs
Normal file
13
FrictionDataPoint.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
// FrictionDataPoint.cs
|
||||
using System;
|
||||
|
||||
namespace PLCDataMonitor
|
||||
{
|
||||
public class FrictionDataPoint
|
||||
{
|
||||
public DateTime Timestamp { get; set; }
|
||||
public double Friction1 { get; set; }
|
||||
public double Friction2 { get; set; }
|
||||
public bool IsPaused { get; set; }
|
||||
}
|
||||
}
|
||||
161
HomePage.xaml
161
HomePage.xaml
@@ -5,36 +5,34 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:PLCDataMonitor"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="480" d:DesignWidth="960"
|
||||
d:DesignHeight="700" d:DesignWidth="1000"
|
||||
FontFamily="Microsoft YaHei">
|
||||
|
||||
<!-- 最外层网格,明确划分两行:顶部连接区和数据区 -->
|
||||
<Grid Margin="10">
|
||||
<Grid Margin="5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- 顶部区域自动适应高度 -->
|
||||
<RowDefinition Height="*"/>
|
||||
<!-- 数据区域占剩余空间 -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 顶部PLC连接区域 -->
|
||||
<!--<Border Grid.Row="0" Background="White" Margin="0,0,0,10" Padding="10" Height="60">
|
||||
-->
|
||||
<!-- 这里可以放PLC连接控件,示例: -->
|
||||
<!--
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="PLC连接状态:" Margin="5"/>
|
||||
<TextBlock Text="未连接" Foreground="Red" Margin="5"/>
|
||||
<Button Content="连接" Margin="10,0" Padding="5,2"/>
|
||||
<!-- 顶部PLC连接区域(尽量紧凑) -->
|
||||
<Border Grid.Row="0" Background="White" Margin="0,0,0,5" Padding="5"
|
||||
BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Ellipse x:Name="StatusEllipse" Width="16" Height="16"
|
||||
Fill="{Binding StatusColor, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
|
||||
Margin="0,0,5,0"/>
|
||||
<TextBlock x:Name="StatusText"
|
||||
Text="{Binding ConnectionStatus, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
|
||||
FontSize="12" Foreground="#333" Margin="0,0,15,0"/>
|
||||
<Button x:Name="PauseButton" Content="暂停" Width="70" Height="28" Margin="5,0"
|
||||
Background="#E67E22" Foreground="White" BorderThickness="0" Click="PauseButton_Click"/>
|
||||
<Button x:Name="ResumeButton" Content="继续" Width="70" Height="28" Margin="5,0"
|
||||
Background="#27AE60" Foreground="White" BorderThickness="0" Click="ResumeButton_Click"/>
|
||||
</StackPanel>
|
||||
</Border>-->
|
||||
</Border>
|
||||
|
||||
<!-- 数据显示区域 -->
|
||||
<!-- 数据显示区域:明确设置5行,每行高度平均分配,并强制所有行高度一致 -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
@@ -42,110 +40,109 @@
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 工位1压力 -->
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="工位1压力" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="TbStation1Pressure" Text="0.0" FontSize="28" FontWeight="Bold" Foreground="#3498DB"/>
|
||||
<TextBlock Text=" N" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<Border Grid.Row="0" Grid.Column="0" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="工位1压力" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="TbStation1Pressure" Text="0.0" FontSize="24" FontWeight="Bold" Foreground="#3498DB"/>
|
||||
<TextBlock Text=" N" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 工位2压力 -->
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Grid.Column="1">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="工位2压力" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="TbStation2Pressure" Text="0.0" FontSize="28" FontWeight="Bold" Foreground="#E74C3C"/>
|
||||
<TextBlock Text=" N" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<Border Grid.Row="0" Grid.Column="1" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="工位2压力" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="TbStation2Pressure" Text="0.0" FontSize="24" FontWeight="Bold" Foreground="#E74C3C"/>
|
||||
<TextBlock Text=" N" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 摩擦力1 -->
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Grid.Row="1">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="摩擦力1" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="TbFriction1" Text="0.0" FontSize="28" FontWeight="Bold" Foreground="#F39C12"/>
|
||||
<TextBlock Text=" N" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<Border Grid.Row="1" Grid.Column="0" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="摩擦力1" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="TbFriction1" Text="0.0" FontSize="24" FontWeight="Bold" Foreground="#F39C12"/>
|
||||
<TextBlock Text=" N" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 摩擦力2 -->
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Grid.Row="1" Grid.Column="1">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="摩擦力2" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="TbFriction2" Text="0.0" FontSize="28" FontWeight="Bold" Foreground="#9B59B6"/>
|
||||
<TextBlock Text=" N" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<Border Grid.Row="1" Grid.Column="1" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="摩擦力2" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="TbFriction2" Text="0.0" FontSize="24" FontWeight="Bold" Foreground="#9B59B6"/>
|
||||
<TextBlock Text=" N" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 设定次数 -->
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Grid.Row="2">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="设定次数" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="txtcount1" Text="0" FontSize="28" FontWeight="Bold" Foreground="#F39C12"/>
|
||||
<TextBlock Text=" 次" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<Border Grid.Row="2" Grid.Column="0" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="设定次数" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="txtcount1" Text="0" FontSize="24" FontWeight="Bold" Foreground="#F39C12"/>
|
||||
<TextBlock Text=" 次" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 实际次数 -->
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Grid.Row="2" Grid.Column="1">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="实际次数" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="txtcount2" Text="0" FontSize="28" FontWeight="Bold" Foreground="#9B59B6"/>
|
||||
<TextBlock Text=" 次" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<Border Grid.Row="2" Grid.Column="1" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="实际次数" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="txtcount2" Text="0" FontSize="24" FontWeight="Bold" Foreground="#9B59B6"/>
|
||||
<TextBlock Text=" 次" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 设定次数 -->
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Grid.Row="3">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="温度1" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="txtT1" Text="0" FontSize="28" FontWeight="Bold" Foreground="#F39C12"/>
|
||||
<TextBlock Text=" ℃" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<!-- 温度1 -->
|
||||
<Border Grid.Row="3" Grid.Column="0" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="温度1" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="txtT1" Text="0" FontSize="24" FontWeight="Bold" Foreground="#F39C12"/>
|
||||
<TextBlock Text=" ℃" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 实际次数 -->
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Grid.Row="3" Grid.Column="1">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="温度2" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="txtT2" Text="0" FontSize="28" FontWeight="Bold" Foreground="#9B59B6"/>
|
||||
<TextBlock Text=" ℃" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<!-- 温度2 -->
|
||||
<Border Grid.Row="3" Grid.Column="1" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="温度2" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="txtT2" Text="0" FontSize="24" FontWeight="Bold" Foreground="#9B59B6"/>
|
||||
<TextBlock Text=" ℃" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Background="White" Margin="8" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4" Grid.Row="4" Grid.ColumnSpan="2">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="距离" FontSize="14" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
|
||||
<TextBlock x:Name="txtDistince" Text="0" FontSize="28" FontWeight="Bold" Foreground="#F39C12"/>
|
||||
<TextBlock Text=" m" FontSize="16" Foreground="#666" Margin="3,5,0,0"/>
|
||||
<!-- 距离(跨两列,确保行高与其他行一致) -->
|
||||
<Border Grid.Row="4" Grid.ColumnSpan="2" Background="White" Margin="3" BorderThickness="1" BorderBrush="#E0E0E0" CornerRadius="4">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2">
|
||||
<TextBlock Text="距离" FontSize="12" FontWeight="Bold" Foreground="#333"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||||
<TextBlock x:Name="txtDistince" Text="0" FontSize="24" FontWeight="Bold" Foreground="#F39C12"/>
|
||||
<TextBlock Text=" m" FontSize="14" Foreground="#666" Margin="3,4,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -14,6 +14,21 @@ namespace PLCDataMonitor
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
||||
// 定义暂停/继续事件
|
||||
public event EventHandler PauseRequested;
|
||||
public event EventHandler ResumeRequested;
|
||||
|
||||
private void PauseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PauseRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void ResumeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ResumeRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
// 更新数据显示
|
||||
public void UpdateData(string pressure1, string pressure2, string friction1, string friction2, int count1, int count2, string t1, string t2, string Distince)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,31 @@ namespace PLCDataMonitor
|
||||
private TcpClient _tcpClient;
|
||||
private IModbusMaster _modbusMaster;
|
||||
|
||||
|
||||
// PLC 地址(请根据实际PLC修改!)
|
||||
private const int PauseControlCoil = 1000; // 写入 true=暂停,false=继续(线圈地址)
|
||||
private const int PauseStatusCoil = 1001; // 读取当前暂停状态(线圈地址)
|
||||
|
||||
|
||||
// 记录暂停时间段(用于导出过滤)
|
||||
private List<(DateTime start, DateTime end)> _pausePeriods = new List<(DateTime, DateTime)>();
|
||||
private bool _lastPausedState = false; // 上一次读取的暂停状态
|
||||
private DateTime _pauseStartTime; // 本次暂停开始时间
|
||||
|
||||
public List<(DateTime start, DateTime end)> PausePeriods => _pausePeriods;
|
||||
|
||||
|
||||
|
||||
|
||||
// 添加清空方法
|
||||
public void ClearPausePeriods()
|
||||
{
|
||||
_pausePeriods.Clear();
|
||||
_lastPausedState = false;
|
||||
_pauseStartTime = default;
|
||||
}
|
||||
|
||||
|
||||
// 连接状态属性
|
||||
public Brush StatusColor
|
||||
{
|
||||
@@ -69,6 +94,12 @@ namespace PLCDataMonitor
|
||||
|
||||
_homePage = new HomePage();
|
||||
|
||||
|
||||
// 订阅暂停/继续事件
|
||||
_homePage.PauseRequested += (s, e) => SendPauseCommand();
|
||||
_homePage.ResumeRequested += (s, e) => SendResumeCommand();
|
||||
|
||||
|
||||
// 关键修改:创建报表页时传入共享队列
|
||||
_reportPage = new ReportPage();
|
||||
_reportPage.CurveDataQueue = _sharedCurveDataQueue;
|
||||
@@ -234,49 +265,49 @@ namespace PLCDataMonitor
|
||||
{
|
||||
|
||||
|
||||
//int i = 0;
|
||||
//while (i < 100)
|
||||
//{
|
||||
// // 添加随机测试数据
|
||||
// Random random = new Random();
|
||||
int i = 0;
|
||||
while (i < 100)
|
||||
{
|
||||
// 添加随机测试数据
|
||||
Random random = new Random();
|
||||
|
||||
// // 生成随机摩擦力值(包含负值)
|
||||
// double friction1 = random.Next(-200, 200);
|
||||
// double friction2 = random.Next(-200, 200);
|
||||
// 生成随机摩擦力值(包含负值)
|
||||
double friction1 = random.Next(-200, 200);
|
||||
double friction2 = random.Next(-200, 200);
|
||||
|
||||
// // 创建数据点(时间戳相同,确保数据一致性)
|
||||
// var dataPoint = new Tuple<DateTime, double, double>(
|
||||
// DateTime.Now,
|
||||
// friction1,
|
||||
// friction2
|
||||
// );
|
||||
// 创建数据点(时间戳相同,确保数据一致性)
|
||||
var dataPoint = new Tuple<DateTime, double, double>(
|
||||
DateTime.Now,
|
||||
friction1,
|
||||
friction2
|
||||
);
|
||||
|
||||
// // 关键修改:同时添加到两个地方
|
||||
// // 1. 添加到共享曲线数据队列(曲线页面和报表页面共享)
|
||||
// _sharedCurveDataQueue.Enqueue(dataPoint);
|
||||
// 关键修改:同时添加到两个地方
|
||||
// 1. 添加到共享曲线数据队列(曲线页面和报表页面共享)
|
||||
_sharedCurveDataQueue.Enqueue(dataPoint);
|
||||
|
||||
// // 2. 添加到报表数据
|
||||
// _reportPage.AddReportRecord(new ReportData
|
||||
// {
|
||||
// D = random.Next(1, 100),
|
||||
// Friction1 = friction1.ToString("F1"), // 使用相同的摩擦力值
|
||||
// Friction2 = friction2.ToString("F1"), // 使用相同的摩擦力值
|
||||
// Id = (i + 11).ToString(),
|
||||
// Pressure1 = random.Next(1, 10).ToString(),
|
||||
// Pressure2 = random.Next(2, 12).ToString(),
|
||||
// t1 = random.Next(20, 40),
|
||||
// t2 = random.Next(25, 45),
|
||||
// Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") // 使用相同的时间格式
|
||||
// });
|
||||
// 2. 添加到报表数据
|
||||
_reportPage.AddReportRecord(new ReportData
|
||||
{
|
||||
D = random.Next(1, 100),
|
||||
Friction1 = friction1.ToString("F1"), // 使用相同的摩擦力值
|
||||
Friction2 = friction2.ToString("F1"), // 使用相同的摩擦力值
|
||||
Id = (i + 11).ToString(),
|
||||
Pressure1 = random.Next(1, 10).ToString(),
|
||||
Pressure2 = random.Next(2, 12).ToString(),
|
||||
t1 = random.Next(20, 40),
|
||||
t2 = random.Next(25, 45),
|
||||
Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") // 使用相同的时间格式
|
||||
});
|
||||
|
||||
// // 3. 通知曲线页面更新(这个调用只是触发重绘,数据已经在队列中)
|
||||
// _curvePage.AddFrictionData(friction1, friction2);
|
||||
// 3. 通知曲线页面更新(这个调用只是触发重绘,数据已经在队列中)
|
||||
_curvePage.AddFrictionData(friction1, friction2);
|
||||
|
||||
// // 为了让曲线更平滑,可以少量数据生成
|
||||
// Thread.Sleep(100); // 每100ms一个数据点
|
||||
// 为了让曲线更平滑,可以少量数据生成
|
||||
Thread.Sleep(100); // 每100ms一个数据点
|
||||
|
||||
// i++;
|
||||
//}
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -295,6 +326,37 @@ namespace PLCDataMonitor
|
||||
// 读取所有数据(包含新增的次数、最大摩擦力)
|
||||
var data = ReadModbusData();
|
||||
|
||||
// 读取暂停状态
|
||||
bool isPaused = false;
|
||||
try
|
||||
{
|
||||
bool[] pauseStatus = _modbusMaster?.ReadCoils(0x01, (ushort)PauseStatusCoil, 1);
|
||||
isPaused = pauseStatus != null && pauseStatus.Length > 0 && pauseStatus[0];
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 读取失败时默认 false
|
||||
}
|
||||
|
||||
|
||||
// --- 记录暂停时间段 ---
|
||||
if (isPaused != _lastPausedState)
|
||||
{
|
||||
if (isPaused) // 进入暂停
|
||||
{
|
||||
_pauseStartTime = DateTime.Now;
|
||||
}
|
||||
else // 退出暂停
|
||||
{
|
||||
if (_pauseStartTime != default)
|
||||
{
|
||||
_pausePeriods.Add((_pauseStartTime, DateTime.Now));
|
||||
}
|
||||
}
|
||||
_lastPausedState = isPaused;
|
||||
}
|
||||
// --- 结束记录暂停时间段 ---
|
||||
|
||||
var pressure1 = data.pressure1;
|
||||
var pressure2 = data.pressure2;
|
||||
var friction1 = data.friction1;
|
||||
@@ -479,5 +541,37 @@ namespace PLCDataMonitor
|
||||
DisconnectFromPLC();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void SendPauseCommand()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_isConnected && _modbusMaster != null)
|
||||
{
|
||||
_modbusMaster.WriteSingleCoil(0x01, (ushort)PauseControlCoil, true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"发送暂停命令失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendResumeCommand()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_isConnected && _modbusMaster != null)
|
||||
{
|
||||
_modbusMaster.WriteSingleCoil(0x01, (ushort)PauseControlCoil, false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"发送继续命令失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,28 @@
|
||||
using Microsoft.Win32;
|
||||
using NPOI.SS.UserModel;
|
||||
using NPOI.SS.UserModel.Charts;
|
||||
using NPOI.SS.Util;
|
||||
using NPOI.XSSF.UserModel;
|
||||
using OfficeOpenXml;
|
||||
using OfficeOpenXml.Drawing.Chart;
|
||||
using OfficeOpenXml.Style;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
|
||||
|
||||
namespace PLCDataMonitor
|
||||
{
|
||||
public partial class ReportPage : UserControl
|
||||
{
|
||||
public ObservableCollection<ReportData> ReportList { get; set; }
|
||||
|
||||
// 关键修改:使用属性注入,而不是自己创建
|
||||
// 从主窗口注入的共享队列
|
||||
public System.Collections.Concurrent.ConcurrentQueue<Tuple<DateTime, double, double>> CurveDataQueue { get; set; }
|
||||
|
||||
public ReportPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
ReportList = new ObservableCollection<ReportData>();
|
||||
// 不在这里创建队列,等待主窗口注入
|
||||
ReportDataGrid.ItemsSource = ReportList;
|
||||
}
|
||||
|
||||
@@ -38,13 +34,12 @@ namespace PLCDataMonitor
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void ExportExcelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ReportDataGrid.ItemsSource is IEnumerable<ReportData> reportData &&
|
||||
CurveDataQueue != null && CurveDataQueue.Count > 0)
|
||||
CurveDataQueue != null && CurveDataQueue.Count > 0)
|
||||
{
|
||||
Microsoft.Win32.SaveFileDialog saveFileDialog = new Microsoft.Win32.SaveFileDialog
|
||||
SaveFileDialog saveFileDialog = new SaveFileDialog
|
||||
{
|
||||
Filter = "Excel 文件 (*.xlsx)|*.xlsx",
|
||||
FileName = $"滑动摩擦磨损试验报告_{DateTime.Now:yyyyMMddHHmm}.xlsx"
|
||||
@@ -54,19 +49,18 @@ namespace PLCDataMonitor
|
||||
{
|
||||
try
|
||||
{
|
||||
// 设置EPPlus许可证上下文
|
||||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||
|
||||
using (ExcelPackage excelPackage = new ExcelPackage())
|
||||
{
|
||||
// ====== 第一个Sheet:按照图片模板的报表 ======
|
||||
// ====== 第一个Sheet:报表 ======
|
||||
ExcelWorksheet reportSheet = excelPackage.Workbook.Worksheets.Add("滑动摩擦磨损试验报告");
|
||||
|
||||
// 设置A4纸大小
|
||||
// 设置页面
|
||||
reportSheet.PrinterSettings.PaperSize = ePaperSize.A4;
|
||||
reportSheet.PrinterSettings.Orientation = eOrientation.Portrait;
|
||||
|
||||
// 设置标题
|
||||
// 标题
|
||||
reportSheet.Cells["A1:E1"].Merge = true;
|
||||
reportSheet.Cells["A1"].Value = "滑动摩擦磨损试验报告";
|
||||
reportSheet.Cells["A1"].Style.Font.Size = 14;
|
||||
@@ -75,23 +69,20 @@ namespace PLCDataMonitor
|
||||
reportSheet.Cells["A1"].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
|
||||
reportSheet.Row(1).Height = 30;
|
||||
|
||||
// 报告编号和试验日期
|
||||
reportSheet.Cells["A2"].Value = "报告编号:";
|
||||
reportSheet.Cells["D2:E2"].Merge = true;
|
||||
reportSheet.Cells["D2"].Value = "试验日期:______年______月______日";
|
||||
reportSheet.Row(2).Height = 25;
|
||||
|
||||
// 空行
|
||||
reportSheet.Row(3).Height = 10;
|
||||
|
||||
// 创建表格标题行
|
||||
// 表头
|
||||
reportSheet.Cells["A4"].Value = "序号";
|
||||
reportSheet.Cells["B4"].Value = "项目名称";
|
||||
reportSheet.Cells["C4"].Value = "工位1";
|
||||
reportSheet.Cells["D4"].Value = "工位2";
|
||||
reportSheet.Cells["E4"].Value = "备注/说明";
|
||||
|
||||
// 设置标题行样式
|
||||
for (int col = 1; col <= 5; col++)
|
||||
{
|
||||
reportSheet.Cells[4, col].Style.Font.Bold = true;
|
||||
@@ -105,25 +96,65 @@ namespace PLCDataMonitor
|
||||
}
|
||||
reportSheet.Row(4).Height = 25;
|
||||
|
||||
// 设置表格数据
|
||||
int currentRow = 5;
|
||||
var timesdata = new List<Tuple<DateTime, double, double>>(CurveDataQueue);
|
||||
var timedata = timesdata.Select(s => s.Item1).ToList();
|
||||
// ========== 处理曲线数据,过滤暂停段 ==========
|
||||
var mainWindow = (MainWindow)Application.Current.MainWindow;
|
||||
var pausePeriods = mainWindow.PausePeriods; // 假设 MainWindow 有公共属性
|
||||
|
||||
// 获取所有原始数据并按时间排序
|
||||
var allRawData = CurveDataQueue.ToList().OrderBy(d => d.Item1).ToList();
|
||||
if (allRawData.Count == 0)
|
||||
{
|
||||
MessageBox.Show("没有曲线数据可导出!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 过滤掉暂停期间的点
|
||||
var filteredData = allRawData.Where(point =>
|
||||
!pausePeriods.Any(p => point.Item1 >= p.start && point.Item1 <= p.end)
|
||||
).ToList();
|
||||
|
||||
if (filteredData.Count == 0)
|
||||
{
|
||||
MessageBox.Show("有效数据(非暂停期间)为空,无法导出!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 重建连续时间轴(秒)
|
||||
List<double> timeAxis = new List<double>();
|
||||
DateTime firstValid = filteredData.First().Item1;
|
||||
double accumulated = 0;
|
||||
for (int i = 0; i < filteredData.Count; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
timeAxis.Add(0);
|
||||
else
|
||||
{
|
||||
accumulated += (filteredData[i].Item1 - filteredData[i - 1].Item1).TotalSeconds;
|
||||
timeAxis.Add(accumulated);
|
||||
}
|
||||
}
|
||||
double totalValidSeconds = accumulated; // 有效总时长
|
||||
DateTime effectiveStartTime = firstValid;
|
||||
DateTime effectiveEndTime = filteredData.Last().Item1;
|
||||
|
||||
// 计算最大摩擦力(基于过滤后的数据)
|
||||
double maxFriction1 = filteredData.Max(x => Math.Abs(x.Item2));
|
||||
double maxFriction2 = filteredData.Max(x => Math.Abs(x.Item3));
|
||||
|
||||
// ========== 填写报表(第一个Sheet) ==========
|
||||
int currentRow = 5;
|
||||
|
||||
var startTime = timedata.Min();
|
||||
var endTime = timedata.Max();
|
||||
// 序号1:油管型号
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "1", "油管型号", OilPipeType1.Text, OilPipeType2.Text, "");
|
||||
|
||||
// 序号2:滑块型号
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "2", "样件型号", SliderType1.Text, SliderType2.Text, "");
|
||||
|
||||
// 序号3:试验开始时间
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "3", "试验开始时间", startTime.ToString(), "", "试验开始的时间点");
|
||||
|
||||
// 序号4:试验停止时间
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "4", "试验停止时间", endTime.ToString(), "", "试验结束的时间点");
|
||||
// 序号3:试验开始时间(有效)
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "3", "试验开始时间", effectiveStartTime.ToString("yyyy-MM-dd HH:mm:ss"), "", "试验开始的时间点");
|
||||
|
||||
// 序号4:试验停止时间(有效)
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "4", "试验停止时间", effectiveEndTime.ToString("yyyy-MM-dd HH:mm:ss"), "", "试验结束的时间点");
|
||||
|
||||
// 序号5:样件试验前壁厚
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "5", "样件1试验前壁厚(mm)", PreTestThickness.Text, PreTestThickness3.Text, "样件试验前壁厚(mm)");
|
||||
@@ -134,90 +165,44 @@ namespace PLCDataMonitor
|
||||
// 序号7:油管磨损深度
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "7", "油管1磨损深度(mm)", PipeWearDepth.Text, PipeWearDepth3.Text, "油管磨损深度(mm)");
|
||||
|
||||
|
||||
|
||||
//// 序号8:样件试验前壁厚
|
||||
//CreateFormattedRowEPPlus(reportSheet, currentRow++, "8", "样件2试验前壁厚(mm)", PreTestThickness3.Text, "", "样件2试验前壁厚(mm)");
|
||||
|
||||
//// 序号9:样件试验后壁厚
|
||||
//CreateFormattedRowEPPlus(reportSheet, currentRow++, "9", "样件2试验后壁厚(mm)", PostTestThickness3.Text, "", "样件2试验后壁厚(mm)");
|
||||
|
||||
//// 序号10:油管磨损深度
|
||||
//CreateFormattedRowEPPlus(reportSheet, currentRow++, "10", "油管2磨损深度(mm)", PipeWearDepth3.Text, "", "油管2磨损深度(mm)");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 序号5:样件试验前壁厚
|
||||
// 序号8:样件1试验前重量(g)
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "8", "样件1试验前重量(g)", PreTestThickness4.Text, PreTestThickness2.Text, "样件试验前重量(g)");
|
||||
|
||||
// 序号6:样件试验后壁厚
|
||||
// 序号9:样件1试验后重量(g)
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "9", "样件1试验后重量(g)", PostTestThickness4.Text, PostTestThickness2.Text, "样件试验后重量(g)");
|
||||
|
||||
// 序号7:油管磨损深度
|
||||
// 序号10:样件1磨损量(g)
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "10", "样件1磨损量(g)", PipeWearDepth4.Text, PipeWearDepth2.Text, "样件磨损量(g)");
|
||||
|
||||
|
||||
|
||||
//// 序号8:样件试验前壁厚
|
||||
//CreateFormattedRowEPPlus(reportSheet, currentRow++, "14", "样件2试验前重量(g)", PreTestThickness2.Text, "", "样件2试验前壁厚(g)");
|
||||
|
||||
//// 序号9:样件试验后壁厚
|
||||
//CreateFormattedRowEPPlus(reportSheet, currentRow++, "15", "样件2试验后重量(g)", PostTestThickness2.Text, "", "样件2试验后壁厚(g)");
|
||||
|
||||
//// 序号10:油管磨损深度
|
||||
//CreateFormattedRowEPPlus(reportSheet, currentRow++, "16", "样件2磨损量(g)", PipeWearDepth2.Text, "", "油管2磨损深度(g)");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 报表数据(温度、距离、压力)来自 reportData
|
||||
if (reportData.Count() > 0)
|
||||
{
|
||||
reportData = reportData.OrderBy(s => s.Time).ToList();
|
||||
}
|
||||
|
||||
// 序号8:水箱温度
|
||||
// 序号11:水箱温度
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "11", "水箱温度(℃)",
|
||||
reportData.Last().t1?.ToString("F1"),
|
||||
reportData.Last().t2?.ToString("F1"),
|
||||
"实时监测温度");
|
||||
|
||||
// 序号9:测试实际距离
|
||||
// 序号12:测试实际距离
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "12", "测试实际距离(m)",
|
||||
reportData.Last().D?.ToString(),
|
||||
reportData.Last().D?.ToString(),
|
||||
"累计滑动行程");
|
||||
|
||||
// 序号10:压力
|
||||
// 序号13:压力
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "13", "压力(N)",
|
||||
reportData.Last().Pressure1.ToString(),
|
||||
reportData.Last().Pressure2.ToString(),
|
||||
"施加载荷");
|
||||
|
||||
var allData = new List<Tuple<DateTime, double, double>>(CurveDataQueue);
|
||||
var maxFriction1 = allData.Select(x => Math.Abs(x.Item2)).Max(x => x);
|
||||
var maxFriction2 = allData.Select(x => Math.Abs(x.Item3)).Max(x => x);
|
||||
// 序号11:最大摩擦力
|
||||
// 序号14:最大摩擦力(基于过滤后的有效数据)
|
||||
CreateFormattedRowEPPlus(reportSheet, currentRow++, "14", "最大摩擦力(N)",
|
||||
maxFriction1.ToString("F1"),
|
||||
maxFriction1.ToString("F1"),
|
||||
maxFriction2.ToString("F1"),
|
||||
"测试过程中最大值");
|
||||
|
||||
//// 序号12:磨损量
|
||||
//CreateFormattedRowEPPlus(reportSheet, currentRow++, "21", "磨损量(g)", "", "", "试验前后质量差");
|
||||
"测试过程中最大值(剔除暂停段)");
|
||||
|
||||
// 空行
|
||||
currentRow++;
|
||||
@@ -226,29 +211,23 @@ namespace PLCDataMonitor
|
||||
reportSheet.Cells[currentRow, 1].Value = "备注:";
|
||||
reportSheet.Cells[currentRow, 1].Style.Font.Bold = true;
|
||||
|
||||
// ====== 修复1:调整备注行高度 ======
|
||||
// 先合并备注内容的单元格(A到E列)
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int remarkRow = currentRow + 1 + i;
|
||||
reportSheet.Cells[remarkRow, 1, remarkRow, 5].Merge = true; // 合并A到E列
|
||||
reportSheet.Cells[remarkRow, 1, remarkRow, 5].Merge = true;
|
||||
reportSheet.Cells[remarkRow, 1].Style.WrapText = true;
|
||||
reportSheet.Cells[remarkRow, 1].Style.VerticalAlignment = ExcelVerticalAlignment.Top;
|
||||
reportSheet.Row(remarkRow).Height = 40; // 设置足够的高度
|
||||
reportSheet.Row(remarkRow).Height = 40;
|
||||
}
|
||||
|
||||
// 备注内容
|
||||
reportSheet.Cells[currentRow + 1, 1].Value = "1. 所有数据应在试验结束后立即记录,确保真实、准确。";
|
||||
reportSheet.Cells[currentRow + 2, 1].Value = "2. 壁厚和磨损深度需使用精密测量仪器测量。";
|
||||
reportSheet.Cells[currentRow + 3, 1].Value = "3. 若试验过程中出现异常(如异响、过热、泄漏等),请在备注中说明。";
|
||||
reportSheet.Cells[currentRow + 4, 1].Value = "4. 磨损量需通过精密电子天平称重(试验前后)。";
|
||||
|
||||
currentRow += 5; // 移动到备注之后
|
||||
|
||||
// 空行
|
||||
currentRow += 5;
|
||||
currentRow += 2;
|
||||
|
||||
// 签名行
|
||||
reportSheet.Cells[currentRow, 1].Value = "测试人签字:______";
|
||||
reportSheet.Cells[currentRow, 4].Value = "审核人签字:______";
|
||||
reportSheet.Row(currentRow).Height = 30;
|
||||
@@ -257,198 +236,102 @@ namespace PLCDataMonitor
|
||||
reportSheet.Cells[currentRow, 1].Value = "报告日期:______年______月______日";
|
||||
reportSheet.Row(currentRow).Height = 25;
|
||||
|
||||
// 设置列宽
|
||||
reportSheet.Column(1).Width = 23; // A列
|
||||
reportSheet.Column(2).Width = 30; // B列
|
||||
reportSheet.Column(3).Width = 20; // C列
|
||||
reportSheet.Column(4).Width = 20; // D列
|
||||
reportSheet.Column(5).Width = 20; // E列
|
||||
reportSheet.Column(1).Width = 23;
|
||||
reportSheet.Column(2).Width = 30;
|
||||
reportSheet.Column(3).Width = 20;
|
||||
reportSheet.Column(4).Width = 20;
|
||||
reportSheet.Column(5).Width = 20;
|
||||
|
||||
// ====== 第二个Sheet:曲线数据和图表 ======
|
||||
ExcelWorksheet curveSheet = excelPackage.Workbook.Worksheets.Add("实时摩擦力曲线数据");
|
||||
|
||||
// 获取曲线数据
|
||||
curveSheet.Column(1).Width = 15;
|
||||
curveSheet.Column(2).Width = 20;
|
||||
curveSheet.Column(3).Width = 20;
|
||||
|
||||
if (allData.Count == 0)
|
||||
{
|
||||
MessageBox.Show("没有曲线数据可导出!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 按时间排序
|
||||
allData = allData.OrderBy(d => d.Item1.Ticks).ToList();
|
||||
|
||||
// 计算时间范围
|
||||
DateTime firstTime = allData.First().Item1;
|
||||
DateTime lastTime = allData.Last().Item1;
|
||||
double totalSeconds = Math.Max(1, (lastTime - firstTime).TotalSeconds);
|
||||
|
||||
// ====== 修复2:X轴要比最大时间稍大 ======
|
||||
// 方案1:向上取整到最近的整数,再加1秒
|
||||
double xAxisMax = Math.Ceiling(totalSeconds) + 1;
|
||||
|
||||
// 方案2:确保至少有12秒显示范围(如果数据在10秒左右)
|
||||
if (totalSeconds > 8 && totalSeconds < 12)
|
||||
{
|
||||
xAxisMax = 12; // 10秒左右的数据,显示到12秒
|
||||
}
|
||||
else if (xAxisMax < 10)
|
||||
{
|
||||
xAxisMax = 10; // 最小显示到10秒
|
||||
}
|
||||
|
||||
Console.WriteLine($"数据最大时间: {totalSeconds:F3}秒, X轴设置: 0-{xAxisMax:F1}秒");
|
||||
|
||||
// 设置列宽
|
||||
curveSheet.Column(1).Width = 15; // A列
|
||||
curveSheet.Column(2).Width = 20; // B列
|
||||
curveSheet.Column(3).Width = 20; // C列
|
||||
|
||||
// 表头行
|
||||
// 表头
|
||||
curveSheet.Cells["A1"].Value = "时间(秒)";
|
||||
curveSheet.Cells["B1"].Value = "工位1摩擦力(N)";
|
||||
curveSheet.Cells["C1"].Value = "工位2摩擦力(N)";
|
||||
|
||||
// 设置表头样式
|
||||
curveSheet.Cells["A1:C1"].Style.Font.Bold = true;
|
||||
curveSheet.Cells["A1:C1"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
|
||||
curveSheet.Cells["A1:C1"].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
|
||||
curveSheet.Cells["A1:C1"].Style.Border.Bottom.Style = ExcelBorderStyle.Medium;
|
||||
curveSheet.Row(1).Height = 25;
|
||||
|
||||
// 写入数据
|
||||
// 写入过滤后的数据和重建的时间轴
|
||||
int dataRow = 2;
|
||||
foreach (var data in allData)
|
||||
for (int i = 0; i < filteredData.Count; i++)
|
||||
{
|
||||
double seconds = (data.Item1 - firstTime).TotalSeconds;
|
||||
double seconds = timeAxis[i];
|
||||
var data = filteredData[i];
|
||||
|
||||
curveSheet.Cells[dataRow, 1].Value = Math.Round(seconds, 3);
|
||||
curveSheet.Cells[dataRow, 2].Value = Math.Round(data.Item2, 1);
|
||||
curveSheet.Cells[dataRow, 3].Value = Math.Round(data.Item3, 1);
|
||||
|
||||
// 设置数值格式
|
||||
curveSheet.Cells[dataRow, 1].Style.Numberformat.Format = "0.000";
|
||||
curveSheet.Cells[dataRow, 2].Style.Numberformat.Format = "0.00";
|
||||
curveSheet.Cells[dataRow, 3].Style.Numberformat.Format = "0.00";
|
||||
|
||||
dataRow++;
|
||||
}
|
||||
|
||||
int lastDataRow = dataRow - 1;
|
||||
Console.WriteLine($"数据写入完成: 第2行到第{lastDataRow}行");
|
||||
|
||||
|
||||
|
||||
|
||||
// ====== 第二个Sheet:曲线数据和图表 ======
|
||||
|
||||
if (allData.Count == 0)
|
||||
{
|
||||
MessageBox.Show("没有曲线数据可导出!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 按时间排序
|
||||
allData = allData.OrderBy(d => d.Item1.Ticks).ToList();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (totalSeconds > 8 && totalSeconds < 12)
|
||||
{
|
||||
xAxisMax = 12; // 10秒左右的数据,显示到12秒
|
||||
}
|
||||
// 计算X轴范围(基于有效时长)
|
||||
double xAxisMax = Math.Ceiling(totalValidSeconds) + 1;
|
||||
if (totalValidSeconds > 8 && totalValidSeconds < 12)
|
||||
xAxisMax = 12;
|
||||
else if (xAxisMax < 10)
|
||||
{
|
||||
xAxisMax = 10; // 最小显示到10秒
|
||||
}
|
||||
xAxisMax = 10;
|
||||
|
||||
Console.WriteLine($"数据最大时间: {totalSeconds:F3}秒, X轴设置: 0-{xAxisMax:F1}秒");
|
||||
|
||||
// 设置列宽
|
||||
curveSheet.Column(1).Width = 15; // A列
|
||||
curveSheet.Column(2).Width = 20; // B列
|
||||
curveSheet.Column(3).Width = 20; // C列
|
||||
|
||||
// 表头行
|
||||
curveSheet.Cells["A1"].Value = "时间(秒)";
|
||||
curveSheet.Cells["B1"].Value = "工位1摩擦力(N)";
|
||||
curveSheet.Cells["C1"].Value = "工位2摩擦力(N)";
|
||||
|
||||
// 设置表头样式
|
||||
curveSheet.Cells["A1:C1"].Style.Font.Bold = true;
|
||||
curveSheet.Cells["A1:C1"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
|
||||
curveSheet.Cells["A1:C1"].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
|
||||
curveSheet.Cells["A1:C1"].Style.Border.Bottom.Style = ExcelBorderStyle.Medium;
|
||||
curveSheet.Row(1).Height = 25;
|
||||
|
||||
|
||||
|
||||
Console.WriteLine($"数据写入完成: 第2行到第{lastDataRow}行");
|
||||
|
||||
// ====== 创建散点图 ======
|
||||
// 创建散点图
|
||||
var chart = curveSheet.Drawings.AddChart("摩擦力曲线", eChartType.XYScatterLines);
|
||||
chart.SetPosition(0, 0, 3, 0);
|
||||
chart.SetSize(800, 400);
|
||||
|
||||
// 设置图表位置和大小
|
||||
chart.SetPosition(0, 0, 3, 0); // 从第0行,第3列开始
|
||||
chart.SetSize(800, 400); // 宽度800px,高度400px
|
||||
|
||||
// 设置图表标题
|
||||
chart.Title.Text = "实时摩擦力曲线";
|
||||
chart.Title.Font.Size = 14;
|
||||
chart.Title.Font.Bold = true;
|
||||
|
||||
// 添加工位1数据系列
|
||||
var series1 = chart.Series.Add(
|
||||
curveSheet.Cells[2, 2, lastDataRow, 2], // Y值:B2到B{lastDataRow}
|
||||
curveSheet.Cells[2, 1, lastDataRow, 1] // X值:A2到A{lastDataRow}
|
||||
curveSheet.Cells[2, 2, lastDataRow, 2],
|
||||
curveSheet.Cells[2, 1, lastDataRow, 1]
|
||||
);
|
||||
series1.Header = "工位1摩擦力";
|
||||
|
||||
|
||||
// 添加工位2数据系列
|
||||
var series2 = chart.Series.Add(
|
||||
curveSheet.Cells[2, 3, lastDataRow, 3], // Y值:C2到C{lastDataRow}
|
||||
curveSheet.Cells[2, 1, lastDataRow, 1] // X值:A2到A{lastDataRow}
|
||||
curveSheet.Cells[2, 3, lastDataRow, 3],
|
||||
curveSheet.Cells[2, 1, lastDataRow, 1]
|
||||
);
|
||||
series2.Header = "工位2摩擦力";
|
||||
|
||||
|
||||
// 设置X轴范围
|
||||
chart.XAxis.MinValue = 0;
|
||||
chart.XAxis.MaxValue = xAxisMax;
|
||||
|
||||
// 计算合适的刻度
|
||||
double majorUnit = CalculateMajorUnit(xAxisMax);
|
||||
chart.XAxis.MajorUnit = majorUnit;
|
||||
|
||||
Console.WriteLine($"散点图X轴设置: 0-{xAxisMax:F1}秒, 主要刻度: {majorUnit:F1}秒");
|
||||
|
||||
// 设置X轴标题
|
||||
chart.XAxis.Title.Text = "时间 (秒)";
|
||||
chart.XAxis.Title.Font.Size = 11;
|
||||
chart.XAxis.Title.Font.Bold = true;
|
||||
|
||||
// 设置Y轴标题
|
||||
chart.YAxis.Title.Text = "摩擦力 (N)";
|
||||
chart.YAxis.Title.Font.Size = 11;
|
||||
chart.YAxis.Title.Font.Bold = true;
|
||||
|
||||
// 设置图例位置
|
||||
chart.Legend.Position = eLegendPosition.Bottom;
|
||||
|
||||
// 添加网格线
|
||||
chart.XAxis.MajorGridlines.Fill.Color = System.Drawing.Color.LightGray;
|
||||
chart.YAxis.MajorGridlines.Fill.Color = System.Drawing.Color.LightGray;
|
||||
|
||||
// ====== 添加信息行 ======
|
||||
// 信息行
|
||||
int infoRow = lastDataRow + 2;
|
||||
curveSheet.Cells[infoRow, 1].Value = "图表信息:";
|
||||
curveSheet.Cells[infoRow, 1].Style.Font.Bold = true;
|
||||
|
||||
curveSheet.Cells[infoRow + 1, 1].Value = "数据时间范围:";
|
||||
curveSheet.Cells[infoRow + 1, 2].Value = $"0 - {totalSeconds:F3} 秒";
|
||||
curveSheet.Cells[infoRow + 1, 1].Value = "有效数据时间范围:";
|
||||
curveSheet.Cells[infoRow + 1, 2].Value = $"0 - {totalValidSeconds:F3} 秒";
|
||||
|
||||
curveSheet.Cells[infoRow + 2, 1].Value = "X轴显示范围:";
|
||||
curveSheet.Cells[infoRow + 2, 2].Value = $"0 - {xAxisMax:F1} 秒";
|
||||
@@ -456,25 +339,23 @@ namespace PLCDataMonitor
|
||||
curveSheet.Cells[infoRow + 3, 1].Value = "X轴主要刻度:";
|
||||
curveSheet.Cells[infoRow + 3, 2].Value = $"{majorUnit:F1} 秒";
|
||||
|
||||
curveSheet.Cells[infoRow + 4, 1].Value = "数据点数:";
|
||||
curveSheet.Cells[infoRow + 4, 2].Value = allData.Count;
|
||||
curveSheet.Cells[infoRow + 4, 1].Value = "有效数据点数:";
|
||||
curveSheet.Cells[infoRow + 4, 2].Value = filteredData.Count;
|
||||
|
||||
// 保存文件
|
||||
FileInfo excelFile = new FileInfo(saveFileDialog.FileName);
|
||||
excelPackage.SaveAs(excelFile);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
MessageBox.Show("滑动摩擦磨损试验报告导出成功!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
//// 清理数据
|
||||
// 清理数据(可选)
|
||||
ReportList.Clear();
|
||||
if (CurveDataQueue != null)
|
||||
{
|
||||
while (CurveDataQueue.TryDequeue(out _)) { }
|
||||
}
|
||||
// 同时清空暂停时间段(如果主窗口提供了方法)
|
||||
mainWindow.ClearPausePeriods(); // 需要在 MainWindow 中实现
|
||||
ReportDataGrid.ItemsSource = ReportList;
|
||||
OnExportCompleted?.Invoke();
|
||||
}
|
||||
@@ -491,20 +372,16 @@ namespace PLCDataMonitor
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:计算合适的刻度
|
||||
private double CalculateMajorUnit(double xAxisMax)
|
||||
{
|
||||
if (xAxisMax <= 5) return 1; // 0-5秒:每1秒一个刻度
|
||||
if (xAxisMax <= 10) return 2; // 5-10秒:每2秒一个刻度
|
||||
if (xAxisMax <= 20) return 5; // 10-20秒:每5秒一个刻度
|
||||
if (xAxisMax <= 50) return 10; // 20-50秒:每10秒一个刻度
|
||||
if (xAxisMax <= 100) return 20; // 50-100秒:每20秒一个刻度
|
||||
|
||||
return Math.Ceiling(xAxisMax / 10); // 超过100秒:分成10个刻度
|
||||
if (xAxisMax <= 5) return 1;
|
||||
if (xAxisMax <= 10) return 2;
|
||||
if (xAxisMax <= 20) return 5;
|
||||
if (xAxisMax <= 50) return 10;
|
||||
if (xAxisMax <= 100) return 20;
|
||||
return Math.Ceiling(xAxisMax / 10);
|
||||
}
|
||||
|
||||
|
||||
// EPPlus版本的CreateFormattedRow辅助方法
|
||||
private void CreateFormattedRowEPPlus(ExcelWorksheet worksheet, int row, string seq, string projectName,
|
||||
string station1, string station2, string remark)
|
||||
{
|
||||
@@ -514,7 +391,6 @@ namespace PLCDataMonitor
|
||||
worksheet.Cells[row, 4].Value = station2;
|
||||
worksheet.Cells[row, 5].Value = remark;
|
||||
|
||||
// 设置样式
|
||||
for (int col = 1; col <= 5; col++)
|
||||
{
|
||||
worksheet.Cells[row, col].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
|
||||
@@ -525,13 +401,11 @@ namespace PLCDataMonitor
|
||||
worksheet.Cells[row, col].Style.Border.Right.Style = ExcelBorderStyle.Thin;
|
||||
}
|
||||
|
||||
// 备注列左对齐并自动换行
|
||||
worksheet.Cells[row, 5].Style.HorizontalAlignment = ExcelHorizontalAlignment.Left;
|
||||
worksheet.Cells[row, 5].Style.WrapText = true;
|
||||
worksheet.Row(row).Height = 25;
|
||||
}
|
||||
|
||||
|
||||
public event Action OnExportCompleted;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user