更新20260605
This commit is contained in:
@@ -1342,6 +1342,24 @@
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
<Button MinWidth="132"
|
||||
Height="34"
|
||||
Margin="0,0,10,10"
|
||||
Padding="12,4"
|
||||
Command="{Binding CalibrateProximalPressureCommand}"
|
||||
Content="近端压力校准"
|
||||
Background="#FFB7791F"
|
||||
IsEnabled="{Binding CanModifySession}"
|
||||
ToolTip="M1300" />
|
||||
<Button MinWidth="132"
|
||||
Height="34"
|
||||
Margin="0,0,10,10"
|
||||
Padding="12,4"
|
||||
Command="{Binding CalibrateDistalPressureCommand}"
|
||||
Content="远端压力校准"
|
||||
Background="#FFB7791F"
|
||||
IsEnabled="{Binding CanModifySession}"
|
||||
ToolTip="M1301" />
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -16,6 +16,7 @@ public interface IModbusTelemetryService
|
||||
float? ReadHoldingFloatRegister(ushort address);
|
||||
bool WriteHoldingRegister(ushort address, ushort value);
|
||||
bool WriteHoldingFloatRegister(ushort address, float value);
|
||||
bool WriteCoil(ushort address, bool value, string operationName);
|
||||
// Legacy PLC coil path retained only for non-RS485 pump compatibility.
|
||||
void SetPumpRunning(string pumpKey, bool isRunning);
|
||||
void SetValveOpen(string valveKey, bool isOpen);
|
||||
|
||||
@@ -386,6 +386,45 @@ public sealed class ModbusTelemetryService : IModbusTelemetryService, IDisposabl
|
||||
}
|
||||
}
|
||||
|
||||
public bool WriteCoil(ushort address, bool value, string operationName)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_master is null)
|
||||
{
|
||||
_lastErrorMessage = $"PLC 离线,未执行{operationName}线圈写入:M{address}";
|
||||
Logger.Warning(
|
||||
"PLC 离线,跳过线圈写入,Operation={Operation},Coil=M{CoilAddress},Value={Value}",
|
||||
operationName,
|
||||
address,
|
||||
value);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_master.WriteSingleCoil(_slaveId, address, value);
|
||||
Logger.Information(
|
||||
"PLC 线圈写入成功,Operation={Operation},Coil=M{CoilAddress},Value={Value}",
|
||||
operationName,
|
||||
address,
|
||||
value);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(
|
||||
ex,
|
||||
"PLC 线圈写入失败,Operation={Operation},Coil=M{CoilAddress},Value={Value}",
|
||||
operationName,
|
||||
address,
|
||||
value);
|
||||
HandleConnectionFailure($"{operationName}线圈 M{address} 写入失败:{ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValveOpen(string valveKey, bool isOpen)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
|
||||
@@ -31,6 +31,9 @@ public partial class MainViewModel : ObservableObject, IDisposable
|
||||
private const string SettingsDirectoryName = "Cardiopulmonarybypasssystems";
|
||||
private const string LimitSettingsFileName = "manufacturer-limits.json";
|
||||
private const float EngineeringFloatVerificationTolerance = 0.0001f;
|
||||
private const ushort ProximalPressureCalibrationCoil = 1300;
|
||||
private const ushort DistalPressureCalibrationCoil = 1301;
|
||||
private static readonly TimeSpan PressureCalibrationPulseDuration = TimeSpan.FromMilliseconds(300);
|
||||
private static readonly (string Name, ushort Address)[] FlowCoefficientRegisters =
|
||||
[
|
||||
("流量系数1", 1006),
|
||||
@@ -62,6 +65,7 @@ public partial class MainViewModel : ObservableObject, IDisposable
|
||||
private string _lastTelemetryRefreshFailureMessage = string.Empty;
|
||||
private double? _proximalPressureRawKpa;
|
||||
private double? _distalPressureRawKpa;
|
||||
private bool _pressureCalibrationPulseInProgress;
|
||||
private static string ResolveLimitSettingsPath()
|
||||
{
|
||||
return Path.Combine(
|
||||
@@ -875,6 +879,71 @@ public partial class MainViewModel : ObservableObject, IDisposable
|
||||
_ = RefreshTelemetryAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CalibrateProximalPressure()
|
||||
{
|
||||
await PulsePressureCalibrationAsync("近端压力校准", ProximalPressureCalibrationCoil);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CalibrateDistalPressure()
|
||||
{
|
||||
await PulsePressureCalibrationAsync("远端压力校准", DistalPressureCalibrationCoil);
|
||||
}
|
||||
|
||||
private async Task PulsePressureCalibrationAsync(string calibrationName, ushort coilAddress)
|
||||
{
|
||||
if (!EnsureSessionEditable(calibrationName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pressureCalibrationPulseInProgress)
|
||||
{
|
||||
LatestAction = "压力校准指令正在执行,请稍后再操作。";
|
||||
return;
|
||||
}
|
||||
|
||||
_pressureCalibrationPulseInProgress = true;
|
||||
try
|
||||
{
|
||||
Logger.Information(
|
||||
"执行压力校准脉冲,CalibrationName={CalibrationName},Coil=M{CoilAddress},TelemetryOnline={TelemetryOnline}",
|
||||
calibrationName,
|
||||
coilAddress,
|
||||
IsTelemetryOnline);
|
||||
|
||||
if (!_telemetryService.WriteCoil(coilAddress, true, calibrationName))
|
||||
{
|
||||
LatestAction = IsTelemetryOnline
|
||||
? $"{calibrationName} 指令下发失败:{_telemetryService.LastErrorMessage}"
|
||||
: $"{calibrationName} 指令未下发,PLC 当前离线。";
|
||||
TraceEvents.Insert(0, NewTrace("压力校准", $"{calibrationName} / M{coilAddress}=1 下发失败"));
|
||||
return;
|
||||
}
|
||||
|
||||
LatestAction = $"{calibrationName} 已触发,M{coilAddress}=1。";
|
||||
TraceEvents.Insert(0, NewTrace("压力校准", $"{calibrationName} / M{coilAddress}=1"));
|
||||
|
||||
await Task.Delay(PressureCalibrationPulseDuration);
|
||||
|
||||
if (!_telemetryService.WriteCoil(coilAddress, false, calibrationName))
|
||||
{
|
||||
LatestAction = $"{calibrationName} 释放失败,请检查 PLC 线圈 M{coilAddress}。";
|
||||
TraceEvents.Insert(0, NewTrace("压力校准", $"{calibrationName} / M{coilAddress}=0 释放失败"));
|
||||
return;
|
||||
}
|
||||
|
||||
LatestAction = $"{calibrationName} 已完成校准脉冲。";
|
||||
TraceEvents.Insert(0, NewTrace("压力校准", $"{calibrationName} / M{coilAddress}=0"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_pressureCalibrationPulseInProgress = false;
|
||||
_ = RefreshTelemetryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ClearTrendData()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user