Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23235626ba | ||
|
|
9444959efe | ||
|
|
7a10b9f4b6 | ||
|
|
d440dbcc58 |
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("3.9.3155.0")]
|
||||
[assembly: AssemblyFileVersion("3.9.3155.0")]
|
||||
[assembly: AssemblyVersion("3.9.3159.0")]
|
||||
[assembly: AssemblyFileVersion("3.9.3159.0")]
|
||||
|
||||
@@ -243,6 +243,7 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
public void Stop()
|
||||
{
|
||||
log.Info("Op.Stop()");
|
||||
log.WarnFormat("FLYING_BOARD_OPERATION_STOP: state={0}; invoking StopAll(false), hydraulic flow may stop.", opState);
|
||||
uniCB.SetActivity((opState == OpState.TestCompleted) ? Activity.TestCompleted : Activity.Idle);
|
||||
uniCB.StopAll(false);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace TBF.Rig.Operations
|
||||
|
||||
if (Bridge.Ui2MachineQueue.Contains(UI2BenchCmd.Stop))
|
||||
{
|
||||
log.Warn("CENTRAL_STOP_DEQUEUED: CheckUIOp received Stop.");
|
||||
do {
|
||||
cmd = Bridge.Ui2MachineQueue.Dequeue();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
{
|
||||
/// <summary>Meter-time endpoint policy; wall-clock deadline is owned by the sequence.</summary>
|
||||
internal sealed class AsicFlyingDrain
|
||||
{
|
||||
internal double Target { get; }
|
||||
internal bool Complete { get; private set; }
|
||||
internal int SelectedIndex { get; private set; }
|
||||
private readonly bool allowBeyond;
|
||||
private bool targetReached;
|
||||
private double lastTime;
|
||||
|
||||
internal AsicFlyingDrain(double start, double referenceSeconds, double endAtDiv,
|
||||
int endIndex, int extraSeconds, bool allowBeyond)
|
||||
{
|
||||
if (double.IsNaN(start) || double.IsNaN(endAtDiv) || referenceSeconds <= 0 ||
|
||||
double.IsInfinity(start) || double.IsInfinity(endAtDiv) || double.IsNaN(referenceSeconds) ||
|
||||
double.IsInfinity(referenceSeconds) || start <= 0 || endAtDiv <= start)
|
||||
throw new InvalidOperationException("ASIC drain requires valid initial and final meter timestamps.");
|
||||
if (extraSeconds != 0 && (extraSeconds < 10 || extraSeconds > 50 || extraSeconds % 10 != 0))
|
||||
throw new InvalidOperationException("ASIC drain duration must be Measurement time or 10, 20, 30, 40, 50 seconds.");
|
||||
this.allowBeyond = allowBeyond && extraSeconds != 0;
|
||||
Target = extraSeconds == 0 ? start + referenceSeconds : endAtDiv + extraSeconds;
|
||||
if (!this.allowBeyond) Target = Math.Min(Target, start + referenceSeconds);
|
||||
SelectedIndex = endIndex;
|
||||
lastTime = endAtDiv;
|
||||
Complete = endAtDiv >= Target;
|
||||
}
|
||||
|
||||
internal void Observe(int index, double time)
|
||||
{
|
||||
if (Complete || double.IsNaN(time) || double.IsInfinity(time) || time < lastTime) return;
|
||||
lastTime = time;
|
||||
if (!targetReached)
|
||||
{
|
||||
if (allowBeyond || time <= Target) SelectedIndex = index;
|
||||
if (time >= Target) targetReached = true;
|
||||
}
|
||||
// Existing end volume uses a centred 3-sample average. Keep its right neighbour.
|
||||
if (targetReached && index >= SelectedIndex + TestMethods.iPerlCommunication.iPerlHead.IperlAsicHead.StartEndFilterSamplesCount2)
|
||||
Complete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
private readonly object sync = new object();
|
||||
private StreamWriter writer;
|
||||
private string currentTestContext;
|
||||
private DateTime? receiveTimestamp;
|
||||
private string selectionStatus;
|
||||
|
||||
public void Write(
|
||||
AsicReaderCfg cfg,
|
||||
@@ -35,7 +37,9 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
DiagnosticLedState7Data parsedData,
|
||||
double extendedVolume,
|
||||
double extendedTimestamp,
|
||||
string testContext = null)
|
||||
string testContext = null,
|
||||
DateTime? pcReceived = null,
|
||||
string status = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -51,6 +55,8 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
lock (sync)
|
||||
{
|
||||
currentTestContext = testContext;
|
||||
receiveTimestamp = pcReceived;
|
||||
selectionStatus = status;
|
||||
string extension = IsLogFormat(cfg.OpticalDiagnosticFormat) ? ".log" : ".csv";
|
||||
OpticalLogArchive.Append(diagnosticRoot, batchNr, directory =>
|
||||
{
|
||||
@@ -150,13 +156,13 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
string parsedValues = cfg.IncludeParsedValues && parsedData != null ? parsedData.ToString() : String.Empty;
|
||||
writer.WriteLine(String.Join(";", new[]
|
||||
{
|
||||
Csv(DateTime.Now.ToString("O", CultureInfo.InvariantCulture)),
|
||||
Csv((receiveTimestamp ?? DateTime.Now).ToString("O", CultureInfo.InvariantCulture)),
|
||||
Csv(batchNr.ToString(CultureInfo.InvariantCulture)),
|
||||
Csv(wmPosition.ToString(CultureInfo.InvariantCulture)),
|
||||
Csv(serialNumber),
|
||||
Csv(telegramIndex.ToString(CultureInfo.InvariantCulture)),
|
||||
Csv((isMeasurementData ? "Measurement" : "Flush") + ";" + currentTestContext),
|
||||
Csv(parsedData == null ? "Invalid" : "Parsed"),
|
||||
Csv(selectionStatus ?? (parsedData == null ? "Invalid" : "Parsed")),
|
||||
Csv(rawFormat),
|
||||
Csv(FormatRaw(rawFormat, rawLine, rawBytes)),
|
||||
Csv(cfg.IncludeParsedValues ? Number(extendedTimestamp) : String.Empty),
|
||||
@@ -172,8 +178,8 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
{
|
||||
string rawFormat = NormalizedRawFormat(cfg.RawOpticalDataFormat);
|
||||
writer.WriteLine("{0:O} | batch={1} | wm={2} | serial={3} | telegram={4} | capture={5} | parse={6} | {7}",
|
||||
DateTime.Now, batchNr, wmPosition, serialNumber ?? String.Empty, telegramIndex,
|
||||
isMeasurementData ? "Measurement" : "Flush", parsedData == null ? "Invalid" : "Parsed", currentTestContext);
|
||||
receiveTimestamp ?? DateTime.Now, batchNr, wmPosition, serialNumber ?? String.Empty, telegramIndex,
|
||||
isMeasurementData ? "Measurement" : "Flush", selectionStatus ?? (parsedData == null ? "Invalid" : "Parsed"), currentTestContext);
|
||||
writer.WriteLine("RAW [{0}] {1}", rawFormat, FormatRaw(rawFormat, rawLine, rawBytes));
|
||||
if (cfg.IncludeParsedValues)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using TBF.Rig.Diagnostics;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
|
||||
@@ -34,6 +39,13 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
|
||||
protected override void OpticalTestStarted(string name, int repetition)
|
||||
{
|
||||
if (experimentalWorker != null) FinishExperimentalOpticalInput();
|
||||
if (ExperimentalSelectionEnabled)
|
||||
experimentalWorker = new AsicOpticalBatchWorker(asicCfg.EnableOpticalBatchSelection ? asicCfg.OpticalBatchSize : 1,
|
||||
asicCfg.OnlyNewOpticalMeterSecond, asicCfg.OpticalBatchTimeoutSeconds);
|
||||
clockProbe = System.Diagnostics.Stopwatch.StartNew();
|
||||
clockProbeFirstTicks = null;
|
||||
flyingDrain = null;
|
||||
OpticalTrace.Configure(name, repetition);
|
||||
OpticalTrace.Begin();
|
||||
}
|
||||
@@ -56,6 +68,98 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
}
|
||||
|
||||
private readonly AsicReaderCfg asicCfg;
|
||||
private AsicOpticalBatchWorker experimentalWorker;
|
||||
protected override void CloseOptoSerialPort()
|
||||
{
|
||||
try { FinishExperimentalOpticalInput(); }
|
||||
finally { base.CloseOptoSerialPort(); }
|
||||
}
|
||||
protected override bool ClearOpticalOnOpen => asicCfg == null || !asicCfg.EnableOpticalExperiments || asicCfg.ClearOpticalInputOnOpen;
|
||||
protected override bool ExperimentalSelectionEnabled => asicCfg != null && asicCfg.EnableOpticalExperiments &&
|
||||
(asicCfg.EnableOpticalBatchSelection || asicCfg.OnlyNewOpticalMeterSecond);
|
||||
protected override bool ReadExperimentalOpticalInput()
|
||||
{
|
||||
if (experimentalWorker == null) return false;
|
||||
PublishExperimentalDecisions();
|
||||
// Drain the available COM input, bounded per scheduler turn so Stop remains responsive.
|
||||
for (int i = 0; i < 256 && OpticalBytesAvailable > 0; i++)
|
||||
{
|
||||
byte[] bytes;
|
||||
string line;
|
||||
try { line = ReadOptoLine(out bytes); }
|
||||
catch (TimeoutException) { break; } // Incomplete line is not a fatal reader failure.
|
||||
log4net.LogManager.GetLogger("AnsiIPerlOptical").Debug("EXPERIMENT_RX | COM" + OptoComPortNr + " | " + line.Replace('\t', ' '));
|
||||
experimentalWorker.Enqueue(line, bytes, DateTime.Now, Convert.ToDouble(ProcessData.RefFlow.Val));
|
||||
}
|
||||
PublishExperimentalDecisions();
|
||||
return true;
|
||||
}
|
||||
private void PublishExperimentalDecisions()
|
||||
{
|
||||
AsicOpticalBatchWorker.Frame frame;
|
||||
while (experimentalWorker.TryRead(out frame))
|
||||
{
|
||||
int selectedIndex = -1;
|
||||
if (frame.Keep)
|
||||
{
|
||||
CommitExperimentalOpticalSample(frame.Data, frame.RefFlow, frame.Volume, frame.Time);
|
||||
selectedIndex = TestEndTelegramIx;
|
||||
}
|
||||
opticalDiagnosticWriter.Write(asicCfg, ProcessData.BatchNr, Position, SerialNr, frame.ReceiveIndex,
|
||||
true, frame.Line, frame.Bytes, frame.Data, frame.Volume, frame.Time,
|
||||
OpticalTrace.Context + ";sourceIndex=" + frame.ReceiveIndex + ";selectedIndex=" + selectedIndex,
|
||||
frame.PcTimestamp, frame.Reason);
|
||||
}
|
||||
}
|
||||
protected override void FinishExperimentalOpticalInput()
|
||||
{
|
||||
if (experimentalWorker == null) return;
|
||||
// Worker has no reader lock or COM access, so joining under the reader lock is safe.
|
||||
experimentalWorker.Complete();
|
||||
PublishExperimentalDecisions();
|
||||
experimentalWorker.Dispose();
|
||||
experimentalWorker = null;
|
||||
}
|
||||
private System.Diagnostics.Stopwatch clockProbe;
|
||||
private uint? clockProbeFirstTicks;
|
||||
private double clockProbeFirstHost, clockProbeLastLog;
|
||||
private AsicFlyingDrain flyingDrain;
|
||||
internal bool FlyingDrainEnabled => asicCfg != null && asicCfg.EnableFlyingDrain && !asicCfg.EnableFlyingEarlyCapture;
|
||||
internal bool FlyingEarlyCaptureEnabled => asicCfg != null && asicCfg.EnableFlyingEarlyCapture;
|
||||
internal int FlyingEarlyCaptureSeconds => Math.Max(1, Math.Min(60, asicCfg.FlyingEarlyCaptureSeconds));
|
||||
internal bool FlyingDrainCompleted { get { lock (this) return flyingDrain == null ||
|
||||
(flyingDrain.Complete && HasFlyingEndpointWindow(flyingDrain.SelectedIndex)); } }
|
||||
|
||||
internal void BeginFlyingDrain(double referenceSeconds)
|
||||
{
|
||||
if (!FlyingDrainEnabled) return;
|
||||
lock (this)
|
||||
{
|
||||
HoldFlyingEndpoint();
|
||||
flyingDrain = new AsicFlyingDrain(TimestampSecStart, referenceSeconds, TimestampSecEnd,
|
||||
TestEndTelegramIx, asicCfg.FlyingDrainSeconds, asicCfg.FlyingDrainAllowBeyondTestTime);
|
||||
WriteDrainMarker("DRAIN_BEGIN;target=" + flyingDrain.Target.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
|
||||
ReplayFlyingSamplesAfter(TestEndTelegramIx, flyingDrain.Observe);
|
||||
SelectFlyingEndpoint(flyingDrain.SelectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
internal void FinishFlyingDrain(string reason)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (flyingDrain == null) return;
|
||||
SelectFlyingEndpoint(flyingDrain.SelectedIndex);
|
||||
WriteDrainMarker("DRAIN_END;reason=" + reason + ";selected=" + flyingDrain.SelectedIndex);
|
||||
flyingDrain = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteDrainMarker(string message)
|
||||
{
|
||||
log4net.LogManager.GetLogger("ASIC_OPTO").Info("reader=" + Name + ";WM=" + Position + ";" + message);
|
||||
OpticalTrace.Selected(false, TestEndTelegramIx, VolumeLtrEnd, TimestampSecEnd, message);
|
||||
}
|
||||
private readonly AsicOpticalDiagnosticWriter opticalDiagnosticWriter =
|
||||
new AsicOpticalDiagnosticWriter();
|
||||
|
||||
@@ -113,6 +217,35 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
if (asicCfg == null)
|
||||
return;
|
||||
|
||||
if (isMeasurementData && parsedData != null && clockProbe != null)
|
||||
{
|
||||
double host = clockProbe.Elapsed.TotalSeconds;
|
||||
if (!clockProbeFirstTicks.HasValue)
|
||||
{
|
||||
clockProbeFirstTicks = parsedData.AsicTimestampTicks;
|
||||
clockProbeFirstHost = host;
|
||||
clockProbeLastLog = host - 5;
|
||||
}
|
||||
if (host - clockProbeLastLog >= 5)
|
||||
{
|
||||
uint ticks = unchecked(parsedData.AsicTimestampTicks - clockProbeFirstTicks.Value);
|
||||
double hostDelta = host - clockProbeFirstHost;
|
||||
string probe = string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
"ASIC_CLOCK_PROBE;reader={0};COM={1};sample={2};pcUtc={3:O};hostDeltaS={4:F3};rawTicks={5};deltaTicks={6};ticksPerHostSecond={7:F3};parsedTime={8:R}",
|
||||
Name, OptoComPortNr, telegramIndex, DateTime.UtcNow, hostDelta, parsedData.AsicTimestampTicks,
|
||||
ticks, hostDelta > 0 ? ticks / hostDelta : 0, extendedTimestamp);
|
||||
log4net.LogManager.GetLogger(typeof(AsicReader)).Info(probe);
|
||||
OpticalTrace.Selected(false, telegramIndex, extendedVolume, extendedTimestamp, probe);
|
||||
clockProbeLastLog = host;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMeasurementData && parsedData != null && flyingDrain != null && IsValidFlyingSample(telegramIndex))
|
||||
{
|
||||
flyingDrain.Observe(telegramIndex, extendedTimestamp);
|
||||
SelectFlyingEndpoint(flyingDrain.SelectedIndex);
|
||||
}
|
||||
|
||||
opticalDiagnosticWriter.Write(
|
||||
asicCfg,
|
||||
ProcessData.BatchNr,
|
||||
@@ -128,4 +261,109 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
OpticalTrace.Context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Experimental selection worker. Never owns or closes a COM port.</summary>
|
||||
internal sealed class AsicOpticalBatchWorker : IDisposable
|
||||
{
|
||||
internal sealed class Frame
|
||||
{
|
||||
internal string Line, Reason;
|
||||
internal byte[] Bytes;
|
||||
internal DateTime PcTimestamp;
|
||||
internal int ReceiveIndex;
|
||||
internal double RefFlow, Volume = double.NaN, Time = double.NaN;
|
||||
internal DiagnosticLedState7Data Data;
|
||||
internal bool Keep;
|
||||
}
|
||||
private readonly BlockingCollection<Frame> input = new BlockingCollection<Frame>(8192);
|
||||
private readonly ConcurrentQueue<Frame> output = new ConcurrentQueue<Frame>();
|
||||
private readonly int batchSize, timeoutMs;
|
||||
private readonly bool newSeconds;
|
||||
private readonly Task worker;
|
||||
private int receiveIndex;
|
||||
internal AsicOpticalBatchWorker(int batchSize, bool newSeconds, int timeoutSeconds)
|
||||
{
|
||||
if (batchSize < 1 || batchSize > 1000) throw new ArgumentOutOfRangeException(nameof(batchSize));
|
||||
if (timeoutSeconds < 1 || timeoutSeconds > 60) throw new ArgumentOutOfRangeException(nameof(timeoutSeconds));
|
||||
this.batchSize = batchSize;
|
||||
this.newSeconds = newSeconds;
|
||||
timeoutMs = timeoutSeconds * 1000;
|
||||
worker = Task.Factory.StartNew(Work, System.Threading.CancellationToken.None,
|
||||
TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
}
|
||||
internal void Enqueue(string line, byte[] bytes, DateTime received, double refFlow)
|
||||
{
|
||||
if (!input.TryAdd(new Frame { Line = line, Bytes = bytes, PcTimestamp = received,
|
||||
ReceiveIndex = receiveIndex++, RefFlow = refFlow }))
|
||||
throw new InvalidOperationException("ASIC experimental input queue overflow; no silent data loss permitted.");
|
||||
}
|
||||
internal bool TryRead(out Frame frame)
|
||||
{
|
||||
if (worker.IsFaulted) worker.GetAwaiter().GetResult();
|
||||
return output.TryDequeue(out frame);
|
||||
}
|
||||
internal void Complete()
|
||||
{
|
||||
input.CompleteAdding();
|
||||
if (!worker.Wait(5000)) throw new TimeoutException("ASIC experimental selection worker failed to stop within 5 seconds.");
|
||||
}
|
||||
private void Work()
|
||||
{
|
||||
var parser = new TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.DiagnosticLedParser(
|
||||
TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.DiagnosticLedState.State7);
|
||||
var batch = new List<Frame>();
|
||||
var elapsed = Stopwatch.StartNew();
|
||||
double volume = double.NaN, time = double.NaN, lastSecond = double.NaN;
|
||||
while (!input.IsCompleted)
|
||||
{
|
||||
Frame frame;
|
||||
if (input.TryTake(out frame, 50))
|
||||
{
|
||||
if (batch.Count == 0) elapsed.Restart();
|
||||
try
|
||||
{
|
||||
frame.Data = parser.ParseLine(frame.Line, false) as DiagnosticLedState7Data;
|
||||
if (frame.Data != null)
|
||||
{
|
||||
// Unwrap EVERY valid telegram before selection, including discarded records.
|
||||
var raw = new TBF.Rig.TestMethods.iPerlCommunication.common.OptoTelegramRaw();
|
||||
raw.UpdateFromSmart(frame.Data, frame.ReceiveIndex, (float)frame.RefFlow, ref volume, ref time);
|
||||
frame.Volume = volume;
|
||||
frame.Time = time;
|
||||
double second = Math.Floor(time);
|
||||
if (!newSeconds || double.IsNaN(lastSecond) || second > lastSecond)
|
||||
{
|
||||
frame.Keep = true;
|
||||
frame.Reason = "RETAINED";
|
||||
lastSecond = second;
|
||||
}
|
||||
else frame.Reason = "DISCARDED_SECOND";
|
||||
}
|
||||
else frame.Reason = "INVALID";
|
||||
}
|
||||
catch (Exception ex) { frame.Reason = "INVALID:" + ex.GetType().Name; frame.Data = null; }
|
||||
batch.Add(frame);
|
||||
}
|
||||
if (batch.Count >= batchSize || (batch.Count > 0 && elapsed.ElapsedMilliseconds >= timeoutMs))
|
||||
Publish(batch);
|
||||
}
|
||||
Publish(batch); // Last partial batch is never lost on normal stop.
|
||||
}
|
||||
private void Publish(List<Frame> batch)
|
||||
{
|
||||
var candidates = batch.Where(frame => frame.Keep).ToList();
|
||||
foreach (var frame in candidates)
|
||||
{
|
||||
frame.Keep = ReferenceEquals(frame, candidates[0]) || ReferenceEquals(frame, candidates[candidates.Count - 1]);
|
||||
frame.Reason = frame.Keep ? "RETAINED_BOUNDARY" : "DISCARDED_BATCH";
|
||||
}
|
||||
foreach (var frame in batch)
|
||||
{
|
||||
if (output.Count >= 16384) throw new InvalidOperationException("ASIC experimental result queue overflow.");
|
||||
output.Enqueue(frame);
|
||||
}
|
||||
batch.Clear();
|
||||
}
|
||||
public void Dispose() { input.Dispose(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,17 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
public string OpticalDiagnosticFormat = "Csv";
|
||||
public string RawOpticalDataFormat = "Hex";
|
||||
public bool IncludeParsedValues = true;
|
||||
public bool EnableOpticalExperiments;
|
||||
public bool ClearOpticalInputOnOpen = true;
|
||||
public bool EnableOpticalBatchSelection;
|
||||
public int OpticalBatchSize = 5;
|
||||
public int OpticalBatchTimeoutSeconds = 30;
|
||||
public bool OnlyNewOpticalMeterSecond;
|
||||
public bool EnableFlyingDrain;
|
||||
public bool EnableFlyingEarlyCapture;
|
||||
public int FlyingEarlyCaptureSeconds = 10;
|
||||
public int FlyingDrainSeconds; // 0 = measurement time; otherwise 10..50 seconds.
|
||||
public bool FlyingDrainAllowBeyondTestTime;
|
||||
|
||||
public override IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,14 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
settingsTabPage = new System.Windows.Forms.TabPage();
|
||||
manualTestTabPage = new System.Windows.Forms.TabPage();
|
||||
logsTabPage = new System.Windows.Forms.TabPage();
|
||||
measurementTabPage = new System.Windows.Forms.TabPage();
|
||||
measurementCfgCtrl = new AsicReaderMeasurementCfgCtrl();
|
||||
measurementTabPage.Controls.Add(measurementCfgCtrl);
|
||||
measurementTabPage.Name = "measurementTabPage";
|
||||
measurementTabPage.Text = "Measurement";
|
||||
measurementTabPage.Size = new System.Drawing.Size(603,403);
|
||||
measurementTabPage.UseVisualStyleBackColor = true;
|
||||
measurementCfgCtrl.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
settingsCfgCtrl = new TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlHeadCfgCtrl();
|
||||
manualTestCfgCtrl = new TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlHeadCfgCtrl();
|
||||
logsCfgCtrl = new AsicReaderLogsCfgCtrl();
|
||||
@@ -23,6 +31,7 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
logsCfgCtrl.Location = new System.Drawing.Point(-4,-4); logsCfgCtrl.Name = "logsCfgCtrl"; logsCfgCtrl.Size = new System.Drawing.Size(611,432);
|
||||
AutoScaleDimensions = new System.Drawing.SizeF(8F,16F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; Controls.Add(tabControl); Name = "AsicReaderCfgCtrl"; Size = new System.Drawing.Size(617,438);
|
||||
tabControl.ResumeLayout(false); settingsTabPage.ResumeLayout(false); manualTestTabPage.ResumeLayout(false); logsTabPage.ResumeLayout(false); ResumeLayout(false);
|
||||
tabControl.Controls.Add(measurementTabPage);
|
||||
}
|
||||
private System.Windows.Forms.TabControl tabControl;
|
||||
private System.Windows.Forms.TabPage settingsTabPage;
|
||||
@@ -31,5 +40,7 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
private TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlHeadCfgCtrl settingsCfgCtrl;
|
||||
private TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlHeadCfgCtrl manualTestCfgCtrl;
|
||||
private AsicReaderLogsCfgCtrl logsCfgCtrl;
|
||||
private System.Windows.Forms.TabPage measurementTabPage;
|
||||
private AsicReaderMeasurementCfgCtrl measurementCfgCtrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,12 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
public partial class AsicReaderCfgCtrl : UserControl, IComponentCfgCtrl
|
||||
{
|
||||
private AsicReaderCfg config;
|
||||
public AsicReaderCfgCtrl() { InitializeComponent(); }
|
||||
public AsicReaderCfgCtrl()
|
||||
{
|
||||
InitializeComponent();
|
||||
string culture = System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
|
||||
measurementTabPage.Text = culture == "sk" ? "Meranie" : culture == "cs" ? "Měření" : "Measurement";
|
||||
}
|
||||
public bool ShowMore { get { return false; } }
|
||||
public IComponentCfg Config
|
||||
{
|
||||
@@ -22,20 +27,21 @@ namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
manualTestCfgCtrl.Config = config;
|
||||
manualTestCfgCtrl.ShowOnlyTab(true);
|
||||
logsCfgCtrl.Config = config;
|
||||
measurementCfgCtrl.Config = config;
|
||||
}
|
||||
}
|
||||
public void Unlock() { settingsCfgCtrl.Unlock(); logsCfgCtrl.Unlock(); }
|
||||
public void Unlock() { settingsCfgCtrl.Unlock(); logsCfgCtrl.Unlock(); measurementCfgCtrl.Unlock(); }
|
||||
public void Closing() { settingsCfgCtrl.Closing(); manualTestCfgCtrl.Closing(); }
|
||||
public CfgUpdateFlags VerifyCfg(ref string message)
|
||||
{
|
||||
CfgUpdateFlags flags = settingsCfgCtrl.VerifyCfg(ref message);
|
||||
return flags == CfgUpdateFlags.None ? logsCfgCtrl.VerifyCfg(ref message) : flags;
|
||||
return flags | logsCfgCtrl.VerifyCfg(ref message) | measurementCfgCtrl.VerifyCfg(ref message);
|
||||
}
|
||||
public CfgUpdateFlags UpdateCfg()
|
||||
{
|
||||
CfgUpdateFlags flags = settingsCfgCtrl.UpdateCfg();
|
||||
if ((flags & CfgUpdateFlags.Error) != 0) return flags;
|
||||
return flags | logsCfgCtrl.UpdateCfg();
|
||||
return flags | logsCfgCtrl.UpdateCfg() | measurementCfgCtrl.UpdateCfg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
{
|
||||
partial class AsicReaderMeasurementCfgCtrl
|
||||
{
|
||||
private System.ComponentModel.IContainer components;
|
||||
private System.Windows.Forms.Label infoLabel;
|
||||
private System.Windows.Forms.CheckBox enableEarlyCaptureCheckBox;
|
||||
private System.Windows.Forms.NumericUpDown earlyCaptureSeconds;
|
||||
private System.Windows.Forms.Label earlyCaptureLabel;
|
||||
private System.Windows.Forms.CheckBox experimentsCheckBox;
|
||||
private System.Windows.Forms.CheckBox clearInputCheckBox;
|
||||
private System.Windows.Forms.CheckBox batchCheckBox;
|
||||
private System.Windows.Forms.NumericUpDown batchSize;
|
||||
private System.Windows.Forms.Label batchSizeLabel;
|
||||
private System.Windows.Forms.CheckBox newSecondCheckBox;
|
||||
private System.Windows.Forms.NumericUpDown batchTimeout;
|
||||
private System.Windows.Forms.Label batchTimeoutLabel;
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && components != null) components.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.infoLabel = new System.Windows.Forms.Label();
|
||||
this.enableEarlyCaptureCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.earlyCaptureSeconds = new System.Windows.Forms.NumericUpDown();
|
||||
this.earlyCaptureLabel = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
this.experimentsCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.clearInputCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.batchCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.batchSize = new System.Windows.Forms.NumericUpDown();
|
||||
this.batchSizeLabel = new System.Windows.Forms.Label();
|
||||
this.newSecondCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.batchTimeout = new System.Windows.Forms.NumericUpDown();
|
||||
this.batchTimeoutLabel = new System.Windows.Forms.Label();
|
||||
this.experimentsCheckBox.Location = new System.Drawing.Point(20, 135);
|
||||
this.experimentsCheckBox.Size = new System.Drawing.Size(560, 25);
|
||||
this.experimentsCheckBox.Name = "experimentsCheckBox";
|
||||
this.clearInputCheckBox.Location = new System.Drawing.Point(40, 165);
|
||||
this.clearInputCheckBox.Size = new System.Drawing.Size(540, 25);
|
||||
this.clearInputCheckBox.Name = "clearInputCheckBox";
|
||||
this.batchCheckBox.Location = new System.Drawing.Point(40, 195);
|
||||
this.batchCheckBox.Size = new System.Drawing.Size(540, 25);
|
||||
this.batchCheckBox.Name = "batchCheckBox";
|
||||
this.batchSizeLabel.Location = new System.Drawing.Point(60, 228);
|
||||
this.batchSizeLabel.Size = new System.Drawing.Size(280, 25);
|
||||
this.batchSize.Location = new System.Drawing.Point(350, 225);
|
||||
this.batchSize.Name = "batchSize";
|
||||
this.batchSize.Minimum = 2;
|
||||
this.batchSize.Maximum = 1000;
|
||||
this.batchSize.Value = 5;
|
||||
this.newSecondCheckBox.Location = new System.Drawing.Point(40, 260);
|
||||
this.newSecondCheckBox.Size = new System.Drawing.Size(540, 25);
|
||||
this.newSecondCheckBox.Name = "newSecondCheckBox";
|
||||
this.batchTimeoutLabel.Location = new System.Drawing.Point(60, 296);
|
||||
this.batchTimeoutLabel.Size = new System.Drawing.Size(280, 25);
|
||||
this.batchTimeout.Location = new System.Drawing.Point(350, 293);
|
||||
this.batchTimeout.Name = "batchTimeout";
|
||||
this.batchTimeout.Minimum = 1;
|
||||
this.batchTimeout.Maximum = 60;
|
||||
this.batchTimeout.Value = 30;
|
||||
this.Controls.Add(this.experimentsCheckBox);
|
||||
this.Controls.Add(this.clearInputCheckBox);
|
||||
this.Controls.Add(this.batchCheckBox);
|
||||
this.Controls.Add(this.batchSizeLabel);
|
||||
this.Controls.Add(this.batchSize);
|
||||
this.Controls.Add(this.newSecondCheckBox);
|
||||
this.Controls.Add(this.batchTimeoutLabel);
|
||||
this.Controls.Add(this.batchTimeout);
|
||||
this.infoLabel.Location = new System.Drawing.Point(20, 335);
|
||||
this.infoLabel.Name = "infoLabel";
|
||||
this.infoLabel.Size = new System.Drawing.Size(560, 60);
|
||||
this.infoLabel.Text = "Duration is based on meter telegram timestamps.";
|
||||
this.Controls.Add(this.infoLabel);
|
||||
this.enableEarlyCaptureCheckBox.Location = new System.Drawing.Point(20, 25);
|
||||
this.enableEarlyCaptureCheckBox.Name = "enableEarlyCaptureCheckBox";
|
||||
this.enableEarlyCaptureCheckBox.Size = new System.Drawing.Size(560, 32);
|
||||
this.enableEarlyCaptureCheckBox.Text = "Enable forward optical measurement (Flying)";
|
||||
this.enableEarlyCaptureCheckBox.Enabled = false;
|
||||
this.earlyCaptureLabel.Location = new System.Drawing.Point(20, 85);
|
||||
this.earlyCaptureLabel.Size = new System.Drawing.Size(280, 25);
|
||||
this.earlyCaptureLabel.Text = "Minimum lead time [s]:";
|
||||
this.earlyCaptureSeconds.Location = new System.Drawing.Point(320, 83);
|
||||
this.earlyCaptureSeconds.Name = "earlyCaptureSeconds";
|
||||
this.earlyCaptureSeconds.Minimum = 1;
|
||||
this.earlyCaptureSeconds.Maximum = 60;
|
||||
this.earlyCaptureSeconds.Value = 10;
|
||||
this.earlyCaptureSeconds.Enabled = false;
|
||||
this.Controls.Add(this.enableEarlyCaptureCheckBox);
|
||||
this.Controls.Add(this.earlyCaptureLabel);
|
||||
this.Controls.Add(this.earlyCaptureSeconds);
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Name = "AsicReaderMeasurementCfgCtrl";
|
||||
this.Size = new System.Drawing.Size(603, 403);
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Forms;
|
||||
using Common;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.AsicReader
|
||||
{
|
||||
public partial class AsicReaderMeasurementCfgCtrl : UserControl
|
||||
{
|
||||
private AsicReaderCfg config;
|
||||
private bool unlocked;
|
||||
public AsicReaderMeasurementCfgCtrl()
|
||||
{
|
||||
InitializeComponent();
|
||||
string culture = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
|
||||
bool sk = culture == "sk", cs = culture == "cs";
|
||||
experimentsCheckBox.Text = sk ? "Experimentálne spracovanie optických dát" : cs ? "Experimentální zpracování optických dat" : "Experimental optical processing";
|
||||
clearInputCheckBox.Text = sk ? "Vyčistiť vstupný COM buffer pri otvorení" : cs ? "Vyčistit vstupní COM buffer při otevření" : "Clear COM input buffer on open";
|
||||
batchCheckBox.Text = sk ? "Z dávky ponechať prvý a posledný validný telegram" : cs ? "Z dávky ponechat první a poslední validní telegram" : "Keep first and last valid telegram of each batch";
|
||||
batchSizeLabel.Text = sk ? "Počet prijatých telegramov:" : cs ? "Počet přijatých telegramů:" : "Received telegrams per batch:";
|
||||
newSecondCheckBox.Text = sk ? "Iba prvý validný telegram novej sekundy meradla" : cs ? "Jen první validní telegram nové sekundy měřidla" : "Only first valid telegram of a new meter second";
|
||||
batchTimeoutLabel.Text = sk ? "Limit neúplnej dávky [s]:" : cs ? "Limit neúplné dávky [s]:" : "Partial batch timeout [s]:";
|
||||
experimentsCheckBox.CheckedChanged += (s, e) => UpdateEnabledState();
|
||||
batchCheckBox.CheckedChanged += (s, e) => UpdateEnabledState();
|
||||
enableEarlyCaptureCheckBox.Text = sk ? "Použiť dopredné optické meranie (Flying)" : cs ? "Použít dopředné optické měření (Flying)" : "Enable forward optical measurement (Flying)";
|
||||
earlyCaptureLabel.Text = sk ? "Minimálny predstih [s]:" : cs ? "Minimální předstih [s]:" : "Minimum lead time [s]:";
|
||||
enableEarlyCaptureCheckBox.CheckedChanged += (s, e) => UpdateEnabledState();
|
||||
infoLabel.Text = sk ? "Dopredné meranie pri stabilnom prietoku. Predstih zahŕňa čas AJ objem.\\r\\nReferenčný objem sa koriguje pomerom času meradla a stanice.\\r\\nMeranie končí so skúškou, bez dočítania po skúške.\\r\\nPríprava používa čas PC a reaguje na Stop. Zmena vyžaduje reštart."
|
||||
: cs ? "Dopředné měření při stabilním průtoku. Předstih zahrnuje čas I objem.\\r\\nReferenční objem se koriguje poměrem času měřidla a stanice.\\r\\nMěření končí se zkouškou, bez dočtení po zkoušce.\\r\\nPříprava používá čas PC a reaguje na Stop. Změna vyžaduje restart."
|
||||
: "Forward measurement at stable flow. Lead includes BOTH time and volume.\\r\\nReference volume is corrected by the meter/reference time ratio.\\r\\nMeasurement ends with the test; no post-test wait.\\r\\nPreparation uses PC time and responds to Stop. Changes require restart.";
|
||||
infoLabel.Text = sk ? "Experiment: bez priemerovania END z 3 vzoriek. RAW zachovať v Logs.\r\nVypnuté experimenty zachovajú pôvodné spracovanie. Vyžaduje reštart." : cs ? "Experiment: bez průměrování END ze 3 vzorků. RAW zachovat v Logs.\r\nVypnuté experimenty zachovají původní zpracování. Vyžaduje restart." : "Experiment: no 3-sample END averaging. Preserve RAW in Logs.\r\nExperiments OFF retain legacy processing. Restart required.";
|
||||
UpdateEnabledState();
|
||||
}
|
||||
public AsicReaderCfg Config
|
||||
{
|
||||
get { return config; }
|
||||
set
|
||||
{
|
||||
config = value;
|
||||
experimentsCheckBox.Checked = config != null && config.EnableOpticalExperiments;
|
||||
clearInputCheckBox.Checked = config == null || config.ClearOpticalInputOnOpen;
|
||||
batchCheckBox.Checked = config != null && config.EnableOpticalBatchSelection;
|
||||
batchSize.Value = config == null ? 5 : Math.Max(2, Math.Min(1000, config.OpticalBatchSize));
|
||||
batchTimeout.Value = config == null ? 30 : Math.Max(1, Math.Min(60, config.OpticalBatchTimeoutSeconds));
|
||||
newSecondCheckBox.Checked = config != null && config.OnlyNewOpticalMeterSecond;
|
||||
enableEarlyCaptureCheckBox.Checked = config != null && config.EnableFlyingEarlyCapture;
|
||||
earlyCaptureSeconds.Value = config == null ? 10 : Math.Max(1, Math.Min(60, config.FlyingEarlyCaptureSeconds));
|
||||
UpdateEnabledState();
|
||||
}
|
||||
}
|
||||
public void Unlock() { unlocked = true; UpdateEnabledState(); }
|
||||
private void UpdateEnabledState()
|
||||
{
|
||||
enableEarlyCaptureCheckBox.Enabled = unlocked;
|
||||
earlyCaptureSeconds.Enabled = unlocked && enableEarlyCaptureCheckBox.Checked;
|
||||
experimentsCheckBox.Enabled = unlocked;
|
||||
clearInputCheckBox.Enabled = batchCheckBox.Enabled = newSecondCheckBox.Enabled = unlocked && experimentsCheckBox.Checked;
|
||||
batchSize.Enabled = batchTimeout.Enabled = unlocked && experimentsCheckBox.Checked && batchCheckBox.Checked;
|
||||
}
|
||||
public CfgUpdateFlags VerifyCfg(ref string message)
|
||||
{
|
||||
if (experimentsCheckBox.Checked && config != null &&
|
||||
(!config.EnableDetailedLogPerWM || string.Equals(config.RawOpticalDataFormat, "Disabled", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
message += Environment.NewLine + "Optical experiments require per-WM logging and RAW enabled in Logs.";
|
||||
return CfgUpdateFlags.Error;
|
||||
}
|
||||
if (config == null)
|
||||
{
|
||||
message += Environment.NewLine + "Missing ASIC measurement configuration.";
|
||||
return CfgUpdateFlags.Error;
|
||||
}
|
||||
return CfgUpdateFlags.None;
|
||||
}
|
||||
public CfgUpdateFlags UpdateCfg()
|
||||
{
|
||||
if (config == null) return CfgUpdateFlags.Error;
|
||||
bool earlyChanged = config.EnableFlyingEarlyCapture != enableEarlyCaptureCheckBox.Checked || config.FlyingEarlyCaptureSeconds != (int)earlyCaptureSeconds.Value;
|
||||
config.EnableFlyingEarlyCapture = enableEarlyCaptureCheckBox.Checked;
|
||||
config.FlyingEarlyCaptureSeconds = (int)earlyCaptureSeconds.Value;
|
||||
bool changed = config.EnableFlyingDrain;
|
||||
changed |= config.EnableOpticalExperiments != experimentsCheckBox.Checked ||
|
||||
config.ClearOpticalInputOnOpen != clearInputCheckBox.Checked || config.EnableOpticalBatchSelection != batchCheckBox.Checked ||
|
||||
config.OpticalBatchSize != (int)batchSize.Value || config.OpticalBatchTimeoutSeconds != (int)batchTimeout.Value ||
|
||||
config.OnlyNewOpticalMeterSecond != newSecondCheckBox.Checked;
|
||||
config.EnableOpticalExperiments = experimentsCheckBox.Checked;
|
||||
config.ClearOpticalInputOnOpen = clearInputCheckBox.Checked;
|
||||
config.EnableOpticalBatchSelection = batchCheckBox.Checked;
|
||||
config.OpticalBatchSize = (int)batchSize.Value;
|
||||
config.OpticalBatchTimeoutSeconds = (int)batchTimeout.Value;
|
||||
config.OnlyNewOpticalMeterSecond = newSecondCheckBox.Checked;
|
||||
config.EnableFlyingDrain = false; // Retire post-test waiting when saving the Measurement tab.
|
||||
return changed || earlyChanged ? CfgUpdateFlags.RestartRqrd : CfgUpdateFlags.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<resheader name="resmimetype"><value>text/microsoft-resx</value></resheader>
|
||||
<resheader name="version"><value>2.0</value></resheader>
|
||||
<resheader name="reader"><value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader>
|
||||
<resheader name="writer"><value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader>
|
||||
</root>
|
||||
@@ -7,6 +7,7 @@ using log4net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml.Serialization;
|
||||
@@ -552,6 +553,33 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
||||
Bridge.OnActivity(this, Strings.Measuring_the_weight);
|
||||
//----------------------------------------------------
|
||||
|
||||
// Forward measurement: preserve BOTH the early volume and timestamp endpoints.
|
||||
var earlyReaders = readDatastreamOps.OfType<TBF.Rig.RegisterReaders.AsicReader.AsicReader>()
|
||||
.Where(reader => reader.FlyingEarlyCaptureEnabled).ToList();
|
||||
IList<IOperation> earlyCaptureOps = earlyReaders.Cast<IOperation>().ToList();
|
||||
if (earlyReaders.Count > 0)
|
||||
{
|
||||
foreach (var reader in earlyReaders) reader.PrepareFlyingEarlyCapture();
|
||||
int leadSeconds = earlyReaders.Max(reader => reader.FlyingEarlyCaptureSeconds);
|
||||
log.InfoFormat("ASIC_EARLY_CAPTURE_BEGIN: readers={0}; leadSeconds={1}; PC deadline; flow unchanged", earlyReaders.Count, leadSeconds);
|
||||
Bridge.OnActivity(this, "Preparing optical measurement");
|
||||
State.Create(string.Format("{0}({1}) : Early optical capture", test.Method, test.Name))
|
||||
.AddOperation(checkUiOp)
|
||||
.AddOperations(readTempPressOps)
|
||||
.AddOperations(earlyCaptureOps)
|
||||
.EnterState();
|
||||
var earlyClock = System.Diagnostics.Stopwatch.StartNew();
|
||||
do
|
||||
{
|
||||
e = StateMachine.WaitRunDevsRunOps();
|
||||
if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; }
|
||||
if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; }
|
||||
}
|
||||
while (earlyClock.Elapsed.TotalSeconds < leadSeconds);
|
||||
log.InfoFormat("ASIC_EARLY_CAPTURE_READY: elapsedPC={0:F3}; no meter-time or volume scaling changed", earlyClock.Elapsed.TotalSeconds);
|
||||
Bridge.OnActivity(this, Strings.Measuring_the_weight);
|
||||
}
|
||||
|
||||
LogProcessDataTestInfo(processDataLogger, test.Procedure.Name, test.Name);
|
||||
|
||||
if (heatMetersPath == null) LogProcessDataHeader(processDataLogger, "Start mass");
|
||||
@@ -559,6 +587,7 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
||||
|
||||
State.Create(string.Format("{0}({1}) : Measuring the start mass", test.Method, test.Name))
|
||||
.AddOperation(checkUiOp)
|
||||
.AddOperations(earlyCaptureOps)
|
||||
.AddOperations(readTempPressOps)
|
||||
.AddOperations(cameraMeasurementOps)
|
||||
.AddOperation(scale.ReadStableMassOp(ref StartMass, test.TimeFlow2Mass, test.MassMethod, test.MassRepeats, test.MassSpread))
|
||||
@@ -600,6 +629,7 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
||||
int finalPulsesCount = 0;
|
||||
|
||||
/// Measurement loop
|
||||
foreach (var reader in earlyReaders) reader.BeginFlyingReferenceWindow();
|
||||
State.Create(string.Format("{0}({1}) : FlyingStartStopTestWithDivOp is running", test.Method, test.Name))
|
||||
.AddOperation(checkUiOp)
|
||||
.AddOperations(readTempPressOps)
|
||||
@@ -654,6 +684,63 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
||||
}
|
||||
while (!e.Contains(Event.TestCompleted) && !e.Contains(Event.Next)); /// 'Next' button is enabled in Debug version only
|
||||
|
||||
// Retain the same operations: the state transition must not Stop/Start (clear) readers.
|
||||
var drainReaders = readDatastreamOps.OfType<TBF.Rig.RegisterReaders.AsicReader.AsicReader>()
|
||||
.Where(reader => reader.FlyingDrainEnabled).ToList();
|
||||
if (drainReaders.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var reader in readDatastreamOps.OfType<TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlAsicHead>())
|
||||
reader.HoldFlyingEndpoint();
|
||||
foreach (var reader in drainReaders) reader.BeginFlyingDrain(cBrd.TestTime);
|
||||
StopRecordingStatistics();
|
||||
State.Create("FlyingStartMassCollection : Processing measurement")
|
||||
.AddOperation(checkUiOp).AddOperations(readDatastreamOps
|
||||
.Where(operation => operation is TBF.Rig.RegisterReaders.AsicReader.AsicReader).ToList())
|
||||
.AddOperations(readTempPressOps).EnterState();
|
||||
var deadline = System.Diagnostics.Stopwatch.StartNew();
|
||||
double nextDrainProgressLog = 0;
|
||||
while (drainReaders.Any(reader => !reader.FlyingDrainCompleted))
|
||||
{
|
||||
if (deadline.Elapsed.TotalSeconds >= nextDrainProgressLog)
|
||||
{
|
||||
log.WarnFormat("ASIC_DRAIN_PROGRESS: elapsedHostS={0:F3};limitHostS=60;pendingReaders={1};referenceTestTime={2};UI polling next",
|
||||
deadline.Elapsed.TotalSeconds, drainReaders.Count(reader => !reader.FlyingDrainCompleted), cBrd.TestTime);
|
||||
nextDrainProgressLog = deadline.Elapsed.TotalSeconds + 5;
|
||||
}
|
||||
string culture = System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
|
||||
Bridge.OnActivity(this, culture == "sk" ? "Prebieha spracovanie merania." :
|
||||
culture == "cs" ? "Probíhá zpracování měření." : "Processing measurement.");
|
||||
e = StateMachine.WaitRunDevsRunOps();
|
||||
if (e.Contains(Event.UiCmdStop) || e.Contains(Event.UiCmdShutdown))
|
||||
log.Warn("ASIC_DRAIN_STOP_EVENT: central UI stop/shutdown received.");
|
||||
if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; }
|
||||
if (e.Contains(Event.Error) || e.Contains(Event.OpArgumentError)) { retVal = Event.Error; goto stopTest; }
|
||||
if (deadline.Elapsed.TotalSeconds >= 60 && drainReaders.Any(reader => !reader.FlyingDrainCompleted))
|
||||
{
|
||||
log.Error("ASIC_DRAIN_TIMEOUT: meter-time target not reached within 60 seconds.");
|
||||
retVal = Event.Error;
|
||||
goto stopTest;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
log.Error("ASIC_DRAIN_FAILED", error);
|
||||
retVal = Event.Error;
|
||||
goto stopTest;
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var reader in drainReaders)
|
||||
reader.FinishFlyingDrain(reader.FlyingDrainCompleted ? "completed" : "cancelled-or-timeout");
|
||||
if (retVal != Event.Done)
|
||||
foreach (var reader in readDatastreamOps.OfType<TBF.Rig.RegisterReaders.AsicReader.AsicReader>())
|
||||
reader.StopDataStreamProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------
|
||||
|
||||
@@ -454,6 +454,30 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
|
||||
int currentTelegramIx;
|
||||
bool startSampleAcquired;
|
||||
private bool holdFlyingEndpoint;
|
||||
internal void HoldFlyingEndpoint() { lock (this) holdFlyingEndpoint = true; }
|
||||
internal void SelectFlyingEndpoint(int index) { lock (this) TestEndTelegramIx = index; }
|
||||
internal bool IsValidFlyingSample(int index)
|
||||
{
|
||||
lock (this)
|
||||
return index >= 0 && optoData[BufferIdx(index)].Flags == OptoTelegramFlags.OK;
|
||||
}
|
||||
internal bool HasFlyingEndpointWindow(int index)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (index - StartEndFilterSamplesCount2 < 0 || index + StartEndFilterSamplesCount2 >= optoDataCount) return false;
|
||||
for (int i = index - StartEndFilterSamplesCount2; i <= index + StartEndFilterSamplesCount2; i++)
|
||||
if (!IsValidFlyingSample(i)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
internal void ReplayFlyingSamplesAfter(int index, Action<int, double> observe)
|
||||
{
|
||||
lock (this)
|
||||
for (int i = Math.Max(index + 1, optoDataCount - EndOptoDataCount); i < optoDataCount; i++)
|
||||
if (IsValidFlyingSample(i)) observe(i, optoData[BufferIdx(i)].TimestampExt);
|
||||
}
|
||||
|
||||
///
|
||||
/// Timestamp from the opto telegram
|
||||
@@ -468,13 +492,13 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
public double TimestampSecStart
|
||||
{
|
||||
//4 * pokus (hodnoty su 4 krat mensie)
|
||||
get { return MultiplyFactor * TimeFromSamples(optoData, optoDataCount, TestStartTelegramIx, StartEndFilterSamplesCount2); }
|
||||
get { return MultiplyFactor * TimeFromSamples(optoData, optoDataCount, TestStartTelegramIx, EndpointFilterRadius); }
|
||||
}
|
||||
/// Test end time for metrology in seconds
|
||||
public double TimestampSecEnd
|
||||
{
|
||||
//4 * pokus (hodnoty su 4 krat mensie)
|
||||
get { return MultiplyFactor * TimeFromSamples(optoData, optoDataCount, TestEndTelegramIx, StartEndFilterSamplesCount2); }
|
||||
get { return MultiplyFactor * TimeFromSamples(optoData, optoDataCount, TestEndTelegramIx, EndpointFilterRadius); }
|
||||
}
|
||||
///
|
||||
public bool NoSamples
|
||||
@@ -499,7 +523,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
public double VolumeLtrEnd
|
||||
{
|
||||
//4 * pokus (hodnoty su 4 krat mensie)
|
||||
get { return NoSamples ? 0 : MultiplyFactor * VolumeFromSamples(optoData, optoDataCount, TestEndTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2); }
|
||||
get { return NoSamples ? 0 : MultiplyFactor * VolumeFromSamples(optoData, optoDataCount, TestEndTelegramIx, ScalingFactor(), EndpointFilterRadius); }
|
||||
}
|
||||
|
||||
|
||||
@@ -782,6 +806,19 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
}
|
||||
|
||||
|
||||
private bool flyingCaptureOnly;
|
||||
internal void PrepareFlyingEarlyCapture()
|
||||
{
|
||||
lock (this) flyingCaptureOnly = true;
|
||||
}
|
||||
internal void BeginFlyingReferenceWindow()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
flyingCaptureOnly = false;
|
||||
log.InfoFormat("ASIC_EARLY_CAPTURE_REFERENCE_BEGIN: reader={0}; buffered={1}; forward start preserved; startIndex={2}; acquired={3}", Name, optoDataCount, TestStartTelegramIx, startSampleAcquired);
|
||||
}
|
||||
}
|
||||
int timeFromStart; /// [s] Time from test start to determine when the test start sample should be taken
|
||||
|
||||
/// <summary>
|
||||
@@ -791,7 +828,9 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
FinishExperimentalOpticalInput();
|
||||
Clear();
|
||||
holdFlyingEndpoint = false;
|
||||
ReadPulses();
|
||||
StartDataStreamProcessing();
|
||||
NotifyOpticalTestStarted();
|
||||
@@ -809,10 +848,16 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
timeFromStart += StateMachine.Period;
|
||||
ReadPulses();
|
||||
|
||||
if (!startSampleAcquired && (timeFromStart >= StartSampleDelaySec) && (currentTelegramIx >= 0))
|
||||
if (ExperimentalSelectionEnabled) return Event.ReadRegisterDone;
|
||||
if (holdFlyingEndpoint) return Event.ReadRegisterDone;
|
||||
if (!startSampleAcquired && (flyingCaptureOnly || timeFromStart >= StartSampleDelaySec) &&
|
||||
(currentTelegramIx >= (flyingCaptureOnly ? 1 : 0)))
|
||||
{
|
||||
startSampleAcquired = true;
|
||||
TestStartTelegramIx = currentTelegramIx;
|
||||
if (flyingCaptureOnly)
|
||||
log.InfoFormat("ASIC_FORWARD_START_SELECTED: reader={0}; index={1}; time={2:R}; final paired volume recorded at operation stop",
|
||||
Name, TestStartTelegramIx, TimestampSecStart);
|
||||
}
|
||||
else if (startSampleAcquired)
|
||||
{
|
||||
@@ -835,6 +880,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
lock (this) flyingCaptureOnly = false;
|
||||
ReleaseCommandPort();
|
||||
log.DebugFormat("Flow filtering end, feature vector calculation start: {0:HH:mm:ss.fff}", DateTime.Now);
|
||||
|
||||
@@ -910,7 +956,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
if (!OpticalTestDiagnosticsEnabled) return;
|
||||
try
|
||||
{
|
||||
int radius = StartEndFilterSamplesCount2;
|
||||
int radius = EndpointFilterRadius;
|
||||
string selection = String.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
"indexBase=0;startVolumeIndices={0};endVolumeIndices={1}..{2};startTimeIndex={0};endTimeIndex={3};noSamples={4};startAcquired={5};telegramCount={6};multiplyFactor={7}",
|
||||
TestStartTelegramIx, TestEndTelegramIx - radius, TestEndTelegramIx + radius,
|
||||
@@ -1162,6 +1208,32 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
}
|
||||
|
||||
protected virtual bool IsOptoPortOpen => optoSerialPort != null && optoSerialPort.IsOpen;
|
||||
protected int OpticalBytesAvailable => IsOptoPortOpen ? optoSerialPort.BytesToRead : 0;
|
||||
protected virtual bool ClearOpticalOnOpen => true;
|
||||
protected virtual bool ExperimentalSelectionEnabled => false;
|
||||
protected virtual bool ReadExperimentalOpticalInput() { return false; }
|
||||
protected virtual void FinishExperimentalOpticalInput() { }
|
||||
private int EndpointFilterRadius => ExperimentalSelectionEnabled ? 0 : StartEndFilterSamplesCount2;
|
||||
|
||||
protected void CommitExperimentalOpticalSample(DiagnosticLedState7Data data, double refFlow, double extendedVolume, double extendedTime)
|
||||
{
|
||||
// Called only while holding the reader lock. The worker never touches reader state.
|
||||
int index = optoDataCount;
|
||||
int bufferIx = BufferIdx(index);
|
||||
optoData[bufferIx].UpdateFromSmart(data, index, (float)refFlow,
|
||||
ref volumeRawExtLast, ref timestampExtLast);
|
||||
optoData[bufferIx].VolumeRawExt = volumeRawExtLast = extendedVolume;
|
||||
optoData[bufferIx].TimestampExt = timestampExtLast = extendedTime;
|
||||
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
||||
OptoTelegramReceived(index, true, volumeRawExtLast, timestampExtLast);
|
||||
optoDataCount++;
|
||||
if (!startSampleAcquired && (flyingCaptureOnly || timeFromStart >= StartSampleDelaySec))
|
||||
{
|
||||
startSampleAcquired = true;
|
||||
TestStartTelegramIx = index;
|
||||
}
|
||||
TestEndTelegramIx = index;
|
||||
}
|
||||
|
||||
protected virtual void DiscardOptoInputBuffer()
|
||||
{
|
||||
@@ -1206,7 +1278,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
volumeRawExtLast = double.NaN;
|
||||
timestampExtLast = double.NaN;
|
||||
|
||||
DiscardOptoInputBuffer();
|
||||
if (ClearOpticalOnOpen) DiscardOptoInputBuffer();
|
||||
|
||||
if (flowDirectionDetection != null) flowDirectionDetection.ClearFifo(); /// Clear FIFO for flow direction detection
|
||||
|
||||
@@ -1281,8 +1353,15 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
/// </summary>
|
||||
public void StopDataStreamProcessing()
|
||||
{
|
||||
dataStreamState = DataStreamState.Flush;
|
||||
CloseOptoSerialPort();
|
||||
lock (this)
|
||||
{
|
||||
try { FinishExperimentalOpticalInput(); }
|
||||
finally
|
||||
{
|
||||
dataStreamState = DataStreamState.Flush;
|
||||
CloseOptoSerialPort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@@ -1311,6 +1390,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (optoState == DataStreamState.ProcessAndSave && ReadExperimentalOpticalInput()) return;
|
||||
try
|
||||
{
|
||||
int nrBytes = optoSerialPort.BytesToRead;
|
||||
|
||||
@@ -1715,6 +1715,13 @@
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\AsicReaderLogsCfgCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\AsicFlyingDrain.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\AsicReaderMeasurementCfgCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\AsicReaderMeasurementCfgCtrl.Designer.cs">
|
||||
<DependentUpon>AsicReaderMeasurementCfgCtrl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\AsicReaderLogsCfgCtrl.Designer.cs">
|
||||
<DependentUpon>AsicReaderLogsCfgCtrl.cs</DependentUpon>
|
||||
</Compile>
|
||||
@@ -3992,6 +3999,9 @@
|
||||
<EmbeddedResource Include="Rig\RegisterReaders\AsicReader\AsicReaderLogsCfgCtrl.resx">
|
||||
<DependentUpon>AsicReaderLogsCfgCtrl.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Rig\RegisterReaders\AsicReader\AsicReaderMeasurementCfgCtrl.resx">
|
||||
<DependentUpon>AsicReaderMeasurementCfgCtrl.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Rig\RegisterReaders\iPerlReaderUNI\IPerlUniCfgCtrl.resx">
|
||||
<DependentUpon>IPerlUniCfgCtrl.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.RegisterReaders.AsicReader;
|
||||
|
||||
namespace TBFTests.Rig.RegisterReaders.AsicReader
|
||||
{
|
||||
[TestClass]
|
||||
[DoNotParallelize]
|
||||
public class AsicFlyingDrainTests
|
||||
{
|
||||
private static string ExperimentalLine(uint ticks, uint volume)
|
||||
{
|
||||
string body = "FFCC09\t2020\tFFFA\t" + volume.ToString("X6") +
|
||||
"\t0054\t0B01\t" + ticks.ToString("X8") + "\tA7\t00001234\t00F0\t00F1\t0100\t0200\t03\tFFEC\t0010\t" +
|
||||
"02\t02\t00000099\t01\tFFAA10\t000123\tFFEE\t0011\t03\t";
|
||||
byte checksum = 0;
|
||||
foreach (char c in body) checksum = unchecked((byte)(checksum + (byte)c));
|
||||
return body + checksum.ToString("X2");
|
||||
}
|
||||
[TestMethod]
|
||||
public void ExperimentalPartialBatchDeadlineUsesHostClockWithoutFurtherInput()
|
||||
{
|
||||
using (var worker = new AsicOpticalBatchWorker(20, false, 1))
|
||||
{
|
||||
worker.Enqueue(ExperimentalLine(819200, 1000), new byte[0], DateTime.Now, 1);
|
||||
AsicOpticalBatchWorker.Frame result = null;
|
||||
Assert.IsTrue(SpinWait.SpinUntil(() => worker.TryRead(out result), 3000), "No incoming data must not prevent the PC deadline from flushing the batch.");
|
||||
Assert.IsTrue(result.Keep);
|
||||
worker.Complete();
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public void ExperimentalSelectedEndpointUsesItsOwnVolumeNotSparseNeighbourAverage()
|
||||
{
|
||||
var reader = SeedReader(false, false);
|
||||
var config = (AsicReaderCfg)reader.GetType().GetField("asicCfg", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(reader);
|
||||
config.EnableOpticalExperiments = config.EnableOpticalBatchSelection = true;
|
||||
var samples = (TBF.Rig.TestMethods.iPerlCommunication.common.OptoTelegramRaw[])
|
||||
typeof(TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlAsicHead).GetField("optoData", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(reader);
|
||||
Assert.AreEqual(samples[reader.TestEndTelegramIx].VolumeRawExt, reader.VolumeLtrEnd, 1e-10);
|
||||
Assert.AreEqual(samples[reader.TestEndTelegramIx].TimestampExt, reader.TimestampSecEnd, 1e-10);
|
||||
}
|
||||
[DataTestMethod]
|
||||
[DataRow(5)] [DataRow(20)]
|
||||
public void ExperimentalBatchRetainsExactlyFirstAndLastAndLogsEveryInput(int count)
|
||||
{
|
||||
using (var worker = new AsicOpticalBatchWorker(count, false, 30))
|
||||
{
|
||||
DateTime pc = new DateTime(2026, 10, 6, 12, 0, 0);
|
||||
for (int i = 0; i < count; i++) worker.Enqueue(ExperimentalLine((uint)(8192 * (100 + i)), (uint)(1000 + i)), new byte[] { 1 }, pc.AddSeconds(i), 1);
|
||||
worker.Complete();
|
||||
var result = new System.Collections.Generic.List<AsicOpticalBatchWorker.Frame>();
|
||||
AsicOpticalBatchWorker.Frame frame;
|
||||
while (worker.TryRead(out frame)) result.Add(frame);
|
||||
Assert.AreEqual(count, result.Count);
|
||||
Assert.IsTrue(result[0].Keep && result[count - 1].Keep);
|
||||
Assert.AreEqual(2, System.Linq.Enumerable.Count(result, item => item.Keep));
|
||||
Assert.AreEqual(pc, result[0].PcTimestamp);
|
||||
Assert.AreEqual(count - 1, result[count - 1].ReceiveIndex);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public void ExperimentalNewSecondDropsDuplicatesAndInvalidLinesWithoutLosingTail()
|
||||
{
|
||||
using (var worker = new AsicOpticalBatchWorker(20, true, 30))
|
||||
{
|
||||
worker.Enqueue("invalid", new byte[0], DateTime.Now, 1);
|
||||
worker.Enqueue(ExperimentalLine(819200, 1000), new byte[0], DateTime.Now, 1);
|
||||
worker.Enqueue(ExperimentalLine(819201, 1001), new byte[0], DateTime.Now, 1);
|
||||
worker.Enqueue(ExperimentalLine(827392, 1002), new byte[0], DateTime.Now, 1);
|
||||
worker.Complete(); // Partial batch smaller than configured 20 is emitted on Stop.
|
||||
var result = new System.Collections.Generic.List<AsicOpticalBatchWorker.Frame>();
|
||||
AsicOpticalBatchWorker.Frame frame;
|
||||
while (worker.TryRead(out frame)) result.Add(frame);
|
||||
Assert.AreEqual(4, result.Count);
|
||||
Assert.IsFalse(result[0].Keep);
|
||||
Assert.IsTrue(result[1].Keep && result[3].Keep);
|
||||
Assert.AreEqual("DISCARDED_SECOND", result[2].Reason);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public void ExperimentalDefaultsOffAndRoundTripPreservesOptions()
|
||||
{
|
||||
var cfg = new AsicReaderCfg();
|
||||
Assert.IsFalse(cfg.EnableOpticalExperiments);
|
||||
Assert.IsTrue(cfg.ClearOpticalInputOnOpen);
|
||||
Assert.IsFalse(cfg.EnableOpticalBatchSelection || cfg.OnlyNewOpticalMeterSecond);
|
||||
cfg.EnableOpticalExperiments = cfg.EnableOpticalBatchSelection = cfg.OnlyNewOpticalMeterSecond = true;
|
||||
cfg.OpticalBatchSize = 20;
|
||||
var output = new StringWriter();
|
||||
AsicReaderCfg.Serializer.Serialize(output, cfg);
|
||||
var restored = (AsicReaderCfg)AsicReaderCfg.Serializer.Deserialize(new StringReader(output.ToString()));
|
||||
Assert.IsTrue(restored.EnableOpticalExperiments && restored.EnableOpticalBatchSelection && restored.OnlyNewOpticalMeterSecond);
|
||||
Assert.AreEqual(20, restored.OpticalBatchSize);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EarlyCaptureDefaultsOffAndRoundTripsPerReader()
|
||||
{
|
||||
var cfg = new AsicReaderCfg();
|
||||
Assert.IsFalse(cfg.EnableFlyingEarlyCapture);
|
||||
Assert.AreEqual(10, cfg.FlyingEarlyCaptureSeconds);
|
||||
cfg.EnableFlyingEarlyCapture = true;
|
||||
cfg.FlyingEarlyCaptureSeconds = 20;
|
||||
cfg.EnableFlyingDrain = true;
|
||||
var output = new StringWriter();
|
||||
AsicReaderCfg.Serializer.Serialize(output, cfg);
|
||||
var restored = (AsicReaderCfg)AsicReaderCfg.Serializer.Deserialize(new StringReader(output.ToString()));
|
||||
var reader = new TBF.Rig.RegisterReaders.AsicReader.AsicReader(restored);
|
||||
Assert.IsTrue(reader.FlyingEarlyCaptureEnabled);
|
||||
Assert.AreEqual(20, reader.FlyingEarlyCaptureSeconds);
|
||||
Assert.IsFalse(reader.FlyingDrainEnabled, "Forward measurement must finish with the reference test, not enter post-test drain.");
|
||||
Assert.IsFalse(new AsicReaderCfg().EnableFlyingEarlyCapture);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReferenceBoundaryPreservesForwardStartAndTimer()
|
||||
{
|
||||
var reader = new TBF.Rig.RegisterReaders.AsicReader.AsicReader(new AsicReaderCfg());
|
||||
var type = typeof(TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlAsicHead);
|
||||
var acquired = type.GetField("startSampleAcquired", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var timer = type.GetField("timeFromStart", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
reader.PrepareFlyingEarlyCapture();
|
||||
acquired.SetValue(reader, true);
|
||||
timer.SetValue(reader, 12);
|
||||
reader.BeginFlyingReferenceWindow();
|
||||
Assert.IsTrue((bool)acquired.GetValue(reader));
|
||||
Assert.AreEqual(12, timer.GetValue(reader));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LegacyXmlAndDefaultConfigLeaveFeatureDisabled()
|
||||
{
|
||||
var defaults = new AsicReaderCfg();
|
||||
Assert.IsFalse(defaults.EnableFlyingDrain);
|
||||
using (var input = new StringReader("<AsicReaderCfg><Name>ASIC</Name></AsicReaderCfg>"))
|
||||
{
|
||||
var cfg = (AsicReaderCfg)AsicReaderCfg.Serializer.Deserialize(input);
|
||||
Assert.IsFalse(cfg.EnableFlyingDrain);
|
||||
Assert.AreEqual(0, cfg.FlyingDrainSeconds);
|
||||
Assert.IsFalse(cfg.FlyingDrainAllowBeyondTestTime);
|
||||
Assert.IsFalse(new TBF.Rig.RegisterReaders.AsicReader.AsicReader(cfg).FlyingDrainEnabled);
|
||||
var reader = new TBF.Rig.RegisterReaders.AsicReader.AsicReader(cfg);
|
||||
reader.BeginFlyingDrain(0); // Disabled path must not inspect samples or require valid times.
|
||||
Assert.IsTrue(reader.FlyingDrainCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void XmlRoundTripPreservesReaderSpecificSettings()
|
||||
{
|
||||
var cfg = new AsicReaderCfg { EnableFlyingDrain = true, FlyingDrainSeconds = 50, FlyingDrainAllowBeyondTestTime = true };
|
||||
var writer = new StringWriter();
|
||||
AsicReaderCfg.Serializer.Serialize(writer, cfg);
|
||||
var restored = (AsicReaderCfg)AsicReaderCfg.Serializer.Deserialize(new StringReader(writer.ToString()));
|
||||
Assert.IsTrue(restored.EnableFlyingDrain);
|
||||
Assert.AreEqual(50, restored.FlyingDrainSeconds);
|
||||
Assert.IsTrue(restored.FlyingDrainAllowBeyondTestTime);
|
||||
Assert.IsFalse(new AsicReaderCfg().EnableFlyingDrain, "Settings must not leak to another reader.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MeasurementTimeSelectsLastSampleWithinReferenceWindow()
|
||||
{
|
||||
var policy = new AsicFlyingDrain(100, 78, 137, 4, 0, false);
|
||||
policy.Observe(5, 175);
|
||||
policy.Observe(6, 177);
|
||||
policy.Observe(7, 179);
|
||||
Assert.IsTrue(policy.Complete);
|
||||
Assert.AreEqual(6, policy.SelectedIndex);
|
||||
Assert.AreEqual(178.0, policy.Target);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExactTargetWaitsForRightNeighbourRequiredByLegacyVolumeFilter()
|
||||
{
|
||||
var policy = new AsicFlyingDrain(100, 78, 137, 4, 0, false);
|
||||
policy.Observe(5, 178);
|
||||
Assert.IsFalse(policy.Complete);
|
||||
policy.Observe(6, 179);
|
||||
Assert.IsTrue(policy.Complete);
|
||||
Assert.AreEqual(5, policy.SelectedIndex);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(10)] [DataRow(20)] [DataRow(30)] [DataRow(40)] [DataRow(50)]
|
||||
public void FixedDurationStartsAtSelectedDivEndpointAndCanBeCapped(int seconds)
|
||||
{
|
||||
var unlimited = new AsicFlyingDrain(100, 78, 175, 4, seconds, true);
|
||||
Assert.AreEqual(175.0 + seconds, unlimited.Target);
|
||||
unlimited.Observe(5, unlimited.Target + 0.5);
|
||||
Assert.IsFalse(unlimited.Complete);
|
||||
unlimited.Observe(6, unlimited.Target + 1.5);
|
||||
Assert.IsTrue(unlimited.Complete);
|
||||
Assert.AreEqual(5, unlimited.SelectedIndex);
|
||||
var capped = new AsicFlyingDrain(100, 78, 175, 4, seconds, false);
|
||||
Assert.AreEqual(178.0, capped.Target);
|
||||
capped.Observe(5, 177);
|
||||
capped.Observe(6, 179);
|
||||
Assert.IsTrue(capped.Complete);
|
||||
Assert.AreEqual(5, capped.SelectedIndex);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MeterTimeModeNeverExtrapolatesEvenWhenAllowBeyondChecked()
|
||||
{
|
||||
var policy = new AsicFlyingDrain(100, 78, 137, 4, 0, true);
|
||||
policy.Observe(5, 177);
|
||||
policy.Observe(6, 179);
|
||||
Assert.AreEqual(5, policy.SelectedIndex);
|
||||
Assert.IsTrue(policy.Complete);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void InvalidOrBackwardsTimesDoNotCompleteAndRolloverExtendedTimeIsAccepted()
|
||||
{
|
||||
var policy = new AsicFlyingDrain(65530, 20, 65535, 4, 0, false);
|
||||
policy.Observe(5, double.NaN);
|
||||
policy.Observe(6, double.PositiveInfinity);
|
||||
policy.Observe(7, 1);
|
||||
Assert.IsFalse(policy.Complete);
|
||||
Assert.AreEqual(4, policy.SelectedIndex);
|
||||
policy.Observe(8, 65549);
|
||||
policy.Observe(9, 65551);
|
||||
Assert.IsTrue(policy.Complete);
|
||||
Assert.AreEqual(8, policy.SelectedIndex);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PolicyRejectsInvalidInitialSamplesAndInvalidConfig()
|
||||
{
|
||||
Assert.ThrowsException<InvalidOperationException>(() => new AsicFlyingDrain(0, 78, 37, 4, 0, false));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => new AsicFlyingDrain(100, 0, 137, 4, 0, false));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => new AsicFlyingDrain(100, 78, 137, 4, 9, false));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReaderReplaysBufferedTailAndPublishesSelectedFilteredVolume()
|
||||
{
|
||||
var reader = SeedReader(true, false);
|
||||
reader.BeginFlyingDrain(78);
|
||||
Assert.IsTrue(reader.FlyingDrainCompleted);
|
||||
reader.FinishFlyingDrain("completed");
|
||||
Assert.AreEqual(5, reader.TestEndTelegramIx);
|
||||
Assert.AreEqual(100.0, reader.TimestampSecStart);
|
||||
Assert.AreEqual(178.0, reader.TimestampSecEnd);
|
||||
Assert.AreEqual(15.0, reader.VolumeLtrEnd, 1e-9, "Legacy end filter must average indices 4, 5, 6.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReaderDisabledRetainsExistingEndpointAndInvalidFilterWindowCannotComplete()
|
||||
{
|
||||
var disabled = SeedReader(false, false);
|
||||
disabled.BeginFlyingDrain(78);
|
||||
Assert.AreEqual(3, disabled.TestEndTelegramIx);
|
||||
Assert.AreEqual(137.0, disabled.TimestampSecEnd);
|
||||
var invalid = SeedReader(true, true);
|
||||
invalid.BeginFlyingDrain(78);
|
||||
Assert.IsFalse(invalid.FlyingDrainCompleted, "Invalid neighbour must not produce a zero-volume successful result.");
|
||||
invalid.FinishFlyingDrain("timeout");
|
||||
}
|
||||
|
||||
private static TBF.Rig.RegisterReaders.AsicReader.AsicReader SeedReader(bool enabled, bool invalidNeighbour)
|
||||
{
|
||||
var reader = new TBF.Rig.RegisterReaders.AsicReader.AsicReader(new AsicReaderCfg
|
||||
{ Name = "ASIC_TEST", EnableFlyingDrain = enabled });
|
||||
var samples = new TBF.Rig.TestMethods.iPerlCommunication.common.OptoTelegramRaw[8];
|
||||
double[] times = { 99, 100, 101, 137, 177, 178, 179, 180 };
|
||||
for (int i = 0; i < samples.Length; i++)
|
||||
samples[i] = new TBF.Rig.TestMethods.iPerlCommunication.common.OptoTelegramRaw
|
||||
{
|
||||
Counter = i, TimestampExt = times[i], VolumeRawExt = 10 + i,
|
||||
Flags = invalidNeighbour && i == 6 ? TBF.Rig.TestMethods.iPerlCommunication.common.OptoTelegramFlags.InvalidTelegram
|
||||
: TBF.Rig.TestMethods.iPerlCommunication.common.OptoTelegramFlags.OK
|
||||
};
|
||||
var type = typeof(TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlAsicHead);
|
||||
const BindingFlags fields = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
type.GetField("optoData", fields).SetValue(reader, samples);
|
||||
type.GetField("optoDataCount", fields).SetValue(reader, samples.Length);
|
||||
reader.TestStartTelegramIx = 1;
|
||||
reader.TestEndTelegramIx = 3;
|
||||
return reader;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DrainStateRetainsReaderOperationWithoutRestartingOrClearingIt()
|
||||
{
|
||||
var current = typeof(TBF.Rig.State).GetField("currentState", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
object previous = current.GetValue(null);
|
||||
int starts = 0, stops = 0;
|
||||
var reader = new Moq.Mock<TBF.Rig.IOperation>();
|
||||
reader.Setup(op => op.Start()).Callback(() => starts++);
|
||||
reader.Setup(op => op.Stop()).Callback(() => stops++);
|
||||
try
|
||||
{
|
||||
current.SetValue(null, TBF.Rig.State.Create("idle"));
|
||||
TBF.Rig.State.Create("measurement").AddOperation(reader.Object).EnterState();
|
||||
TBF.Rig.State.Create("drain").AddOperation(reader.Object).EnterState();
|
||||
Assert.AreEqual(1, starts);
|
||||
Assert.AreEqual(0, stops);
|
||||
TBF.Rig.State.Create("calculation").EnterState();
|
||||
Assert.AreEqual(1, stops);
|
||||
}
|
||||
finally { current.SetValue(null, previous); }
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GuiUnlockAndCheckboxGateOptionsAndRequestRestartOnChange()
|
||||
{
|
||||
Exception failure = null;
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ui = new AsicReaderMeasurementCfgCtrl())
|
||||
{
|
||||
var cfg = new AsicReaderCfg();
|
||||
ui.Config = cfg;
|
||||
const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
var enable = (CheckBox)ui.GetType().GetField("enableEarlyCaptureCheckBox", flags).GetValue(ui);
|
||||
var mode = (NumericUpDown)ui.GetType().GetField("earlyCaptureSeconds", flags).GetValue(ui);
|
||||
Assert.IsFalse(enable.Enabled || mode.Enabled);
|
||||
ui.Unlock();
|
||||
Assert.IsTrue(enable.Enabled);
|
||||
Assert.IsFalse(mode.Enabled);
|
||||
enable.Checked = true;
|
||||
Assert.IsTrue(mode.Enabled);
|
||||
mode.Value = 30;
|
||||
cfg.EnableFlyingDrain = true;
|
||||
string message = "";
|
||||
Assert.AreEqual(Common.CfgUpdateFlags.None, ui.VerifyCfg(ref message));
|
||||
Assert.AreEqual(Common.CfgUpdateFlags.RestartRqrd, ui.UpdateCfg());
|
||||
Assert.AreEqual(30, cfg.FlyingEarlyCaptureSeconds);
|
||||
Assert.IsTrue(cfg.EnableFlyingEarlyCapture);
|
||||
Assert.IsFalse(cfg.EnableFlyingDrain);
|
||||
Assert.AreEqual(Common.CfgUpdateFlags.None, ui.UpdateCfg());
|
||||
var experiments = (CheckBox)ui.GetType().GetField("experimentsCheckBox", flags).GetValue(ui);
|
||||
var batching = (CheckBox)ui.GetType().GetField("batchCheckBox", flags).GetValue(ui);
|
||||
var size = (NumericUpDown)ui.GetType().GetField("batchSize", flags).GetValue(ui);
|
||||
Assert.IsTrue(experiments.Enabled);
|
||||
Assert.IsFalse(batching.Enabled || size.Enabled);
|
||||
experiments.Checked = true;
|
||||
Assert.IsTrue(batching.Enabled);
|
||||
Assert.IsFalse(size.Enabled);
|
||||
batching.Checked = true;
|
||||
Assert.IsTrue(size.Enabled);
|
||||
size.Value = 20;
|
||||
Assert.AreEqual(Common.CfgUpdateFlags.Error, ui.VerifyCfg(ref message));
|
||||
cfg.EnableDetailedLogPerWM = true;
|
||||
Assert.AreEqual(Common.CfgUpdateFlags.None, ui.VerifyCfg(ref message));
|
||||
Assert.AreEqual(Common.CfgUpdateFlags.RestartRqrd, ui.UpdateCfg());
|
||||
Assert.IsTrue(cfg.EnableOpticalExperiments && cfg.EnableOpticalBatchSelection);
|
||||
Assert.AreEqual(20, cfg.OpticalBatchSize);
|
||||
}
|
||||
}
|
||||
catch (Exception error) { failure = error; }
|
||||
});
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
Assert.IsTrue(thread.Join(10000));
|
||||
if (failure != null) throw failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,6 +380,58 @@ namespace TBFTests.Rig.RegisterReaders.AsicReader
|
||||
Assert.IsFalse(reader.Open);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FlyingLegacy_EndSelectionKeepsExistingRunDelay_NotNewestTelegram()
|
||||
{
|
||||
var reader = Create(1, 1, 1);
|
||||
reader.ReadDatastreamOp().Start();
|
||||
var type = typeof(IperlAsicHead);
|
||||
const BindingFlags fields = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
type.GetField("startSampleAcquired", fields).SetValue(reader, true);
|
||||
var history = (int[])type.GetField("endTelegramIdx", fields).GetValue(reader);
|
||||
for (int i = 0; i < history.Length; i++) history[i] = 100 + i;
|
||||
type.GetField("currentTelegramIx", fields).SetValue(reader, 999);
|
||||
int expectedEnd = history[history.Length - 1];
|
||||
Assert.AreEqual(Event.ReadRegisterDone, reader.ReadDatastreamOp().Run());
|
||||
Assert.AreEqual(expectedEnd, reader.TestEndTelegramIx,
|
||||
"Disabled drain must preserve the delayed endpoint, not replace it with the newest telegram.");
|
||||
Assert.AreEqual(999, history[0]);
|
||||
Assert.IsTrue(reader.Open);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FlyingLegacy_NoSamplesRetainsZeroVolumeAndMissingSampleFlag()
|
||||
{
|
||||
var reader = Create(1, 1, 1);
|
||||
reader.ReadDatastreamOp().Start();
|
||||
Assert.IsTrue(reader.NoSamples);
|
||||
Assert.AreEqual(0.0, reader.VolumeLtrStart);
|
||||
Assert.AreEqual(0.0, reader.VolumeLtrEnd);
|
||||
Assert.AreEqual(0.0, reader.TimestampSecStart);
|
||||
Assert.AreEqual(0.0, reader.TimestampSecEnd);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(1, 1)]
|
||||
[DataRow(1, 2)]
|
||||
[DataRow(2, 1)]
|
||||
[DataRow(2, 2)]
|
||||
public void FlyingLegacy_StopClosesPortWithoutReadingAdditionalTelegrams(int group, int mux)
|
||||
{
|
||||
var reader = Create(1, group, mux);
|
||||
var operation = reader.ReadDatastreamOp();
|
||||
operation.Start();
|
||||
int reads = reader.TotalReads;
|
||||
operation.Stop();
|
||||
Assert.IsFalse(reader.Open);
|
||||
Assert.AreEqual(1, reader.Closes);
|
||||
Assert.AreEqual(reads, reader.TotalReads,
|
||||
"Legacy stop must not consume an additional optical telegram.");
|
||||
Assert.AreEqual(0, reader.ActiveReads);
|
||||
reader.EndSession();
|
||||
Assert.AreEqual(1, reader.Closes, "Session cleanup must remain idempotent.");
|
||||
}
|
||||
|
||||
private SimulatedAsic Create(int position, int group, int mux)
|
||||
{
|
||||
var cfg = (AsicReaderCfg)new Factory().DefaultConfig();
|
||||
@@ -433,7 +485,7 @@ namespace TBFTests.Rig.RegisterReaders.AsicReader
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, SimulatedAsic> ports;
|
||||
private volatile bool open;
|
||||
public int Opens, Closes, ActiveReads, ClosedDuringRead, CommandDisposals;
|
||||
public int Opens, Closes, ActiveReads, ClosedDuringRead, CommandDisposals, TotalReads;
|
||||
public uint RawVolume = 40000;
|
||||
public bool NoData;
|
||||
public readonly ManualResetEventSlim ReadEntered = new ManualResetEventSlim();
|
||||
@@ -471,6 +523,7 @@ namespace TBFTests.Rig.RegisterReaders.AsicReader
|
||||
|
||||
protected override string ReadOptoLine(out byte[] bytes)
|
||||
{
|
||||
Interlocked.Increment(ref TotalReads);
|
||||
Interlocked.Increment(ref ActiveReads);
|
||||
try
|
||||
{
|
||||
|
||||
@@ -166,6 +166,7 @@
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\AsicCorrectionsTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\ManualReaderPortReleaseTests.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\AsicGroupPortLifecycleTests.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\AsicReader\AsicFlyingDrainTests.cs" />
|
||||
<None Include="Rig\RegisterReaders\AsicReader\AsicGroupPortLifecycleTests.txt" />
|
||||
<Compile Include="Rig\RegisterReaders\AllyReader\AllyOpticalSampleTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\AllyReader\Optical\AllyOpticalProtocolTests.cs" />
|
||||
|
||||
Reference in New Issue
Block a user