This commit is contained in:
GukSang.Jin
2026-01-30 13:39:58 +08:00
parent 3d51db9a4b
commit 07c8bd92da
3 changed files with 198 additions and 33 deletions

View File

@@ -442,10 +442,80 @@ namespace COFTester.Services
} }
/// <summary> /// <summary>
/// 写入启动/停止寄存器 M31 /// 触发测试启动按钮 M30复归型
/// M30 是复归型按钮需要写入脉冲信号true → 延迟 → false
/// 然后通过读取 M31 来判断测试是否真正开始1=测试中0=停止)
/// </summary>
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;
}
}
/// <summary>
/// 读取测试状态标记位 M31
/// M31: 1=测试中0=停止
/// </summary>
/// <returns>true=测试中false=停止</returns>
public virtual async Task<bool> 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;
}
}
/// <summary>
/// 写入启动/停止寄存器 M31已弃用请使用 TriggerTestStartAsync
/// M30 复归型, M31 标注为 1=开始0=停止 /// M30 复归型, M31 标注为 1=开始0=停止
/// </summary> /// </summary>
/// <param name="value">1=开始测试, 0=停止测试</param> /// <param name="value">1=开始测试, 0=停止测试</param>
[Obsolete("请使用 TriggerTestStartAsync() 触发 M30然后通过 ReadTestStatusAsync() 读取 M31 状态")]
public virtual async Task WriteStartStopRegisterAsync(ushort value) public virtual async Task WriteStartStopRegisterAsync(ushort value)
{ {
try try
@@ -649,6 +719,7 @@ namespace COFTester.Services
/// 數據採集主循環 /// 數據採集主循環
/// 正確流程1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置) /// 正確流程1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置)
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動 /// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
/// M30 复归型按钮触发测试M31 状态标记位1=测试中0=停止)
/// </summary> /// </summary>
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token) protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{ {
@@ -666,11 +737,30 @@ namespace COFTester.Services
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate); int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 数据采集配置: {totalPoints} 点, 间隔 {intervalMs:F1}ms"); 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++) 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 = 0PLC 已停止测试");
break; // 退出采集循环
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 读取 M31 状态失败: {ex.Message}");
// 读取失败不影响数据采集,继续执行
}
}
// 读取力值和位移 // 读取力值和位移
var dataPoint = await ReadSensorDataAsync(); var dataPoint = await ReadSensorDataAsync();
@@ -707,7 +797,16 @@ namespace COFTester.Services
finally finally
{ {
_isAcquiring = false; _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(); OnTestFinished();
} }
} }
@@ -1106,6 +1205,7 @@ namespace COFTester.Services
/// 數據採集主循環 /// 數據採集主循環
/// 正確流程1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置) /// 正確流程1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置)
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動 /// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
/// M30 复归型按钮触发测试M31 状态标记位1=测试中0=停止)
/// </summary> /// </summary>
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token) protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{ {
@@ -1123,11 +1223,30 @@ namespace COFTester.Services
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate); int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 数据采集配置: {totalPoints} 点, 间隔 {intervalMs:F1}ms"); 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++) 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 = 0PLC 已停止测试");
break; // 退出采集循环
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 读取 M31 状态失败: {ex.Message}");
// 读取失败不影响数据采集,继续执行
}
}
// 读取力值和位移 // 读取力值和位移
var dataPoint = await ReadSensorDataAsync(); var dataPoint = await ReadSensorDataAsync();
@@ -1164,7 +1283,16 @@ namespace COFTester.Services
finally finally
{ {
_isAcquiring = false; _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(); OnTestFinished();
} }
} }
@@ -1547,6 +1675,7 @@ namespace COFTester.Services
/// 數據採集主循環 /// 數據採集主循環
/// 正確流程1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置) /// 正確流程1. 寫入測試參數 → 2. 循環讀取數據(力值、位移、位置)
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動 /// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
/// M30 复归型按钮触发测试M31 状态标记位1=测试中0=停止)
/// </summary> /// </summary>
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token) protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{ {
@@ -1563,11 +1692,30 @@ namespace COFTester.Services
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate); int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 数据采集配置: {totalPoints} 点, 间隔 {intervalMs:F1}ms"); 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++) 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 = 0PLC 已停止测试");
break; // 退出采集循环
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 读取 M31 状态失败: {ex.Message}");
// 读取失败不影响数据采集,继续执行
}
}
// 读取力值和位移 // 读取力值和位移
var dataPoint = await ReadSensorDataAsync(); var dataPoint = await ReadSensorDataAsync();
@@ -1604,7 +1752,16 @@ namespace COFTester.Services
finally finally
{ {
_isAcquiring = false; _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(); OnTestFinished();
} }
} }

View File

@@ -640,16 +640,40 @@ namespace COFTester.ViewModels
StatusMessage = $"开始测试 - {_selectedDirection}方向"; StatusMessage = $"开始测试 - {_selectedDirection}方向";
UpdateScottPlot(); UpdateScottPlot();
// M30 复归型 M31标注为 1开始0停止 // M30 复归型按钮M31 状态标记位1=测试中0=停止
// 向 M31 写入 1 开始测试 // 向 M30 写入复归型脉冲来触发测试启动
if (_daqService is ModbusServiceBase modbusService) if (_daqService is ModbusServiceBase modbusService)
{ {
Task.Run(async () => Task.Run(async () =>
{ {
try try
{ {
await modbusService.WriteStartStopRegisterAsync(1); // M31 = 1 开始 // 触发 M30 复归型按钮
System.Diagnostics.Debug.WriteLine("[ViewModel] M31 = 1 (开始测试)"); 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) catch (Exception ex)
{ {
@@ -670,31 +694,16 @@ namespace COFTester.ViewModels
private void StopTest() private void StopTest()
{ {
// M30 复归型 M31标注为 1开始0停止 // M30 复归型按钮M31 状态标记位1=测试中0=停止
// M31 写入 0 停止测试 // M31 是只读状态位,由 PLC 控制,我们只需停止数据采集
if (_daqService is ModbusServiceBase modbusService) // PLC 会自动将 M31 设置为 0
{
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}";
});
}
});
}
_daqService.StopAcquisition(); _daqService.StopAcquisition();
IsTesting = false; IsTesting = false;
TestButtonText = Lang.StartTest; // 恢复按钮文本为"开始测试" TestButtonText = Lang.StartTest; // 恢复按钮文本为"开始测试"
StatusMessage = Lang.TestStopped; StatusMessage = Lang.TestStopped;
System.Diagnostics.Debug.WriteLine("[ViewModel] 停止测试,等待 PLC 将 M31 设置为 0");
} }
private async void Reset() private async void Reset()

View File

@@ -257,8 +257,7 @@
<!-- 开始测试按钮 - 动态显示状态:开始测试 → 测试中(不可点击) → 开始测试 --> <!-- 开始测试按钮 - 动态显示状态:开始测试 → 测试中(不可点击) → 开始测试 -->
<Button Content="{Binding TestButtonText}" Command="{Binding StartCommand}" <Button Content="{Binding TestButtonText}" Command="{Binding StartCommand}"
Height="70" Width="110" Background="#27AE60" Height="70" Width="110" Margin="5">
Style="{StaticResource IndustrialButtonStyle}" Margin="5">
<Button.Style> <Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource IndustrialButtonStyle}"> <Style TargetType="Button" BasedOn="{StaticResource IndustrialButtonStyle}">
<Setter Property="Background" Value="#27AE60"/> <Setter Property="Background" Value="#27AE60"/>