更新20260601
This commit is contained in:
@@ -24,5 +24,6 @@ public sealed record RealtimeSnapshot(
|
||||
double MassLoss,
|
||||
double MassLossRate,
|
||||
int IgnitionSeconds,
|
||||
int DeviceTestSeconds,
|
||||
int TestSeconds,
|
||||
double TotalSmoke);
|
||||
|
||||
@@ -23,8 +23,12 @@ public sealed class ExperimentDataService : IExperimentDataService
|
||||
private double _accumulatedTotalSmoke;
|
||||
private int? _lastAccumulationSeconds;
|
||||
private int? _lastRecordedSeconds;
|
||||
private int? _deviceTestTimerBaselineSeconds;
|
||||
private bool _hasObservedDeviceTestTimerReset;
|
||||
private bool _isTestRunning;
|
||||
private bool _isRecordingActive;
|
||||
private bool _isPollingSnapshot;
|
||||
private RealtimeSnapshot? _completedSnapshot;
|
||||
|
||||
public ExperimentDataService(IRealtimeDataService realtimeDataService)
|
||||
{
|
||||
@@ -50,26 +54,26 @@ public sealed class ExperimentDataService : IExperimentDataService
|
||||
{
|
||||
Records.Clear();
|
||||
ResetComputedState();
|
||||
_completedSnapshot = null;
|
||||
_isTestRunning = true;
|
||||
_testClock.Restart();
|
||||
|
||||
if (double.IsFinite(CurrentSnapshot.CurrentMass))
|
||||
{
|
||||
_initialMass = CurrentSnapshot.CurrentMass;
|
||||
}
|
||||
_isRecordingActive = false;
|
||||
_testClock.Reset();
|
||||
PrepareDeviceTestTimerGate();
|
||||
PublishSnapshot(BuildWaitingSnapshot(CurrentSnapshot));
|
||||
|
||||
QueueImmediateDevicePoll();
|
||||
|
||||
Log.Information(
|
||||
"Experiment test started. InitialMass={InitialMass}, SystemSeconds={TestSeconds}.",
|
||||
_initialMass,
|
||||
CurrentSnapshot.TestSeconds);
|
||||
"Experiment test started. Waiting for D1015 timing marker. DeviceTimerBaseline={DeviceTimerBaseline}.",
|
||||
_deviceTestTimerBaselineSeconds);
|
||||
}
|
||||
|
||||
public void StopTest()
|
||||
{
|
||||
_isTestRunning = false;
|
||||
_isRecordingActive = false;
|
||||
_testClock.Stop();
|
||||
_completedSnapshot = BuildCompletedSnapshot(CurrentSnapshot);
|
||||
PublishSnapshot(BuildIdleSnapshot(CurrentSnapshot));
|
||||
|
||||
Log.Information(
|
||||
@@ -85,6 +89,8 @@ public sealed class ExperimentDataService : IExperimentDataService
|
||||
if (!_isTestRunning)
|
||||
{
|
||||
_testClock.Reset();
|
||||
_isRecordingActive = false;
|
||||
_completedSnapshot = null;
|
||||
ResetComputedState();
|
||||
PublishSnapshot(BuildIdleSnapshot(CurrentSnapshot));
|
||||
}
|
||||
@@ -110,7 +116,7 @@ public sealed class ExperimentDataService : IExperimentDataService
|
||||
|
||||
try
|
||||
{
|
||||
var elapsed = _isTestRunning ? _testClock.Elapsed : TimeSpan.Zero;
|
||||
var elapsed = _isTestRunning && _isRecordingActive ? _testClock.Elapsed : TimeSpan.Zero;
|
||||
var snapshot = await Task.Run(() => _realtimeDataService.GetCurrentSnapshot(elapsed));
|
||||
if (_isTestRunning)
|
||||
{
|
||||
@@ -157,6 +163,28 @@ public sealed class ExperimentDataService : IExperimentDataService
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_isRecordingActive)
|
||||
{
|
||||
if (!ShouldStartTimedRecording(snapshot))
|
||||
{
|
||||
PublishSnapshot(BuildWaitingSnapshot(snapshot));
|
||||
return;
|
||||
}
|
||||
|
||||
BeginTimedRecording(snapshot);
|
||||
snapshot = snapshot with
|
||||
{
|
||||
TestSeconds = 0
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
snapshot = snapshot with
|
||||
{
|
||||
TestSeconds = GetElapsedTestSeconds()
|
||||
};
|
||||
}
|
||||
|
||||
var currentSecond = Math.Max(0, snapshot.TestSeconds);
|
||||
if (_lastRecordedSeconds.HasValue && currentSecond <= _lastRecordedSeconds.Value)
|
||||
{
|
||||
@@ -183,6 +211,11 @@ public sealed class ExperimentDataService : IExperimentDataService
|
||||
return BuildIdleSnapshot(snapshot);
|
||||
}
|
||||
|
||||
if (!_isRecordingActive)
|
||||
{
|
||||
return BuildWaitingSnapshot(snapshot);
|
||||
}
|
||||
|
||||
var timedSnapshot = snapshot with
|
||||
{
|
||||
TestSeconds = GetElapsedTestSeconds()
|
||||
@@ -209,17 +242,103 @@ public sealed class ExperimentDataService : IExperimentDataService
|
||||
|
||||
private RealtimeSnapshot BuildIdleSnapshot(RealtimeSnapshot snapshot)
|
||||
{
|
||||
var completedSnapshot = _completedSnapshot;
|
||||
|
||||
return snapshot with
|
||||
{
|
||||
TestSeconds = completedSnapshot?.TestSeconds ?? -1,
|
||||
InitialMass = completedSnapshot?.InitialMass ?? double.NaN,
|
||||
MassLoss = completedSnapshot?.MassLoss ?? double.NaN,
|
||||
MassLossRate = completedSnapshot?.MassLossRate ?? double.NaN,
|
||||
TotalHeatRelease = completedSnapshot?.TotalHeatRelease ?? double.NaN,
|
||||
TotalSmoke = completedSnapshot?.TotalSmoke ?? double.NaN
|
||||
};
|
||||
}
|
||||
|
||||
private RealtimeSnapshot BuildWaitingSnapshot(RealtimeSnapshot snapshot)
|
||||
{
|
||||
var displayInitialMass = double.IsFinite(snapshot.CurrentMass)
|
||||
? snapshot.CurrentMass
|
||||
: double.NaN;
|
||||
|
||||
return snapshot with
|
||||
{
|
||||
TestSeconds = -1,
|
||||
InitialMass = _initialMass ?? double.NaN,
|
||||
MassLoss = double.NaN,
|
||||
InitialMass = displayInitialMass,
|
||||
MassLoss = double.IsFinite(displayInitialMass) ? 0 : double.NaN,
|
||||
MassLossRate = double.NaN,
|
||||
TotalHeatRelease = double.NaN,
|
||||
TotalSmoke = double.NaN
|
||||
TotalHeatRelease = 0,
|
||||
TotalSmoke = 0
|
||||
};
|
||||
}
|
||||
|
||||
private RealtimeSnapshot? BuildCompletedSnapshot(RealtimeSnapshot snapshot)
|
||||
{
|
||||
if (Records.Count == 0)
|
||||
{
|
||||
return snapshot.TestSeconds >= 0 ? snapshot : null;
|
||||
}
|
||||
|
||||
var lastRecord = Records[Records.Count - 1];
|
||||
return snapshot with
|
||||
{
|
||||
TotalHeatRelease = lastRecord.TotalHeatRelease,
|
||||
InitialMass = lastRecord.InitialMass,
|
||||
MassLoss = lastRecord.MassLoss,
|
||||
MassLossRate = lastRecord.MassLossRate,
|
||||
IgnitionSeconds = lastRecord.IgnitionSeconds,
|
||||
TestSeconds = lastRecord.TestSeconds,
|
||||
TotalSmoke = lastRecord.TotalSmoke
|
||||
};
|
||||
}
|
||||
|
||||
private void PrepareDeviceTestTimerGate()
|
||||
{
|
||||
var currentDeviceSeconds = CurrentSnapshot.DeviceTestSeconds;
|
||||
_deviceTestTimerBaselineSeconds = currentDeviceSeconds >= 0 ? currentDeviceSeconds : null;
|
||||
_hasObservedDeviceTestTimerReset = !_deviceTestTimerBaselineSeconds.HasValue
|
||||
|| _deviceTestTimerBaselineSeconds.Value <= 0;
|
||||
}
|
||||
|
||||
private bool ShouldStartTimedRecording(RealtimeSnapshot snapshot)
|
||||
{
|
||||
var deviceSeconds = snapshot.DeviceTestSeconds;
|
||||
if (deviceSeconds < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_deviceTestTimerBaselineSeconds.HasValue || _hasObservedDeviceTestTimerReset)
|
||||
{
|
||||
return deviceSeconds > 0;
|
||||
}
|
||||
|
||||
var baselineSeconds = _deviceTestTimerBaselineSeconds.Value;
|
||||
if (deviceSeconds <= 0)
|
||||
{
|
||||
_hasObservedDeviceTestTimerReset = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return deviceSeconds != baselineSeconds;
|
||||
}
|
||||
|
||||
private void BeginTimedRecording(RealtimeSnapshot snapshot)
|
||||
{
|
||||
_isRecordingActive = true;
|
||||
_testClock.Restart();
|
||||
|
||||
if (double.IsFinite(snapshot.CurrentMass))
|
||||
{
|
||||
_initialMass = snapshot.CurrentMass;
|
||||
}
|
||||
|
||||
Log.Information(
|
||||
"Experiment timed recording started by D1015. DeviceTestSeconds={DeviceTestSeconds}, InitialMass={InitialMass}.",
|
||||
snapshot.DeviceTestSeconds,
|
||||
_initialMass);
|
||||
}
|
||||
|
||||
private int GetElapsedTestSeconds()
|
||||
{
|
||||
return Math.Max(0, (int)Math.Floor(_testClock.Elapsed.TotalSeconds));
|
||||
|
||||
@@ -26,6 +26,7 @@ public sealed class ModbusRealtimeDataService : IRealtimeDataService
|
||||
private const ushort SmokeProductionRegister = 392;
|
||||
private const ushort IrradianceRegister = 410;
|
||||
private const ushort IgnitionSecondsRegister = 1014;
|
||||
private const ushort DeviceTestSecondsRegister = 1015;
|
||||
private const ushort M3FlameMonitorBit = 3;
|
||||
private static readonly ModbusFloatByteOrder[] RealtimeFloatByteOrders =
|
||||
[
|
||||
@@ -84,6 +85,7 @@ public sealed class ModbusRealtimeDataService : IRealtimeDataService
|
||||
MassLoss: double.NaN,
|
||||
MassLossRate: double.NaN,
|
||||
IgnitionSeconds: ReadInt16OrEmpty(IgnitionSecondsRegister),
|
||||
DeviceTestSeconds: ReadInt16OrEmpty(DeviceTestSecondsRegister),
|
||||
TestSeconds: ToElapsedSeconds(elapsed),
|
||||
TotalSmoke: double.NaN);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user