Files
MobilePhoneGrippingCuttingT…/EmergencyStopWindow.xaml.cs
2026-04-20 14:03:34 +08:00

789 lines
26 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 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
{
//string plcIp = "192.168.1.10";
string plcIp = "127.0.0.1";
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}");
}
}
}
}