This commit is contained in:
GukSang.Jin
2026-01-29 18:11:57 +08:00
parent aa2c73daec
commit 4455ba4c9a
3 changed files with 48 additions and 213 deletions

View File

@@ -167,9 +167,9 @@ namespace COFTester.Models
public ushort VerticalPositionRegister { get; set; } = 12; // D12 升降位置只讀2個寄存器Float
public ushort HorizontalPositionRegister { get; set; } = 16; // D16 水平位置只讀2個寄存器Float
// === 控制寄存器(讀寫)===
public ushort ControlRegister { get; set; } = 10; // 控制命令0=停止1=開始2=重置
public ushort StatusRegister { get; set; } = 12; // 設備狀態寄存器
// === 控制寄存器已移除 ===
// 注意ControlRegister (10) 和 StatusRegister (12) 不存在
// 正確流程1. 寫入測試參數 → 2. 方向控制(M0-M3寫入1) → 3. 循環讀取數據 → 4. 方向控制(M0-M3寫入0)
// === 測試參數寄存器(寫入)===
public ushort SledMassRegister { get; set; } = 20; // 滑塊質量 (g)Float2個寄存器
@@ -217,9 +217,9 @@ namespace COFTester.Models
public ushort VerticalPositionRegister { get; set; } = 12; // D12 升降位置只讀2個寄存器Float
public ushort HorizontalPositionRegister { get; set; } = 16; // D16 水平位置只讀2個寄存器Float
// === 控制寄存器(讀寫)===
public ushort ControlRegister { get; set; } = 10; // 控制命令0=停止1=開始2=重置
public ushort StatusRegister { get; set; } = 12; // 設備狀態寄存器
// === 控制寄存器已移除 ===
// 注意ControlRegister (10) 和 StatusRegister (12) 不存在
// 正確流程1. 寫入測試參數 → 2. 方向控制(M0-M3寫入1) → 3. 循環讀取數據 → 4. 方向控制(M0-M3寫入0)
// === 測試參數寄存器(寫入)===
public ushort SledMassRegister { get; set; } = 20; // 滑塊質量 (g)Float2個寄存器

View File

