数据更新112

This commit is contained in:
GukSang.Jin
2026-06-03 15:35:55 +08:00
parent b5f768ecdb
commit 9d55347ff3
4 changed files with 332 additions and 210 deletions

View File

@@ -3,6 +3,7 @@ using NModbus;
using NModbus.IO;
using Serilog;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO.Ports;
using System.Threading;
@@ -38,10 +39,13 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
private SerialPort? adcPort;
private IModbusSerialMaster? plcMaster;
private IModbusSerialMaster? adcMaster;
private DeviceSettings settings = new("0.00", "0.00", "0.30", "0", "0.00", "0", "0.00", "0", "0.00", "COM7", "COM8", 115200);
private DeviceSettings settings = new("0.00", "0.00", "0.30", "0", "0.00", "0", "0.00", "0", "0.00", "COM3", "COM4", 115200);
private SlipDeviceSnapshot snapshot = SlipDeviceSnapshot.Offline();
private DateTime lastAdcErrorLoggedAt = DateTime.MinValue;
private DateTime lastPlcErrorLoggedAt = DateTime.MinValue;
private DateTime lastAdcDiagnosticLoggedAt = DateTime.MinValue;
private DateTime lastAdcCalibrationWarningLoggedAt = DateTime.MinValue;
private string lastAdcCalibrationWarning = string.Empty;
private double verticalLoadN;
private double horizontalFrictionN;
@@ -71,6 +75,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
public void Start(DeviceSettings deviceSettings)
{
settings = deviceSettings;
LogAdcCalibrationWarningIfNeeded();
Stop();
try
@@ -115,6 +120,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
public void UpdateSettings(DeviceSettings deviceSettings)
{
settings = deviceSettings;
LogAdcCalibrationWarningIfNeeded();
Log.Debug(
"设备设置已更新PLC={PlcPort}, ADC={AdcPort}, BaudRate={BaudRate}, TestSpeed={TestSpeed}, ManualSpeed={ManualSpeed}, ManualDisplacement={ManualDisplacement}",
settings.PlcPortName,
@@ -131,57 +137,23 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
public Task PulseResetAsync() => PulseCoilAsync(ResetCoil);
public async Task StartLiftAsync()
public async Task ApplyOldLiftAsync()
{
await WriteCoilAsync(LowerCoil, false);
await WriteCoilAsync(LiftCoil, true);
Log.Information("提升按下运M{LiftCoil}=1, M{LowerCoil}=0", LiftCoil, LowerCoil);
Log.Information("提升按钮按老代码执M{LowerCoil}=0, M{LiftCoil}=1", LowerCoil, LiftCoil);
}
public async Task StopLiftAsync()
{
await WriteCoilAsync(LiftCoil, false);
Log.Information("提升松开停止M{LiftCoil}=0", LiftCoil);
}
public async Task StartLowerAsync()
public async Task ApplyOldLowerAsync()
{
await WriteCoilAsync(LiftCoil, false);
await WriteCoilAsync(LowerCoil, true);
Log.Information("下降按下运M{LowerCoil}=1, M{LiftCoil}=0", LowerCoil, LiftCoil);
Log.Information("下降按钮按老代码执M{LiftCoil}=0, M{LowerCoil}=1", LiftCoil, LowerCoil);
}
public async Task StopLowerAsync()
{
await WriteCoilAsync(LowerCoil, false);
Log.Information("下降松开停止M{LowerCoil}=0", LowerCoil);
}
public Task ToggleOldMoveLeftAsync() => ToggleCoilAsync(MoveLeftCoil);
public async Task StartMoveLeftAsync()
{
await WriteCoilAsync(MoveRightCoil, false);
await WriteCoilAsync(MoveLeftCoil, true);
Log.Information("左移按下运行M{MoveLeftCoil}=1, M{MoveRightCoil}=0", MoveLeftCoil, MoveRightCoil);
}
public async Task StopMoveLeftAsync()
{
await WriteCoilAsync(MoveLeftCoil, false);
Log.Information("左移松开停止M{MoveLeftCoil}=0", MoveLeftCoil);
}
public async Task StartMoveRightAsync()
{
await WriteCoilAsync(MoveLeftCoil, false);
await WriteCoilAsync(MoveRightCoil, true);
Log.Information("右移按下运行M{MoveRightCoil}=1, M{MoveLeftCoil}=0", MoveRightCoil, MoveLeftCoil);
}
public async Task StopMoveRightAsync()
{
await WriteCoilAsync(MoveRightCoil, false);
Log.Information("右移松开停止M{MoveRightCoil}=0", MoveRightCoil);
}
public Task ToggleOldMoveRightAsync() => ToggleCoilAsync(MoveRightCoil);
public async Task StopAllMotionAsync()
{
@@ -332,13 +304,8 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
var friction1Raw = UshortToInt(data[6], data[7]);
var friction2Raw = UshortToInt(data[2], data[3]);
var pressure = ConvertAdc(pressureRaw, settings.NormalPressureZero, settings.NormalPressureCoefficient);
// Keep the old instrument channel wiring: ADC 6/7 uses zero 1 with coefficient 2,
// and ADC 2/3 uses zero 2 with coefficient 1.
var friction1 = ConvertAdc(friction1Raw, settings.FrictionZero1, settings.FrictionCoefficient2);
var friction2 = ConvertAdc(friction2Raw, settings.FrictionZero2, settings.FrictionCoefficient1);
var friction = (friction1 + friction2) * -1.0;
var conversion = ConvertAdcReadings(pressureRaw, friction1Raw, friction2Raw);
LogAdcDiagnostic(pressureRaw, friction1Raw, friction2Raw, conversion);
lock (sync)
{
@@ -346,14 +313,25 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
frictionRawValue1 = friction1Raw;
frictionRawValue2 = friction2Raw;
hasAdcRawValues = true;
verticalLoadN = pressure;
horizontalFrictionN = friction;
adcLastError = string.Empty;
isAdcConnected = true;
if (conversion.IsValid)
{
verticalLoadN = conversion.Pressure;
horizontalFrictionN = conversion.Friction;
adcLastError = string.Empty;
isAdcConnected = true;
}
else
{
verticalLoadN = 0;
horizontalFrictionN = 0;
adcLastError = conversion.Error;
isAdcConnected = false;
}
RefreshSnapshotLocked();
}
Thread.Sleep(10);
Thread.Sleep(conversion.IsValid ? 10 : 100);
}
catch (Exception ex)
{
@@ -405,13 +383,19 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
}
}
private async Task PulseCoilAsync(ushort coil)
{
Log.Information("发送 PLC 脉冲线圈M{Coil}", coil);
await WriteCoilAsync(coil, true);
await Task.Delay(80);
await WriteCoilAsync(coil, false);
}
private Task PulseCoilAsync(ushort coil) =>
Task.Run(() =>
{
lock (plcIoLock)
{
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
Log.Information("按老代码逻辑发送 PLC 脉冲线圈M{Coil}=true -> false", coil);
master.WriteSingleCoil(SlaveId, coil, true);
Log.Debug("写入 PLC 线圈M{Coil}=true", coil);
master.WriteSingleCoil(SlaveId, coil, false);
Log.Debug("写入 PLC 线圈M{Coil}=false", coil);
}
});
private Task ToggleCoilAsync(ushort coil) =>
Task.Run(() =>
@@ -532,24 +516,141 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
error);
}
private static double ConvertAdc(int rawValue, string zeroText, string coefficientText)
private AdcConversionResult ConvertAdcReadings(int pressureRaw, int friction1Raw, int friction2Raw)
{
var zero = ParseDouble(zeroText);
var coefficient = ParseDouble(coefficientText);
if (Math.Abs(coefficient) < 0.0001)
if (!ValidateAdcSettings(out var pressureZero, out var pressureCoefficient, out var frictionZero1, out var frictionCoefficient1, out var frictionZero2, out var frictionCoefficient2, out var error))
{
return 0;
return AdcConversionResult.Invalid(error);
}
return (rawValue - zero) / coefficient;
var pressure = ConvertAdc(pressureRaw, pressureZero, pressureCoefficient);
// Keep the old instrument channel wiring: ADC 6/7 uses zero 1 with coefficient 2,
// and ADC 2/3 uses zero 2 with coefficient 1.
var friction1 = ConvertAdc(friction1Raw, frictionZero1, frictionCoefficient2);
var friction2 = ConvertAdc(friction2Raw, frictionZero2, frictionCoefficient1);
var friction = (friction1 + friction2) * -1.0;
return AdcConversionResult.Valid(pressure, friction1, friction2, friction);
}
private static double ParseDouble(string value) =>
double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var invariantValue)
? invariantValue
: double.TryParse(value, NumberStyles.Float, CultureInfo.CurrentCulture, out var localValue)
? localValue
: 0;
private bool ValidateAdcSettings(
out double pressureZero,
out double pressureCoefficient,
out double frictionZero1,
out double frictionCoefficient1,
out double frictionZero2,
out double frictionCoefficient2,
out string error)
{
var invalid = new List<string>();
TryParseSetting(settings.NormalPressureZero, "正压力零点", false, invalid, out pressureZero);
TryParseSetting(settings.NormalPressureCoefficient, "正压力校准系数", true, invalid, out pressureCoefficient);
TryParseSetting(settings.FrictionZero1, "摩擦1零点", false, invalid, out frictionZero1);
TryParseSetting(settings.FrictionCoefficient1, "摩擦1校准系数", true, invalid, out frictionCoefficient1);
TryParseSetting(settings.FrictionZero2, "摩擦2零点", false, invalid, out frictionZero2);
TryParseSetting(settings.FrictionCoefficient2, "摩擦2校准系数", true, invalid, out frictionCoefficient2);
if (invalid.Count == 0)
{
error = string.Empty;
return true;
}
error = "ADC 校准参数无效:" + string.Join("、", invalid) + ";请填写旧机实际标定值后再测试";
return false;
}
private void LogAdcCalibrationWarningIfNeeded()
{
if (!ValidateAdcSettings(out _, out _, out _, out _, out _, out _, out var error))
{
var now = DateTime.UtcNow;
if (error == lastAdcCalibrationWarning && now - lastAdcCalibrationWarningLoggedAt < TimeSpan.FromSeconds(5))
{
return;
}
lastAdcCalibrationWarning = error;
lastAdcCalibrationWarningLoggedAt = now;
Log.Warning(
"{Error}. CurrentSettings: NormalPressureZero={NormalPressureZero}, NormalPressureCoefficient={NormalPressureCoefficient}, FrictionZero1={FrictionZero1}, FrictionCoefficient1={FrictionCoefficient1}, FrictionZero2={FrictionZero2}, FrictionCoefficient2={FrictionCoefficient2}",
error,
settings.NormalPressureZero,
settings.NormalPressureCoefficient,
settings.FrictionZero1,
settings.FrictionCoefficient1,
settings.FrictionZero2,
settings.FrictionCoefficient2);
return;
}
lastAdcCalibrationWarning = string.Empty;
}
private void LogAdcDiagnostic(int pressureRaw, int friction1Raw, int friction2Raw, AdcConversionResult conversion)
{
var now = DateTime.UtcNow;
if (now - lastAdcDiagnosticLoggedAt < TimeSpan.FromSeconds(conversion.IsValid ? 1 : 5))
{
return;
}
lastAdcDiagnosticLoggedAt = now;
if (conversion.IsValid)
{
Log.Debug(
"ADC 采样RawPressure={RawPressure}, RawFriction1={RawFriction1}, RawFriction2={RawFriction2}, Pressure={Pressure:F3} N, Friction1={Friction1:F3} N, Friction2={Friction2:F3} N, Friction={Friction:F3} N, Coefficients=[P:{PressureCoefficient}, F1:{FrictionCoefficient1}, F2:{FrictionCoefficient2}], Zeros=[P:{PressureZero}, F1:{FrictionZero1}, F2:{FrictionZero2}]",
pressureRaw,
friction1Raw,
friction2Raw,
conversion.Pressure,
conversion.Friction1,
conversion.Friction2,
conversion.Friction,
settings.NormalPressureCoefficient,
settings.FrictionCoefficient1,
settings.FrictionCoefficient2,
settings.NormalPressureZero,
settings.FrictionZero1,
settings.FrictionZero2);
}
else
{
Log.Warning(
"ADC 采样无效:{Error}. RawPressure={RawPressure}, RawFriction1={RawFriction1}, RawFriction2={RawFriction2}, Coefficients=[P:{PressureCoefficient}, F1:{FrictionCoefficient1}, F2:{FrictionCoefficient2}], Zeros=[P:{PressureZero}, F1:{FrictionZero1}, F2:{FrictionZero2}]",
conversion.Error,
pressureRaw,
friction1Raw,
friction2Raw,
settings.NormalPressureCoefficient,
settings.FrictionCoefficient1,
settings.FrictionCoefficient2,
settings.NormalPressureZero,
settings.FrictionZero1,
settings.FrictionZero2);
}
}
private static double ConvertAdc(int rawValue, double zero, double coefficient) =>
(rawValue - zero) / coefficient;
private static void TryParseSetting(string value, string label, bool requireNonZero, List<string> invalid, out double numericValue)
{
if (!TryParseDouble(value, out numericValue))
{
invalid.Add($"{label}=\"{value}\"");
return;
}
if (requireNonZero && Math.Abs(numericValue) < 0.0001)
{
invalid.Add($"{label}=0");
}
}
private static bool TryParseDouble(string value, out double numericValue) =>
double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out numericValue)
|| double.TryParse(value, NumberStyles.Float, CultureInfo.CurrentCulture, out numericValue);
private static int UshortToInt(ushort first, ushort second)
{
@@ -605,5 +706,20 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
{
}
}
private readonly record struct AdcConversionResult(
bool IsValid,
double Pressure,
double Friction1,
double Friction2,
double Friction,
string Error)
{
public static AdcConversionResult Valid(double pressure, double friction1, double friction2, double friction) =>
new(true, pressure, friction1, friction2, friction, string.Empty);
public static AdcConversionResult Invalid(string error) =>
new(false, 0, 0, 0, 0, error);
}
}
}