diff --git a/CSI-H238M/CSI-H238M/Services/ModbusService.cs b/CSI-H238M/CSI-H238M/Services/ModbusService.cs
index 840fd5f..3c480b7 100644
--- a/CSI-H238M/CSI-H238M/Services/ModbusService.cs
+++ b/CSI-H238M/CSI-H238M/Services/ModbusService.cs
@@ -442,10 +442,80 @@ namespace COFTester.Services
}
///
- /// 写入启动/停止寄存器 M31
+ /// 触发测试启动按钮 M30(复归型)
+ /// M30 是复归型按钮,需要写入脉冲信号:true → 延迟 → false
+ /// 然后通过读取 M31 来判断测试是否真正开始(1=测试中,0=停止)
+ ///
+ public virtual async Task TriggerTestStartAsync()
+ {
+ try
+ {
+ if (_modbusMaster != null && _isConnected)
+ {
+ const ushort START_BUTTON_ADDRESS = 30; // M30 复归型按钮
+
+ System.Diagnostics.Debug.WriteLine($"[Modbus] 触发测试启动按钮 M{START_BUTTON_ADDRESS}");
+
+ // 写入 true(触发)
+ await _modbusMaster.WriteSingleCoilAsync(1, START_BUTTON_ADDRESS, true);
+ System.Diagnostics.Debug.WriteLine($"[Modbus] M{START_BUTTON_ADDRESS} = true");
+
+ // 延迟 100ms
+ await Task.Delay(100);
+
+ // 写入 false(复位)
+ await _modbusMaster.WriteSingleCoilAsync(1, START_BUTTON_ADDRESS, false);
+ System.Diagnostics.Debug.WriteLine($"[Modbus] M{START_BUTTON_ADDRESS} = false");
+
+ // 延迟 100ms 确保 PLC 处理完成
+ await Task.Delay(100);
+
+ System.Diagnostics.Debug.WriteLine($"[Modbus] M{START_BUTTON_ADDRESS} 脉冲信号发送完成");
+ }
+ }
+ catch (Exception ex)
+ {
+ OnErrorOccurred($"触发测试启动失败: {ex.Message}");
+ System.Diagnostics.Debug.WriteLine($"[Modbus] 触发 M30 异常: {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// 读取测试状态标记位 M31
+ /// M31: 1=测试中,0=停止
+ ///
+ /// true=测试中,false=停止
+ public virtual async Task ReadTestStatusAsync()
+ {
+ try
+ {
+ if (_modbusMaster != null && _isConnected)
+ {
+ const ushort STATUS_FLAG_ADDRESS = 31; // M31 状态标记位
+
+ var coils = await _modbusMaster.ReadCoilsAsync(1, STATUS_FLAG_ADDRESS, 1);
+ bool isRunning = coils[0];
+
+ System.Diagnostics.Debug.WriteLine($"[Modbus] 读取测试状态 M{STATUS_FLAG_ADDRESS} = {(isRunning ? "1 (测试中)" : "0 (停止)")}");
+
+ return isRunning;
+ }
+ return false;
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[Modbus] 读取 M31 状态异常: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 写入启动/停止寄存器 M31(已弃用,请使用 TriggerTestStartAsync)
/// M30 复归型, M31 标注为 1=开始,0=停止
///
/// 1=开始测试, 0=停止测试
+ [Obsolete("请使用 TriggerTestStartAsync() 触发 M30,然后通过 ReadTestStatusAsync() 读取 M31 状态")]
public virtual async Task WriteStartStopRegisterAsync(ushort value)
{
try
@@ -649,6 +719,7 @@ namespace COFTester.Services
/// 數據採集主循環
/// 正確流程:1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置)
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
+ /// M30 复归型按钮触发测试,M31 状态标记位(1=测试中,0=停止)
///
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{
@@ -666,11 +737,30 @@ namespace COFTester.Services
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 数据采集配置: {totalPoints} 点, 间隔 {intervalMs:F1}ms");
- System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 注意:使用 M31=1 启动测试,M31=0 停止测试");
+ System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 监控 M31 状态:1=测试中,0=停止");
// 循环读取传感器数据(包含力值、位移和位置)
for (int i = 0; i < totalPoints && !token.IsCancellationRequested; i++)
{
+ // 每隔一定次数检查 M31 状态(避免频繁读取)
+ if (i % 10 == 0) // 每 10 个数据点检查一次
+ {
+ try
+ {
+ bool isTestRunning = await ReadTestStatusAsync();
+ if (!isTestRunning)
+ {
+ System.Diagnostics.Debug.WriteLine("[ModbusTCP] M31 = 0,PLC 已停止测试");
+ break; // 退出采集循环
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 读取 M31 状态失败: {ex.Message}");
+ // 读取失败不影响数据采集,继续执行
+ }
+ }
+
// 读取力值和位移
var dataPoint = await ReadSensorDataAsync();
@@ -707,7 +797,16 @@ namespace COFTester.Services
finally
{
_isAcquiring = false;
- System.Diagnostics.Debug.WriteLine("[ModbusTCP] 數據採集結束(不再發送 Stop 命令)");
+ System.Diagnostics.Debug.WriteLine("[ModbusTCP] 數據採集結束");
+
+ // 最后再检查一次 M31 状态
+ try
+ {
+ bool isTestRunning = await ReadTestStatusAsync();
+ System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 采集结束时 M31 = {(isTestRunning ? "1 (测试中)" : "0 (停止)")}");
+ }
+ catch { }
+
OnTestFinished();
}
}
@@ -1106,6 +1205,7 @@ namespace COFTester.Services
/// 數據採集主循環
/// 正確流程:1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置)
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
+ /// M30 复归型按钮触发测试,M31 状态标记位(1=测试中,0=停止)
///
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{
@@ -1123,11 +1223,30 @@ namespace COFTester.Services
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 数据采集配置: {totalPoints} 点, 间隔 {intervalMs:F1}ms");
- System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 注意:使用 M31=1 启动测试,M31=0 停止测试");
+ System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 监控 M31 状态:1=测试中,0=停止");
// 循环读取传感器数据(包含力值、位移和位置)
for (int i = 0; i < totalPoints && !token.IsCancellationRequested; i++)
{
+ // 每隔一定次数检查 M31 状态(避免频繁读取)
+ if (i % 10 == 0) // 每 10 个数据点检查一次
+ {
+ try
+ {
+ bool isTestRunning = await ReadTestStatusAsync();
+ if (!isTestRunning)
+ {
+ System.Diagnostics.Debug.WriteLine("[ModbusRTU] M31 = 0,PLC 已停止测试");
+ break; // 退出采集循环
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 读取 M31 状态失败: {ex.Message}");
+ // 读取失败不影响数据采集,继续执行
+ }
+ }
+
// 读取力值和位移
var dataPoint = await ReadSensorDataAsync();
@@ -1164,7 +1283,16 @@ namespace COFTester.Services
finally
{
_isAcquiring = false;
- System.Diagnostics.Debug.WriteLine("[ModbusRTU] 數據採集結束(不再發送 Stop 命令)");
+ System.Diagnostics.Debug.WriteLine("[ModbusRTU] 數據採集結束");
+
+ // 最后再检查一次 M31 状态
+ try
+ {
+ bool isTestRunning = await ReadTestStatusAsync();
+ System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 采集结束时 M31 = {(isTestRunning ? "1 (测试中)" : "0 (停止)")}");
+ }
+ catch { }
+
OnTestFinished();
}
}
@@ -1547,6 +1675,7 @@ namespace COFTester.Services
/// 數據採集主循環
/// 正確流程:1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置)
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
+ /// M30 复归型按钮触发测试,M31 状态标记位(1=测试中,0=停止)
///
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{
@@ -1563,11 +1692,30 @@ namespace COFTester.Services
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 数据采集配置: {totalPoints} 点, 间隔 {intervalMs:F1}ms");
- System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 注意:使用 M31=1 启动测试,M31=0 停止测试");
+ System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 监控 M31 状态:1=测试中,0=停止");
// 循环读取传感器数据(包含力值、位移和位置)
for (int i = 0; i < totalPoints && !token.IsCancellationRequested; i++)
{
+ // 每隔一定次数检查 M31 状态(避免频繁读取)
+ if (i % 10 == 0) // 每 10 个数据点检查一次
+ {
+ try
+ {
+ bool isTestRunning = await ReadTestStatusAsync();
+ if (!isTestRunning)
+ {
+ System.Diagnostics.Debug.WriteLine("[ModbusASCII] M31 = 0,PLC 已停止测试");
+ break; // 退出采集循环
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 读取 M31 状态失败: {ex.Message}");
+ // 读取失败不影响数据采集,继续执行
+ }
+ }
+
// 读取力值和位移
var dataPoint = await ReadSensorDataAsync();
@@ -1604,7 +1752,16 @@ namespace COFTester.Services
finally
{
_isAcquiring = false;
- System.Diagnostics.Debug.WriteLine("[ModbusASCII] 数据采集结束(不再发送 Stop 命令)");
+ System.Diagnostics.Debug.WriteLine("[ModbusASCII] 数据采集结束");
+
+ // 最后再检查一次 M31 状态
+ try
+ {
+ bool isTestRunning = await ReadTestStatusAsync();
+ System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 采集结束时 M31 = {(isTestRunning ? "1 (测试中)" : "0 (停止)")}");
+ }
+ catch { }
+
OnTestFinished();
}
}
diff --git a/CSI-H238M/CSI-H238M/ViewModels/ViewModel.cs b/CSI-H238M/CSI-H238M/ViewModels/ViewModel.cs
index 0b43b29..0b7d6d7 100644
--- a/CSI-H238M/CSI-H238M/ViewModels/ViewModel.cs
+++ b/CSI-H238M/CSI-H238M/ViewModels/ViewModel.cs
@@ -640,16 +640,40 @@ namespace COFTester.ViewModels
StatusMessage = $"开始测试 - {_selectedDirection}方向";
UpdateScottPlot();
- // M30 复归型 M31标注为 1开始,0停止
- // 向 M31 写入 1 开始测试
+ // M30 复归型按钮,M31 状态标记位(1=测试中,0=停止)
+ // 向 M30 写入复归型脉冲来触发测试启动
if (_daqService is ModbusServiceBase modbusService)
{
Task.Run(async () =>
{
try
{
- await modbusService.WriteStartStopRegisterAsync(1); // M31 = 1 开始
- System.Diagnostics.Debug.WriteLine("[ViewModel] M31 = 1 (开始测试)");
+ // 触发 M30 复归型按钮
+ await modbusService.TriggerTestStartAsync();
+ System.Diagnostics.Debug.WriteLine("[ViewModel] M30 脉冲已发送(触发测试启动)");
+
+ // 等待一小段时间让 PLC 处理
+ await Task.Delay(200);
+
+ // 读取 M31 确认测试是否真正开始
+ bool isTestRunning = await modbusService.ReadTestStatusAsync();
+
+ Application.Current?.Dispatcher.InvokeAsync(() =>
+ {
+ if (isTestRunning)
+ {
+ System.Diagnostics.Debug.WriteLine("[ViewModel] M31 = 1,测试已启动");
+ StatusMessage = "测试已启动";
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine("[ViewModel] M31 = 0,测试未启动");
+ StatusMessage = "测试启动失败,请检查设备";
+ // 启动失败时恢复按钮状态
+ IsTesting = false;
+ TestButtonText = Lang.StartTest;
+ }
+ });
}
catch (Exception ex)
{
@@ -670,31 +694,16 @@ namespace COFTester.ViewModels
private void StopTest()
{
- // M30 复归型 M31标注为 1开始,0停止
- // 向 M31 写入 0 停止测试
- if (_daqService is ModbusServiceBase modbusService)
- {
- Task.Run(async () =>
- {
- try
- {
- await modbusService.WriteStartStopRegisterAsync(0); // M31 = 0 停止
- System.Diagnostics.Debug.WriteLine("[ViewModel] M31 = 0 (停止测试)");
- }
- catch (Exception ex)
- {
- Application.Current?.Dispatcher.InvokeAsync(() =>
- {
- StatusMessage = $"停止测试失败: {ex.Message}";
- });
- }
- });
- }
+ // M30 复归型按钮,M31 状态标记位(1=测试中,0=停止)
+ // M31 是只读状态位,由 PLC 控制,我们只需停止数据采集
+ // PLC 会自动将 M31 设置为 0
_daqService.StopAcquisition();
IsTesting = false;
TestButtonText = Lang.StartTest; // 恢复按钮文本为"开始测试"
StatusMessage = Lang.TestStopped;
+
+ System.Diagnostics.Debug.WriteLine("[ViewModel] 停止测试,等待 PLC 将 M31 设置为 0");
}
private async void Reset()
diff --git a/CSI-H238M/CSI-H238M/Views/TestPage.xaml b/CSI-H238M/CSI-H238M/Views/TestPage.xaml
index 763358b..cd97cd3 100644
--- a/CSI-H238M/CSI-H238M/Views/TestPage.xaml
+++ b/CSI-H238M/CSI-H238M/Views/TestPage.xaml
@@ -257,8 +257,7 @@