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}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|