@@ -62,15 +62,17 @@ namespace COFTester.Services
}
/// <summary>
/// Modbus 控制命令枚舉
/// Modbus 控制命令枚舉(已廢棄)
/// 注意ControlRegister 不存在,請使用方向控制 M0-M3
/// </summary>
[Obsolete("ControlRegister 不存在,請使用 DirectionControl 方法")]
public enum ModbusControlCommand : ushort
{
/// <summary>停止測試</summary>
/// <summary>停止測試(已廢棄)</summary>
Stop = 0,
/// <summary>開始測試</summary>
/// <summary>開始測試(已廢棄)</summary>
Start = 1,
/// <summary>重置系統</summary>
/// <summary>重置系統(已廢棄)</summary>
Reset = 2
}
@@ -123,14 +125,24 @@ namespace COFTester.Services
/// Modbus 通信服務基類
/// 支援 ModbusTCP、ModbusRTU、SerialPort (NModbus 3.0.81 + .NET 8)
///
/// 正確控制流程:
/// 1. 寫入測試參數 (WriteTestParametersAsync)
/// 2. 方向控制開始 (M0-M3 寫入 1) - 上升/下降/向左/向右
/// 3. 循環讀取數據 (ReadSensorDataAsync)
/// 4. 方向控制停止 (M0-M3 寫入 0)
///
/// 寄存器映射表:
/// ┌─────────────────┬──────────┬──────────┬─────────────────────────────┐
/// │ 寄存器名稱 │ 地址 │ 類型 │ 說明 │
/// ├─────────────────┼──────────┼──────────┼─────────────────────────────┤
/// │ Force │ 0-1 │ Float │ 力值 (N),只讀 │
/// │ Displacement │ 2-3 │ Float │ 位移 (mm),只讀 │
/// │ Control10 │ UInt16 │ 0=停止1=開始2=重置
/// │ Status │ 12 │ UInt16 │ 設備狀態
/// │ M0 (Up) │ 0 Coil │ 上升控制1=開始0=停止
/// │ M1 (Down) │ 1 │ Coil │ 下降控制1=開始0=停止
/// │ M2 (Right) │ 2 │ Coil │ 向右控制1=開始0=停止 │
/// │ M3 (Left) │ 3 │ Coil │ 向左控制1=開始0=停止 │
/// │ M1300 │ 1300 │ Coil │ 零點標定(脉冲信号) │
/// │ M1301 │ 1301 │ Coil │ 復位按鈕(切換型) │
/// │ SledMass │ 20-21 │ Float │ 滑塊質量 (g) │
/// │ TestSpeed │ 22-23 │ Float │ 測試速度 (mm/min) │
/// │ TestDuration │ 24-25 │ Float │ 測試時長 (s) │
@@ -171,7 +183,8 @@ namespace COFTester.Services
/// <summary>
/// 開始數據採集
/// 流程1. 寫入測試參數 → 2. 發送開始命令(1) → 3. 循環讀取數據 → 4. 發送停止命令(0)
/// 正確流程1. 寫入測試參數 → 2. 方向控制(M0-M3寫入1) → 3. 循環讀取數據 → 4. 方向控制(M0-M3寫入0)
/// 注意:不再使用 Start/Stop 命令,改用方向控制
/// </summary>
public virtual void StartAcquisition(TestParameters parameters)
{
@@ -194,7 +207,8 @@ namespace COFTester.Services
}
/// <summary>
/// 停止數據採集,發送停止命令(0)
/// 停止數據採集
/// 注意:不再發送停止命令,由方向控制(M0-M3寫入0)來停止
/// </summary>
public virtual void StopAcquisition()
{
@@ -203,16 +217,18 @@ namespace COFTester.Services
}
/// <summary>
/// 重置傳感器,發送重置命令(2)
/// 重置傳感器(已廢棄)
/// 注意ControlRegister 不存在,請使用 ToggleResetAsync(1301) 進行復位
/// </summary>
[Obsolete("請使用 ToggleResetAsync(1301) 進行復位")]
public virtual async void ResetSensors()
{
try
{
if (_modbusMaster != null && _isConnected)
{
System.Diagnostics.Debug.WriteLine("[Modbus] 發送重置命令 (2)");
await WriteControlRegisterAsync((ushort)ModbusControlCommand.Reset);
System.Diagnostics.Debug.WriteLine("[Modbus] 警告ResetSensors 已廢棄,請使用 ToggleResetAsync(1301)");
await ToggleResetAsync(1301);
}
}
catch (Exception ex)
@@ -427,7 +443,6 @@ namespace COFTester.Services
protected abstract Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token);
protected abstract Task<TestDataPoint?> ReadSensorDataAsync();
protected abstract Task WriteControlRegisterAsync(ushort value);
/// <summary>
/// 寫入校準命令到 M1300 寄存器
@@ -443,11 +458,6 @@ namespace COFTester.Services
/// 寫入測試參數到 PLC 寄存器
/// </summary>
protected abstract Task WriteTestParametersAsync(TestParameters parameters);
/// <summary>
/// 讀取設備狀態
/// </summary>
protected abstract Task<ModbusDeviceStatus> ReadDeviceStatusAsync();
/// <summary>
/// 將兩個寄存器轉換為 Float (IEEE 754 Big-Endian)
@@ -584,7 +594,8 @@ namespace COFTester.Services
/// <summary>
/// 數據採集主循環
/// 流程:寫入參數 → 開始(1) → 讀取數據 → 停止(0)
/// 正確流程:1. 寫入測試參數 → 2. 循環讀取數據
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
/// </summary>
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{
@@ -598,14 +609,12 @@ namespace COFTester.Services
// 短暫延遲確保 PLC 處理完參數
await Task.Delay(100, token);
// 2. 發送開始命令 (1)
System.Diagnostics.Debug.WriteLine("[ModbusTCP] 發送開始命令 (1)");
await WriteControlRegisterAsync((ushort)ModbusControlCommand.Start);
// 2. 循環讀取傳感器數據(不再發送 Start 命令)
double intervalMs = 1000.0 / parameters.SamplingRate;
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 開始數據採集: {totalPoints} 點, 間隔 {intervalMs:F1}ms");
System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 注意:請使用方向控制按鈕(M0-M3)來啟動運動");
// 3. 循環讀取傳感器數據
for (int i = 0; i < totalPoints && !token.IsCancellationRequested; i++)
@@ -629,18 +638,7 @@ namespace COFTester.Services
finally
{
_isAcquiring = false;
// 4. 發送停止命令 (0)
try
{
System.Diagnostics.Debug.WriteLine("[ModbusTCP] 發送停止命令 (0)");
await WriteControlRegisterAsync((ushort)ModbusControlCommand.Stop);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 停止命令發送失敗: {ex.Message}");
}
System.Diagnostics.Debug.WriteLine("[ModbusTCP] 數據採集結束(不再發送 Stop 命令)");
OnTestFinished();
}
}
@@ -672,22 +670,6 @@ namespace COFTester.Services
}
}
/// <summary>
/// 寫入控制命令
/// 0 = 停止測試
/// 1 = 開始測試
/// 2 = 重置系統
/// </summary>
protected override async Task WriteControlRegisterAsync(ushort value)
{
if (_modbusMaster != null)
{
await _modbusMaster.WriteSingleRegisterAsync(
_config.SlaveId, _config.ControlRegister, value);
System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 控制寄存器[{_config.ControlRegister}] = {value}");
}
}
/// <summary>
/// 寫入校準命令到 M1300
/// 0 = 無操作
@@ -720,51 +702,12 @@ namespace COFTester.Services
/// <summary>
/// 寫入測試參數到 PLC 寄存器
/// 參數映射:
/// - 滑塊質量 → 寄存器 20-21 (Float)
/// - 測試速度 → 寄存器 22-23 (Float)
/// - 測試時長 → 寄存器 24-25 (Float)
/// - 測試行程 → 寄存器 26-27 (Float)
/// - 採樣頻率 → 寄存器 28-29 (Float)
/// </summary>
protected override async Task WriteTestParametersAsync(TestParameters parameters)
{
if (_modbusMaster == null) return;
try
{
// 準備所有參數數據5個Float = 10個寄存器
var allRegisters = new ushort[10];
var sledMassRegs = ConvertFloatToRegisters((float)parameters.SledMass);
var testSpeedRegs = ConvertFloatToRegisters((float)parameters.TestSpeed);
var testDurationRegs = ConvertFloatToRegisters((float)parameters.TestDuration);
var testStrokeRegs = ConvertFloatToRegisters((float)parameters.TestStroke);
var samplingRateRegs = ConvertFloatToRegisters((float)parameters.SamplingRate);
// 組合所有寄存器
allRegisters[0] = sledMassRegs[0];
allRegisters[1] = sledMassRegs[1];
allRegisters[2] = testSpeedRegs[0];
allRegisters[3] = testSpeedRegs[1];
allRegisters[4] = testDurationRegs[0];
allRegisters[5] = testDurationRegs[1];
allRegisters[6] = testStrokeRegs[0];
allRegisters[7] = testStrokeRegs[1];
allRegisters[8] = samplingRateRegs[0];
allRegisters[9] = samplingRateRegs[1];
// 批量寫入所有參數(從寄存器 20 開始)
await _modbusMaster.WriteMultipleRegistersAsync(
_config.SlaveId, _config.SledMassRegister, allRegisters);
System.Diagnostics.Debug.WriteLine($"[ModbusTCP] 參數寫入完成:");
System.Diagnostics.Debug.WriteLine($" 滑塊質量: {parameters.SledMass}g → 寄存器[{_config.SledMassRegister}]");
System.Diagnostics.Debug.WriteLine($" 測試速度: {parameters.TestSpeed}mm/min → 寄存器[{_config.TestSpeedRegister}]");
System.Diagnostics.Debug.WriteLine($" 測試時長: {parameters.TestDuration}s → 寄存器[{_config.TestDurationRegister}]");
System.Diagnostics.Debug.WriteLine($" 測試行程: {parameters.TestStroke}mm → 寄存器[{_config.TestStrokeRegister}]");
System.Diagnostics.Debug.WriteLine($" 採樣頻率: {parameters.SamplingRate}Hz → 寄存器[{_config.SamplingRateRegister}]");
// 寫入手動控制參數
await WriteManualControlParametersAsync(parameters);
}
@@ -866,25 +809,6 @@ namespace COFTester.Services
}
}
/// <summary>
/// 讀取設備狀態
/// </summary>
protected override async Task<ModbusDeviceStatus> ReadDeviceStatusAsync()
{
if (_modbusMaster == null) return ModbusDeviceStatus.Error;
try
{
var registers = await _modbusMaster.ReadHoldingRegistersAsync(
_config.SlaveId, _config.StatusRegister, 1);
return (ModbusDeviceStatus)registers[0];
}
catch
{
return ModbusDeviceStatus.Error;
}
}
public override void Dispose()
{
base.Dispose();
@@ -991,7 +915,8 @@ namespace COFTester.Services
/// <summary>
/// 數據採集主循環
/// 流程:寫入參數 → 開始(1) → 讀取數據 → 停止(0)
/// 正確流程:1. 寫入測試參數 → 2. 循環讀取數據
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
/// </summary>
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{
@@ -1005,14 +930,12 @@ namespace COFTester.Services
// 短暫延遲確保 PLC 處理完參數
await Task.Delay(100, token);
// 2. 發送開始命令 (1)
System.Diagnostics.Debug.WriteLine("[ModbusRTU] 發送開始命令 (1)");
await WriteControlRegisterAsync((ushort)ModbusControlCommand.Start);
// 2. 循環讀取傳感器數據(不再發送 Start 命令)
double intervalMs = 1000.0 / parameters.SamplingRate;
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 開始數據採集: {totalPoints} 點, 間隔 {intervalMs:F1}ms");
System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 注意:請使用方向控制按鈕(M0-M3)來啟動運動");
// 3. 循環讀取傳感器數據
for (int i = 0; i < totalPoints && !token.IsCancellationRequested; i++)
@@ -1036,18 +959,7 @@ namespace COFTester.Services
finally
{
_isAcquiring = false;
// 4. 發送停止命令 (0)
try
{
System.Diagnostics.Debug.WriteLine("[ModbusRTU] 發送停止命令 (0)");
await WriteControlRegisterAsync((ushort)ModbusControlCommand.Stop);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 停止命令發送失敗: {ex.Message}");
}
System.Diagnostics.Debug.WriteLine("[ModbusRTU] 數據採集結束(不再發送 Stop 命令)");
OnTestFinished();
}
}
@@ -1078,22 +990,6 @@ namespace COFTester.Services
}
}
/// <summary>
/// 寫入控制命令
/// 0 = 停止測試
/// 1 = 開始測試
/// 2 = 重置系統
/// </summary>
protected override async Task WriteControlRegisterAsync(ushort value)
{
if (_modbusMaster != null)
{
await _modbusMaster.WriteSingleRegisterAsync(
_config.SlaveId, _config.ControlRegister, value);
System.Diagnostics.Debug.WriteLine($"[ModbusRTU] 控制寄存器[{_config.ControlRegister}] = {value}");
}
}
/// <summary>
/// 寫入校準命令到 M1300
/// 0 = 無操作
@@ -1247,25 +1143,6 @@ namespace COFTester.Services
}
}
/// <summary>
/// 讀取設備狀態
/// </summary>
protected override async Task<ModbusDeviceStatus> ReadDeviceStatusAsync()
{
if (_modbusMaster == null) return ModbusDeviceStatus.Error;
try
{
var registers = await _modbusMaster.ReadHoldingRegistersAsync(
_config.SlaveId, _config.StatusRegister, 1);
return (ModbusDeviceStatus)registers[0];
}
catch
{
return ModbusDeviceStatus.Error;
}
}
public override void Dispose()
{
base.Dispose();
@@ -1364,7 +1241,8 @@ namespace COFTester.Services
/// <summary>
/// 數據採集主循環
/// 流程:寫入參數 → 開始(1) → 讀取數據 → 停止(0)
/// 正確流程:1. 寫入測試參數 → 2. 循環讀取數據
/// 注意:不再發送 Start/Stop 命令,由方向控制(M0-M3)來控制運動
/// </summary>
protected override async Task AcquisitionLoopAsync(TestParameters parameters, CancellationToken token)
{
@@ -1377,14 +1255,12 @@ namespace COFTester.Services
await Task.Delay(100, token);
// 2. 發送開始命令 (1)
System.Diagnostics.Debug.WriteLine("[ModbusASCII] 發送開始命令 (1)");
await WriteControlRegisterAsync((ushort)ModbusControlCommand.Start);
// 2. 循環讀取傳感器數據(不再發送 Start 命令)
double intervalMs = 1000.0 / parameters.SamplingRate;
int totalPoints = (int)(parameters.TestDuration * parameters.SamplingRate);
System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 開始數據採集: {totalPoints} 點, 間隔 {intervalMs:F1}ms");
System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 注意:請使用方向控制按鈕(M0-M3)來啟動運動");
// 3. 循環讀取傳感器數據
for (int i = 0; i < totalPoints && !token.IsCancellationRequested; i++)
@@ -1408,18 +1284,7 @@ namespace COFTester.Services
finally
{
_isAcquiring = false;
// 4. 發送停止命令 (0)
try
{
System.Diagnostics.Debug.WriteLine("[ModbusASCII] 發送停止命令 (0)");
await WriteControlRegisterAsync((ushort)ModbusControlCommand.Stop);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 停止命令發送失敗: {ex.Message}");
}
System.Diagnostics.Debug.WriteLine("[ModbusASCII] 數據採集結束(不再發送 Stop 命令)");
OnTestFinished();
}
}
@@ -1447,16 +1312,6 @@ namespace COFTester.Services
}
}
protected override async Task WriteControlRegisterAsync(ushort value)
{
if (_modbusMaster != null)
{
await _modbusMaster.WriteSingleRegisterAsync(
_config.SlaveId, _config.ControlRegister, value);
System.Diagnostics.Debug.WriteLine($"[ModbusASCII] 控制寄存器[{_config.ControlRegister}] = {value}");
}
}
/// <summary>
/// 寫入校準命令到 M1300
/// </summary>
@@ -1598,22 +1453,6 @@ namespace COFTester.Services
}
}
protected override async Task<ModbusDeviceStatus> ReadDeviceStatusAsync()
{
if (_modbusMaster == null) return ModbusDeviceStatus.Error;
try
{
var registers = await _modbusMaster.ReadHoldingRegistersAsync(
_config.SlaveId, _config.StatusRegister, 1);
return (ModbusDeviceStatus)registers[0];
}
catch
{
return ModbusDeviceStatus.Error;
}
}
public override void Dispose()
{
base.Dispose();

View File

@@ -12,8 +12,6 @@
"CurveDisplacementRegister": 360,
"VerticalPositionRegister": 12,
"HorizontalPositionRegister": 16,
"ControlRegister": 10,
"StatusRegister": 12,
"SledMassRegister": 20,
"TestSpeedRegister": 22,
"TestDurationRegister": 24,
@@ -46,8 +44,6 @@
"CurveDisplacementRegister": 360,
"VerticalPositionRegister": 12,
"HorizontalPositionRegister": 16,
"ControlRegister": 10,
"StatusRegister": 12,
"SledMassRegister": 20,
"TestSpeedRegister": 22,
"TestDurationRegister": 24,