Files
MobilePhoneGrippingCuttingT…/EmergencyStopWindow.xaml.cs

789 lines
26 KiB
C#
Raw Normal View History

2026-04-20 11:51:33 +08:00
using Modbus.Device;
using OxyPlot;
using OxyPlot.Series;
using OxyPlot.Wpf;
using PdfSharpCore.Drawing;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
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;
using ;
using .Data;
namespace EmptyLoadTest
{
/// <summary>
/// Window1.xaml 的交互逻辑
/// </summary>
public partial class EmergencyStopWindow : Window
{
private ObservableCollection<ReportItem> _reportData;
private DispatcherTimer _dataInsertTimer;
private int _dataIndex = 1;
#region
private TcpClient _tcpClient => ModbusResourceManager.Instance.TcpClient;
private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster;
private readonly DispatcherTimer _readTimer;
private DispatcherTimer _dataTimer;
#endregion
.Function ma;
DataChange c = new DataChange();
private PlotModel _plotModel;
private LineSeries _temperatureSeries;
public EmergencyStopWindow()
{
InitializeComponent();
InitializeData();
InitializeUI();
InitializeModbusTcp();
_readTimer = InitDispatcherTimer();
_dataTimer = InitDispatcherTimer2();
}
private void InitializeUI()
{
// 初始化图表
_plotModel = new PlotModel
{
Title = "扭矩-转速曲线",
Subtitle = "医用刨削器特征"
};
_plotModel.Axes.Add(new OxyPlot.Axes.LinearAxis
{
Position = OxyPlot.Axes.AxisPosition.Bottom,
Title = "扭矩(N.cm)",
Minimum = 0
});
_plotModel.Axes.Add(new OxyPlot.Axes.LinearAxis
{
Position = OxyPlot.Axes.AxisPosition.Left,
Title = "转速(rad/min)"
});
_temperatureSeries = new LineSeries
{
Title = "转速变化",
Color = OxyColors.Blue,
MarkerType = MarkerType.Circle,
MarkerSize = 3
};
_plotModel.Series.Add(_temperatureSeries);
plotView.Model = _plotModel;
}
private static EmergencyStopWindow _instance;
public static EmergencyStopWindow Instance
{
get
{
if (_instance == null || !_instance.IsLoaded)
{
_instance = new EmergencyStopWindow();
}
return _instance;
}
}
private void InitializeModbusTcp()
{
try
{
2026-04-20 14:03:34 +08:00
//string plcIp = "192.168.1.10";
string plcIp = "127.0.0.1";
2026-04-20 11:51:33 +08:00
bool initSuccess = ModbusResourceManager.Instance.Init(plcIp, 502);
if (!initSuccess)
{
MessageBox.Show("连接Modbus服务器失败", "错误");
this.Close();
return;
}
// 检查连接状态
if (_tcpClient == null || !_tcpClient.Connected)
{
MessageBox.Show("Modbus连接异常", "错误");
this.Close();
return;
}
ma = new .Function(_modbusMaster);
}
catch (Exception ex)
{
ShowError($"Modbus初始化失败: {ex.Message}");
}
}
private void ShowError(string msg) => MessageBox.Show(msg, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
private DispatcherTimer InitDispatcherTimer()
{
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(100)
};
timer.Tick += async (s, e) =>
{
if (_modbusMaster != null)
{
try
{
await ReadAddr262DataAsync();
}
catch { }
}
};
return timer;
}
private DispatcherTimer InitDispatcherTimer2()
{
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(50)
};
timer.Tick += DataTimer_Tick;
return timer;
}
private async System.Threading.Tasks.Task ReadAddr262DataAsync()
{
try
{
// 创建任务列表
var tasks = new List<Task>
{
ReadAndUpdateFloatAsync(1306, 1, actualSpeed, "F2", " "),
ReadAndUpdateFloatAsync(1304, 2, force, "F2", " "),
ReadAndUpdateFloatAsync(322, 2, settingforce, "F3", " "),
ReadAndUpdateFloatAsync(300, 2, speed, "F3", " "),
ReadAndUpdateFloatAsync(430, 2, maxforce, "F2", " "),
ReadAndUpdateFloatAsync(4324, 1, reachforce, "F0", " "),
ReadAndUpdateFloatAsync(4322, 1, maxforce2, "F0", " "),
ReadStatusAsync(),
ReadresetAndStopStatusAsync()
};
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
ShowError($"读取数据失败:{ex.Message}");
}
}
private void updateReport()
{
// 2. 读取配置文件App.config
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.Settings["txtSetTorque"].Value = settingforce.Text;
config.AppSettings.Settings["txtTestTime"].Value = (DateTime.Now - starttime).TotalSeconds.ToString("F1");
config.AppSettings.Settings["txtSpeedAtSetTorque"].Value = maxforce2.Text;
config.AppSettings.Settings["txtSpeedAtMaxTorque"].Value = maxforce2.Text;
// 4. 保存配置更改关键不调用Save则修改不生效
config.Save(ConfigurationSaveMode.Modified);
// 刷新配置管理器,让后续读取能获取最新值
ConfigurationManager.RefreshSection("appSettings");
}
// 添加这些变量
private bool _testStarted = false;
private bool _testCompleted = false;
private async Task ReadStatusAsync()
{
try
{
bool[] registers = await Task.Run(async () =>
await _modbusMaster?.ReadCoilsAsync(1, 321, 1)
);
if (registers != null && registers.Length >= 1)
{
bool value = registers[0];
Dispatcher.Invoke(() =>
{
if (value)
{
statusB.Background = Brushes.Green;
status.Text = "运行中";
_dataInsertTimer?.Start();
_dataTimer?.Start();
updateReport();
// 测试已开始
if (!_testStarted)
{
_testStarted = true;
}
}
else
{
statusB.Background = Brushes.Blue;
status.Text = "空闲";
// 只有当测试确实开始过且尚未完成时才导出
if (_testStarted && !_testCompleted)
{
_testCompleted = true;
// 检查是否有测试数据
if (_reportData != null && _reportData.Count > 0)
{
// 延时一小段时间确保数据已保存
Task.Delay(500).ContinueWith(_ =>
{
Dispatcher.Invoke(async () =>
{
await AutoExportCSV();
});
});
}
}
_dataInsertTimer?.Stop();
_dataTimer?.Stop();
}
});
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取地址{200}失败:{ex.Message}");
}
}
private void InitializeData()
{
_reportData = new ObservableCollection<ReportItem>();
_dataInsertTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(20) // 每秒插入一次数据
};
_dataInsertTimer.Tick += InsertData_Tick;
}
// 添加线程锁,防止并发问题
private readonly object _dataLock = new object();
private int _lastForceValueIndex = 0;
private float _lastForceValue = 0f;
private const float FORCE_CHANGE_THRESHOLD = 0.1f; // 力变化阈值,防止重复记录
private void InsertData_Tick(object sender, EventArgs e)
{
try
{
// 解析当前力值
if (!float.TryParse(force.Text, out float currentForceValue))
{
currentForceValue = 0;
}
//ushort[] registers = _modbusMaster?.ReadHoldingRegisters(1, 1304, 2);
//if (registers != null && registers.Length >= 2)
//{
// currentForceValue = c.UshortToFloat(registers[1], registers[0]);
//}
if (!float.TryParse(actualSpeed.Text, out float currentSpeedValue))
{
currentSpeedValue = 0;
}
// 简化条件:去掉阈值限制或降低第一次记录的阈值
if (_reportData.Count == 0 || currentForceValue >= 0.2f)
{
// 使用锁确保线程安全
lock (_dataLock)
{
// 更新最后记录的值
_lastForceValue = currentForceValue;
_lastForceValueIndex = _dataIndex;
// 创建报表项
var reportItem = new ReportItem
{
Index = _dataIndex,
Time = DateTime.Now,
Torque = currentForceValue,
Speed = (int)currentSpeedValue,
};
// 限制最大数据条数,避免内存过大
if (_reportData.Count > 1000)
{
_reportData.RemoveAt(0);
}
// 添加到报表数据
_reportData.Add(reportItem);
// 递增索引
_dataIndex++;
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"插入数据失败:{ex.Message}");
}
}
public ObservableCollection<ReportItem> GetReportData()
{
return _reportData;
}
// 清空报表数据
public void ClearReportData()
{
_reportData?.Clear();
_dataIndex = 1;
}
private async Task ReadresetAndStopStatusAsync()
{
try
{
bool[] registers = await Task.Run(async () =>
await _modbusMaster?.ReadCoilsAsync(1, 91, 2)
);
if (registers != null && registers.Length >= 1)
{
bool value = registers[0];
Dispatcher.Invoke(() =>
{
if (value)
{
fastleftB.Background = Brushes.Red;
fastleft.Text = "复位中";
}
else if (registers[1])
{
fastleftB.Background = Brushes.Green;
fastleft.Text = "复位完成";
shacheB.Visibility = Visibility.Hidden;
}
});
}
bool[] registers2 = await Task.Run(async () =>
await _modbusMaster?.ReadCoilsAsync(1, 310, 1)
);
if (registers2 != null && registers2.Length >= 1)
{
bool value2 = registers2[0];
Dispatcher.Invoke(() =>
{
if (value2)
{
limit.Visibility = Visibility.Visible;
}
else
{
limit.Visibility = Visibility.Hidden;
}
});
}
bool[] registers3 = await Task.Run(async () =>
await _modbusMaster?.ReadCoilsAsync(1, 1300, 1)
);
if (registers3 != null && registers3.Length >= 1)
{
bool value3 = registers3[0];
Dispatcher.Invoke(() =>
{
if (value3)
{
stopB.Background = Brushes.Red;
stop.Text = "急停未复位";
faststop.Visibility = Visibility.Visible;
}
else
{
stopB.Background = Brushes.Green;
stop.Text = "急停已复位,系统可重新运行";
faststop.Visibility = Visibility.Hidden;
}
});
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取地址{200}失败:{ex.Message}");
}
}
private async Task ReadAndUpdateFloatAsync(int address, int length, System.Windows.Controls.TextBox control, string format, string unit)
{
try
{
ushort[] registers = await Task.Run(async () =>
await _modbusMaster?.ReadHoldingRegistersAsync(1, (ushort)address, (ushort)length)
);
if (registers != null && registers.Length >= 2)
{
float value = c.UshortToFloat(registers[1], registers[0]);
Dispatcher.Invoke(() => control.Text = value.ToString(format) + unit);
}
else if (registers != null && registers.Length >= 1)
{
int value = registers[0];
Dispatcher.Invoke(() => control.Text = value.ToString(format) + unit);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取地址{address}失败:{ex.Message}");
}
}
private async Task ReadAndUpdateFloatAsync(int address, int length, System.Windows.Controls.TextBlock control, string format, string unit)
{
try
{
ushort[] registers = await Task.Run(async () =>
await _modbusMaster?.ReadHoldingRegistersAsync(1, (ushort)address, (ushort)length)
);
if (registers != null && registers.Length >= 2)
{
float value = c.UshortToFloat(registers[1], registers[0]);
Dispatcher.Invoke(() => control.Text = value.ToString(format) + unit);
}
else if (registers != null && registers.Length >= 1)
{
int value = registers[0];
Dispatcher.Invoke(() => control.Text = value.ToString(format) + unit);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取地址{address}失败:{ex.Message}");
}
}
private void settingforce_GotFocus(object sender, RoutedEventArgs e)
{
ma.WriteToPLCForNew(settingforce.Text.Trim(), 322, Function.DataType.);
System.Threading.Tasks.Task.Delay(50);
t0.Focus();
}
private void speed_GotFocus(object sender, RoutedEventArgs e)
{
ma.WriteToPLCForNew(speed.Text.Trim(), 300, Function.DataType.);
System.Threading.Tasks.Task.Delay(50);
t0.Focus();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//ma.BtnClickFunctionForNew(Function.ButtonType.置位型, 300);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
//ma.BtnClickFunctionForNew(Function.ButtonType.置位型, 301);
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
ma.BtnClickFunctionForNew(Function.ButtonType., 320);
starttime = DateTime.Now;
// 重置测试状态标志
_testStarted = true;
_testCompleted = false;
_reportData?.Clear(); // 清空之前的测试数据
_dataIndex = 1;
}
DateTime starttime;
private readonly Queue<float> _forceBuffer = new Queue<float>();
private readonly Queue<float> _speedBuffer = new Queue<float>();
private const int BUFFER_SIZE = 5; // 滑动窗口大小
private void DataTimer_Tick(object sender, EventArgs e)
{
float forceV = float.Parse(force.Text);
float actualSpeedV = float.Parse(actualSpeed.Text);
float settingforceV = float.Parse(settingforce.Text);
_temperatureSeries.Points.Add(new OxyPlot.DataPoint(forceV, actualSpeedV));
_plotModel.InvalidatePlot(true);
//CurveWindow.Instance.UpdateData(forceV, actualSpeedV);
if (forceV + 1 >= settingforceV)
{
shacheB.Visibility = Visibility.Visible;
shacheB.Background = Brushes.Red;
shache.Text = "请松踏板!!";
}
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
ma.BtnClickFunctionForNew(Function.ButtonType., 90);
//shacheB.Visibility = Visibility.Hidden;
}
private void Button_Click_4(object sender, RoutedEventArgs e)
{
var mainWindow = ExperimentReportWindow.Instance;
// 检查窗口状态只在窗口未显示时调用ShowDialog
if (!mainWindow.IsVisible)
{
mainWindow.Show();
}
else
{
// 如果窗口已显示,可将其激活到前台
mainWindow.Activate();
}
Close();
}
private void Button_Click_5(object sender, RoutedEventArgs e)
{
var mainWindow = ProcessReportWindow.Instance;
// 检查窗口状态只在窗口未显示时调用ShowDialog
if (!mainWindow.IsVisible)
{
mainWindow.Show();
}
else
{
// 如果窗口已显示,可将其激活到前台
mainWindow.Activate();
}
Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_readTimer?.Start();
}
private void _PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_modbusMaster?.WriteSingleCoil(1, 300, true);
((Button)sender).CaptureMouse();
}
private void _PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_modbusMaster?.WriteSingleCoil(1, 300, false);
((Button)sender).ReleaseMouseCapture();
}
private void _LostMouseCapture(object sender, MouseEventArgs e)
{
_modbusMaster?.WriteSingleCoil(1, 300, false);
}
// 鼠标按下时触发
private void _PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// 鼠标按下时置位
_modbusMaster?.WriteSingleCoil(1, 301, true);
// 捕获鼠标,确保即使拖动出按钮范围也能收到抬起事件
((Button)sender).CaptureMouse();
}
private void _PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// 鼠标抬起时复位
_modbusMaster?.WriteSingleCoil(1, 301, false);
// 释放鼠标捕获
((Button)sender).ReleaseMouseCapture();
}
private void _LostMouseCapture(object sender, MouseEventArgs e)
{
// 如果意外失去鼠标捕获,也复位
_modbusMaster?.WriteSingleCoil(1, 301, false);
}
private void Button_Click_6(object sender, RoutedEventArgs e)
{
var mainWindow = MainWindow.Instance;
if (!mainWindow.IsVisible)
{
mainWindow.Show();
}
else
{
mainWindow.Activate();
}
Close();
}
private void Button_Click_7(object sender, RoutedEventArgs e)
{
_temperatureSeries?.Points.Clear();
// 如果使用了滑动窗口缓冲区,也需要清除
if (_forceBuffer != null) _forceBuffer.Clear();
if (_speedBuffer != null) _speedBuffer.Clear();
_dataIndex = 0;
// 刷新图表
_plotModel?.InvalidatePlot(true);
}
private void Button_Click_8(object sender, RoutedEventArgs e)
{
SaveChartAsImage();
}
private void SaveChartAsImage()
{
try
{
int imageWidth = 1000; // 设置宽度
int imageHeight = 700; // 设置高度
if (plotView == null || _temperatureSeries.Points.Count == 0)
{
MessageBox.Show("没有数据可保存", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var saveFileDialog = new Microsoft.Win32.SaveFileDialog
{
Filter = "PNG图片 (*.png)|*.png",
FileName = $"曲线图_{DateTime.Now:yyyyMMdd_HHmmss}.png",
DefaultExt = ".png"
};
if (saveFileDialog.ShowDialog() == true)
{
// 方法1直接截图plotView控件
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
imageWidth,
imageHeight,
96d, 96d, PixelFormats.Pbgra32);
renderBitmap.Render(plotView);
// 保存为PNG
using (var fileStream = new FileStream(saveFileDialog.FileName, FileMode.Create))
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
encoder.Save(fileStream);
}
MessageBox.Show($"图片已保存到:{saveFileDialog.FileName}", "保存成功",
MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"保存失败:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private async Task AutoExportCSV()
{
try
{
// 再次检查是否有测试数据
if (_reportData == null || _reportData.Count == 0)
{
return;
}
string fileName = $"过程报表_{DateTime.Now:yyyyMMdd_HHmmss}.csv";
string folderPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Reports");
Directory.CreateDirectory(folderPath);
string filePath = System.IO.Path.Combine(folderPath, fileName);
await Task.Run(() =>
{
using (var writer = new StreamWriter(filePath, false, System.Text.Encoding.UTF8))
{
writer.WriteLine("序号,时间,实时扭矩(N.cm),实时转速(Rad/Min)");
foreach (var item in _reportData)
{
writer.WriteLine($"{item.Index},{item.Time:HH:mm:ss},{item.Torque:F1},{item.Speed}");
}
}
});
Dispatcher.Invoke(() =>
{
MessageBox.Show($"测试完成,报表已自动导出到:{filePath}", "导出成功",
MessageBoxButton.OK, MessageBoxImage.Information);
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"自动导出报表失败:{ex.Message}");
}
}
}
}