Refactor GenesisSmartReader Q3 calibration tests and logic:
- Expand Q3 calibration tests with diverse scenarios in `GenesisSmartReader_Q3Test`. - Introduce channel-specific validation for calibration tests. - Adjust methods for Q3 calibration in `GenesisSmartReader` to support per-channel factors. - Add utilities for preparing simulation data and refinements for test consistency. - Update `CliRunner` with improved task management, timeout handling, and logging enhancements.
This commit is contained in:
parent
33ef914984
commit
5a8478e782
@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// Build Number
|
// Build Number
|
||||||
// Revision
|
// Revision
|
||||||
//
|
//
|
||||||
[assembly: AssemblyVersion("3.9.3056.1")]
|
[assembly: AssemblyVersion("3.9.3057.1")]
|
||||||
[assembly: AssemblyFileVersion("3.9.3056.1")]
|
[assembly: AssemblyFileVersion("3.9.3057.1")]
|
||||||
|
|||||||
@ -633,8 +633,31 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
public double TimestampSecEndCh1 => NoSamples ? 0 : GetCachedTime(_recalculatedStartEndByChannel, 0, 1);
|
public double TimestampSecEndCh1 => NoSamples ? 0 : GetCachedTime(_recalculatedStartEndByChannel, 0, 1);
|
||||||
public double TimestampSecEndCh2 => NoSamples ? 0 : GetCachedTime(_recalculatedStartEndByChannel, 1, 1);
|
public double TimestampSecEndCh2 => NoSamples ? 0 : GetCachedTime(_recalculatedStartEndByChannel, 1, 1);
|
||||||
public double TimestampSecEndCh3 => NoSamples ? 0 : GetCachedTime(_recalculatedStartEndByChannel, 2, 1);
|
public double TimestampSecEndCh3 => NoSamples ? 0 : GetCachedTime(_recalculatedStartEndByChannel, 2, 1);
|
||||||
public double VolumeLtrStartRaw => NoSamples ? 0 : AverageCachedVolume(_rawStartEndByChannel, 0);
|
|
||||||
public double VolumeLtrEndRaw => NoSamples ? 0 : AverageCachedVolume(_rawStartEndByChannel, 1);
|
|
||||||
|
public double VolumeLtrStartAverage => NoSamples ? 0 : AverageCachedVolume(_recalculatedStartEndByChannel, 0);
|
||||||
|
public double VolumeLtrEndAwerage => NoSamples ? 0 : AverageCachedVolume(_recalculatedStartEndByChannel, 1);
|
||||||
|
|
||||||
|
public double VolumeLtrStartRawRaw => NoSamples ? 0 : AverageCachedVolume(_rawStartEndByChannel, 0);
|
||||||
|
public double VolumeLtrEndRawRaw => NoSamples ? 0 : AverageCachedVolume(_rawStartEndByChannel, 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public double VolumeLtrStartRawCh1 => NoSamples ? 0 : GetCachedVolume(_rawStartEndByChannel, 0, 0);
|
||||||
|
public double VolumeLtrStartRawCh2 => NoSamples ? 0 : GetCachedVolume(_rawStartEndByChannel, 1, 0);
|
||||||
|
public double VolumeLtrStartRawCh3 => NoSamples ? 0 : GetCachedVolume(_rawStartEndByChannel, 2, 0);
|
||||||
|
|
||||||
|
public double VolumeLtrEndRawCh1 => NoSamples ? 0 : GetCachedVolume(_rawStartEndByChannel, 0, 1);
|
||||||
|
public double VolumeLtrEndRawCh2 => NoSamples ? 0 : GetCachedVolume(_rawStartEndByChannel, 1, 1);
|
||||||
|
public double VolumeLtrEndRawCh3 => NoSamples ? 0 : GetCachedVolume(_rawStartEndByChannel, 2, 1);
|
||||||
|
|
||||||
|
public double TimestampSecStartRawCh1 => NoSamples ? 0 : GetCachedTime(_rawStartEndByChannel, 0, 0);
|
||||||
|
public double TimestampSecStartRawCh2 => NoSamples ? 0 : GetCachedTime(_rawStartEndByChannel, 1, 0);
|
||||||
|
public double TimestampSecStartRawCh3 => NoSamples ? 0 : GetCachedTime(_rawStartEndByChannel, 2, 0);
|
||||||
|
|
||||||
|
public double TimestampSecEndRawCh1 => NoSamples ? 0 : GetCachedTime(_rawStartEndByChannel, 0, 1);
|
||||||
|
public double TimestampSecEndRawCh2 => NoSamples ? 0 : GetCachedTime(_rawStartEndByChannel, 1, 1);
|
||||||
|
public double TimestampSecEndRawCh3 => NoSamples ? 0 : GetCachedTime(_rawStartEndByChannel, 2, 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -643,7 +666,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return VolumeLtrStartRaw;
|
return VolumeLtrStartAverage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,7 +675,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return VolumeLtrEndRaw;
|
return VolumeLtrEndAwerage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1302,7 +1325,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
log.DebugFormat("DataStreamPostProcessing() - harcoded call GetQ3Calibration(200.0, 120.0, 15625.0);");
|
log.DebugFormat("DataStreamPostProcessing() - harcoded call GetQ3Calibration(200.0, 120.0, 15625.0);");
|
||||||
GetQ3Calibration(200.0, 120.0, 15625.0);
|
SetQ3Calibration(new double[]{15625.0,15625.0,15625.0 });
|
||||||
|
CalculateQ3Calibration(200.0, 120.0);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -3885,20 +3909,61 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
private bool isQ3CalibValid = false;
|
private double[] q3CalibInitial = {Double.NaN,Double.NaN,Double.NaN};
|
||||||
private double q3Calib = 0;
|
private bool[] isChQ3CalibValid = { false,false,false};
|
||||||
|
private double[] q3CalibCh = {Double.NaN,Double.NaN,Double.NaN};
|
||||||
|
|
||||||
public bool Q3CalibValid { get => isQ3CalibValid; }
|
|
||||||
public double Q3CalibValue { get => q3Calib; }
|
public bool Q3CalibValid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (var b in isChQ3CalibValid)
|
||||||
|
{
|
||||||
|
if (!b)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void GetQ3Calibration(double refVolume, double refTime, double initCalibFactor)
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] Q3CalibValue { get => q3CalibInitial; }
|
||||||
|
|
||||||
|
public bool Q3Calib_Ch1Valid { get => isChQ3CalibValid[0]; }
|
||||||
|
public bool Q3Calib_Ch2Valid { get => isChQ3CalibValid[1]; }
|
||||||
|
public bool Q3Calib_Ch3Valid { get => isChQ3CalibValid[2]; }
|
||||||
|
public double Q3Calib_Ch1Value { get => q3CalibCh[0]; }
|
||||||
|
public double Q3Calib_Ch2Value { get => q3CalibCh[1]; }
|
||||||
|
public double Q3Calib_Ch3Value { get => q3CalibCh[2]; }
|
||||||
|
|
||||||
|
void SetQ3Calibration(double[] q3CalibInitial) { this.q3CalibInitial = q3CalibInitial; }
|
||||||
|
|
||||||
|
public void CalculateQ3Calibration(double refVolume, double refTime)
|
||||||
|
{
|
||||||
|
GetQ3Calibration(refVolume, refTime, q3CalibInitial, ref isChQ3CalibValid, ref q3CalibCh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetQ3Calibration(double refVolume, double refTime, double[] initCalibFactor, ref bool[] isChQ3CalibValid, ref double[] q3CalibCh)
|
||||||
{
|
{
|
||||||
log.Debug("=== Q3 CALIBRATION START ===");
|
log.Debug("=== Q3 CALIBRATION START ===");
|
||||||
|
|
||||||
isQ3CalibValid = false;
|
//initialisation
|
||||||
q3Calib = 0.0;
|
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
|
||||||
|
{
|
||||||
|
q3CalibCh[iChannel] = 0.0;
|
||||||
|
isChQ3CalibValid[iChannel] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var init in initCalibFactor)
|
||||||
|
{
|
||||||
|
if (Double.IsNaN(init))
|
||||||
|
{
|
||||||
|
log.Error("Q3 Calibration: Initial calibration value is NaN");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Debug($"Inputs: refVolume={refVolume}, refTime={refTime}, initCalibFactor={initCalibFactor}");
|
log.Debug($"Inputs: refVolume={refVolume}, refTime={refTime}, initCalibFactor={initCalibFactor}");
|
||||||
|
|
||||||
@ -3914,7 +3979,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refVolume <= 0 || refTime <= 0 || initCalibFactor <= 0)
|
if (refVolume <= 0 || refTime <= 0)
|
||||||
{
|
{
|
||||||
log.Debug("EXIT: Invalid input values (<= 0)");
|
log.Debug("EXIT: Invalid input values (<= 0)");
|
||||||
return;
|
return;
|
||||||
@ -3931,6 +3996,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
|
|
||||||
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
|
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
|
||||||
{
|
{
|
||||||
|
|
||||||
var channelData = _rawStartEndByChannel[iChannel];
|
var channelData = _rawStartEndByChannel[iChannel];
|
||||||
|
|
||||||
if (channelData == null || channelData.Length < 2)
|
if (channelData == null || channelData.Length < 2)
|
||||||
@ -3938,7 +4004,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
log.Debug($"Channel {iChannel}: SKIPPED (no data)");
|
log.Debug($"Channel {iChannel}: SKIPPED (no data)");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var start = channelData[0];
|
var start = channelData[0];
|
||||||
var end = channelData[1];
|
var end = channelData[1];
|
||||||
|
|
||||||
@ -3962,10 +4028,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
log.Debug($" End: T={end.TimestampExt}, V={end.VolumeRawExt}");
|
log.Debug($" End: T={end.TimestampExt}, V={end.VolumeRawExt}");
|
||||||
log.Debug($" Delta: dT={deltaTime}, dV={deltaVolume}");
|
log.Debug($" Delta: dT={deltaTime}, dV={deltaVolume}");
|
||||||
|
|
||||||
|
double recalculatedDeltaVolume = 0.0f;
|
||||||
if (deltaTime > 0)
|
if (deltaTime > 0)
|
||||||
{
|
{
|
||||||
double timeCoef = refTime / deltaTime;
|
double timeCoef = refTime / deltaTime; //?
|
||||||
double recalculatedDeltaVolume = deltaVolume * timeCoef;
|
recalculatedDeltaVolume = deltaVolume * timeCoef;
|
||||||
|
|
||||||
recalculatedVariablesByChannel[iChannel][1].VolumeRawExt =
|
recalculatedVariablesByChannel[iChannel][1].VolumeRawExt =
|
||||||
recalculatedVariablesByChannel[iChannel][0].VolumeRawExt + recalculatedDeltaVolume;
|
recalculatedVariablesByChannel[iChannel][0].VolumeRawExt + recalculatedDeltaVolume;
|
||||||
@ -3979,52 +4046,18 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
|
|
||||||
recalculatedVariablesByChannel[iChannel][0].TimestampExt = 0;
|
recalculatedVariablesByChannel[iChannel][0].TimestampExt = 0;
|
||||||
recalculatedVariablesByChannel[iChannel][1].TimestampExt = refTime;
|
recalculatedVariablesByChannel[iChannel][1].TimestampExt = refTime;
|
||||||
}
|
|
||||||
|
if (Math.Abs(recalculatedDeltaVolume) <= Double.Epsilon)
|
||||||
double scaling = 15625.0;
|
{
|
||||||
log.Debug($"Scaling={scaling}");
|
log.Debug($"Channel {iChannel}: recalculatedDeltaVolume is zero");
|
||||||
|
continue;
|
||||||
if (scaling <= 0)
|
}
|
||||||
{
|
|
||||||
log.Debug("EXIT: scaling <= 0");
|
q3CalibCh[iChannel] = (refVolume / recalculatedDeltaVolume) * initCalibFactor[iChannel];
|
||||||
return;
|
double diffPercent = Math.Abs((initCalibFactor[iChannel] - q3CalibCh[iChannel] ) / initCalibFactor[iChannel]) * 100.0;
|
||||||
}
|
isChQ3CalibValid[iChannel] = diffPercent <= 5.0;
|
||||||
|
log.Debug($"Calculated Q3Calib Ch[{iChannel}] ={q3CalibCh[iChannel]} DiffPercent={diffPercent}% isValid[{isChQ3CalibValid[iChannel]}] IninitCalibFactor={initCalibFactor}");
|
||||||
double avgRawVolume = AverageCachedVolume(recalculatedVariablesByChannel, 0);
|
|
||||||
log.Debug($"AverageCachedVolume={avgRawVolume}");
|
|
||||||
|
|
||||||
if (avgRawVolume <= 0)
|
|
||||||
{
|
|
||||||
log.Debug("EXIT: avgRawVolume <= 0");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double measuredVolume = avgRawVolume / scaling;
|
|
||||||
log.Debug($"MeasuredVolume={measuredVolume}");
|
|
||||||
|
|
||||||
if (measuredVolume <= 0)
|
|
||||||
{
|
|
||||||
log.Debug("EXIT: measuredVolume <= 0");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
q3Calib = (refVolume / measuredVolume) * initCalibFactor;
|
|
||||||
|
|
||||||
log.Debug($"Q3Calib (raw)={q3Calib}");
|
|
||||||
|
|
||||||
double diffPercent =
|
|
||||||
Math.Abs((q3Calib - initCalibFactor) / initCalibFactor) * 100.0;
|
|
||||||
|
|
||||||
log.Debug($"DiffPercent={diffPercent}%");
|
|
||||||
|
|
||||||
isQ3CalibValid = diffPercent <= 5.0;
|
|
||||||
|
|
||||||
log.Debug($"Validation: {(isQ3CalibValid ? "VALID" : "INVALID")}");
|
|
||||||
|
|
||||||
if (!isQ3CalibValid)
|
|
||||||
{
|
|
||||||
q3Calib = 0.0;
|
|
||||||
log.Debug("Q3Calib reset to 0 due to invalid result");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("=== Q3 CALIBRATION END ===");
|
log.Debug("=== Q3 CALIBRATION END ===");
|
||||||
|
|||||||
@ -13,46 +13,29 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
|||||||
{
|
{
|
||||||
public class CliRunner
|
public class CliRunner
|
||||||
{
|
{
|
||||||
//static readonly ILog log = LogManager.GetLogger(typeof(CliRunner));
|
|
||||||
private readonly ILog log;
|
private readonly ILog log;
|
||||||
private List<Task> taskPool = new List<Task>();
|
private readonly List<CliTaskInfo> taskPool = new List<CliTaskInfo>();
|
||||||
private long startTime;
|
private long startTimeMs;
|
||||||
private long incommingTime;
|
private long incommingTimeMs;
|
||||||
|
|
||||||
public List<Task> TaskPool { get { return taskPool; } }
|
|
||||||
public void AddTask(Task task) { taskPool.Add(task); }
|
|
||||||
public void WaitAll() { Task.WaitAll(taskPool.ToArray()); }
|
|
||||||
public void Clear() { taskPool.Clear(); }
|
|
||||||
public void CancelAll() { Task.WhenAll(taskPool).ContinueWith(t => { }); }
|
|
||||||
|
|
||||||
public void StartAll()
|
public List<CliTaskInfo> TaskPool
|
||||||
{
|
{
|
||||||
taskPool.ForEach(t => t.Start());
|
get { return taskPool; }
|
||||||
}
|
|
||||||
|
|
||||||
public bool AreTasksDone()
|
|
||||||
{
|
|
||||||
return taskPool.All(task => task.IsCompleted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long StartTime
|
public long StartTime
|
||||||
{
|
{
|
||||||
get => startTime;
|
get { return startTimeMs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public long IncommingTime
|
public long IncommingTime
|
||||||
{
|
{
|
||||||
get => incommingTime;
|
get { return incommingTimeMs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TimeOutReceived(long timeout)
|
|
||||||
{
|
|
||||||
return (DateTime.Now.Ticks - startTime) > timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CliRunner(bool isCliLogging)
|
public CliRunner(bool isCliLogging)
|
||||||
{
|
{
|
||||||
startTime = DateTime.Now.Ticks;
|
ResetStartTime();
|
||||||
|
|
||||||
if (isCliLogging)
|
if (isCliLogging)
|
||||||
{
|
{
|
||||||
@ -60,88 +43,180 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
|||||||
{
|
{
|
||||||
log = new ScopedLoggerFactory().CreateLogger<CliRunner>(
|
log = new ScopedLoggerFactory().CreateLogger<CliRunner>(
|
||||||
@"C:\TBF\Logs\CliRunner.txt",
|
@"C:\TBF\Logs\CliRunner.txt",
|
||||||
10, // maxFileSizeMB
|
10,
|
||||||
7, // maxBackups
|
7,
|
||||||
log4net.Core.Level.Debug,
|
log4net.Core.Level.Debug,
|
||||||
true, // zipRolledFiles
|
true,
|
||||||
true, // singleZipPerDay
|
true,
|
||||||
TimeSpan.FromMinutes(2) // zipScanInterval
|
TimeSpan.FromMinutes(2)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Fallback to console or handle gracefully
|
|
||||||
Console.WriteLine($"Failed to initialize logger: {ex.Message}");
|
Console.WriteLine($"Failed to initialize logger: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public void ResetStartTime()
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
public void AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg)
|
|
||||||
{
|
{
|
||||||
var task = SendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
|
startTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
taskPool.Add(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSendAsync(string fileName, string args)
|
|
||||||
{
|
|
||||||
var task = SendAsync(fileName, args);
|
|
||||||
taskPool.Add(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<string> SendAsync(string fileName, string args, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
var psi = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = fileName,
|
|
||||||
Arguments = args,
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
RedirectStandardError = true,
|
|
||||||
UseShellExecute = false,
|
|
||||||
CreateNoWindow = true
|
|
||||||
};
|
|
||||||
|
|
||||||
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
public void Clear()
|
||||||
process.Start();
|
{
|
||||||
|
foreach (var item in taskPool)
|
||||||
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
|
|
||||||
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
|
try
|
||||||
}
|
{
|
||||||
catch (OperationCanceledException)
|
item?.Cts?.Dispose();
|
||||||
{
|
}
|
||||||
if (!process.HasExited)
|
catch
|
||||||
{
|
{
|
||||||
process.Kill();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw;
|
try
|
||||||
|
{
|
||||||
|
if (item?.Process != null)
|
||||||
|
{
|
||||||
|
item.Process.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//comming answer from serial port - good place for time stamp
|
taskPool.Clear();
|
||||||
incommingTime = DateTime.Now.Ticks;
|
|
||||||
string allOutput = (stdOutTask.Result ?? "") + (stdErrTask.Result ?? "");
|
|
||||||
log?.Debug(allOutput);
|
|
||||||
return allOutput;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void WaitAll()
|
||||||
public void AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new()
|
|
||||||
{
|
{
|
||||||
var task = RunAndCaptureJsonAsync<T>(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
|
var tasks = taskPool
|
||||||
taskPool.Add(task);
|
.Where(t => t != null && t.Task != null)
|
||||||
|
.Select(t => t.Task)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (tasks.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Task.WaitAll(tasks);
|
||||||
|
}
|
||||||
|
catch (AggregateException)
|
||||||
|
{
|
||||||
|
RefreshTaskStates();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T> RunAndCaptureJsonAsync<T>(string fileName, string args, CancellationToken ct = default) where T : new()
|
public bool AreTasksDone()
|
||||||
|
{
|
||||||
|
RefreshTaskStates();
|
||||||
|
return taskPool.Count > 0 && taskPool.All(t => t.State != CliTaskState.Running);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TimeOutReceived(long timeoutMs)
|
||||||
|
{
|
||||||
|
long nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
return (nowMs - startTimeMs) > timeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshTaskStates()
|
||||||
|
{
|
||||||
|
foreach (var item in taskPool)
|
||||||
|
{
|
||||||
|
if (item == null || item.Task == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (item.State == CliTaskState.TimedOut)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!item.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
item.State = CliTaskState.Running;
|
||||||
|
}
|
||||||
|
else if (item.Task.IsCanceled)
|
||||||
|
{
|
||||||
|
item.State = CliTaskState.Canceled;
|
||||||
|
}
|
||||||
|
else if (item.Task.IsFaulted)
|
||||||
|
{
|
||||||
|
item.State = CliTaskState.Faulted;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.State = CliTaskState.Completed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelUndoneTasksAsTimedOut()
|
||||||
|
{
|
||||||
|
foreach (var item in taskPool)
|
||||||
|
{
|
||||||
|
if (item == null || item.Task == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (item.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
if (item.Task.IsCanceled)
|
||||||
|
item.State = CliTaskState.Canceled;
|
||||||
|
else if (item.Task.IsFaulted)
|
||||||
|
item.State = CliTaskState.Faulted;
|
||||||
|
else
|
||||||
|
item.State = CliTaskState.Completed;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.State = CliTaskState.TimedOut;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
item.Cts?.Cancel();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log?.Warn("Cancel token failed", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (item.Process != null && !item.Process.HasExited)
|
||||||
|
{
|
||||||
|
item.Process.Kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log?.Warn("Kill process failed", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg)
|
||||||
|
{
|
||||||
|
AddSendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddSendAsync(string fileName, string args)
|
||||||
|
{
|
||||||
|
ResetStartTime();
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
var info = new CliTaskInfo
|
||||||
|
{
|
||||||
|
Cts = cts,
|
||||||
|
Name = "SendAsync"
|
||||||
|
};
|
||||||
|
|
||||||
|
var task = SendAsync(fileName, args, info, cts.Token);
|
||||||
|
info.Task = task;
|
||||||
|
taskPool.Add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> SendAsync(string fileName, string args, CliTaskInfo info, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var psi = new ProcessStartInfo
|
var psi = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
@ -153,80 +228,210 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
|||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
};
|
};
|
||||||
|
|
||||||
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
|
||||||
process.Start();
|
{
|
||||||
|
info.Process = process;
|
||||||
|
|
||||||
var stdoutTask = process.StandardOutput.ReadToEndAsync();
|
try
|
||||||
var stderrTask = process.StandardError.ReadToEndAsync();
|
{
|
||||||
|
process.Start();
|
||||||
|
|
||||||
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct));
|
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
|
||||||
|
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
|
||||||
|
|
||||||
string allOutput = (stdoutTask.Result ?? "") + (stderrTask.Result ?? "");
|
try
|
||||||
log?.Debug(allOutput);
|
{
|
||||||
|
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
if (info.State != CliTaskState.TimedOut)
|
||||||
|
info.State = CliTaskState.Canceled;
|
||||||
|
|
||||||
string json = ExtractJson(allOutput);
|
if (!process.HasExited)
|
||||||
if (TryJsonStringDeserialize(json, out T result)) return result;
|
{
|
||||||
return default;
|
process.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
incommingTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
|
||||||
|
string allOutput = (stdOutTask.Result ?? "") + (stdErrTask.Result ?? "");
|
||||||
|
log?.Debug(allOutput);
|
||||||
|
|
||||||
|
info.State = CliTaskState.Completed;
|
||||||
|
return allOutput;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
info.State = CliTaskState.Faulted;
|
||||||
|
log?.Error("SendAsync failed", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
info.Process = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryJsonStringDeserialize<T>(string json, out T runAndCaptureJsonAsync) where T : new()
|
public void AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new()
|
||||||
|
{
|
||||||
|
AddRunAndCaptureJsonAsync<T>(
|
||||||
|
data.SerialPortCmdClientPath,
|
||||||
|
data.DefaultArgSettings(eMeterArg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRunAndCaptureJsonAsync<T>(string fileName, string args) where T : new()
|
||||||
|
{
|
||||||
|
ResetStartTime();
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
var info = new CliTaskInfo
|
||||||
|
{
|
||||||
|
Cts = cts,
|
||||||
|
Name = $"RunAndCaptureJsonAsync<{typeof(T).Name}>"
|
||||||
|
};
|
||||||
|
|
||||||
|
var task = RunAndCaptureJsonAsync<T>(fileName, args, info, cts.Token);
|
||||||
|
|
||||||
|
info.Task = task;
|
||||||
|
taskPool.Add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> RunAndCaptureJsonAsync<T>(string fileName, string args, CliTaskInfo info, CancellationToken ct = default) where T : new()
|
||||||
|
{
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = fileName,
|
||||||
|
Arguments = args,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
|
||||||
|
{
|
||||||
|
info.Process = process;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
process.Start();
|
||||||
|
|
||||||
|
var stdoutTask = process.StandardOutput.ReadToEndAsync();
|
||||||
|
var stderrTask = process.StandardError.ReadToEndAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct));
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
if (info.State != CliTaskState.TimedOut)
|
||||||
|
info.State = CliTaskState.Canceled;
|
||||||
|
|
||||||
|
if (!process.HasExited)
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
incommingTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
|
||||||
|
string allOutput = (stdoutTask.Result ?? "") + (stderrTask.Result ?? "");
|
||||||
|
log?.Debug(allOutput);
|
||||||
|
|
||||||
|
string json = ExtractJson(allOutput);
|
||||||
|
T result;
|
||||||
|
if (TryJsonStringDeserialize(json, out result))
|
||||||
|
{
|
||||||
|
info.State = CliTaskState.Completed;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.State = CliTaskState.Completed;
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
info.State = CliTaskState.Faulted;
|
||||||
|
log?.Error("RunAndCaptureJsonAsync failed", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
info.Process = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryJsonStringDeserialize<T>(string json, out T value) where T : new()
|
||||||
{
|
{
|
||||||
if (json != null)
|
if (json != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
runAndCaptureJsonAsync = JsonConvert.DeserializeObject<T>(json);
|
value = JsonConvert.DeserializeObject<T>(json);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
log.Debug(ex.Message);
|
log?.Debug(ex.Message);
|
||||||
runAndCaptureJsonAsync = TryConvert<T>(json);
|
value = TryConvert<T>(json);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runAndCaptureJsonAsync = default;
|
value = default(T);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static T TryConvert<T>(string json) where T : new()
|
private static T TryConvert<T>(string json) where T : new()
|
||||||
{
|
{
|
||||||
|
T obj = new T();
|
||||||
T obj = new T();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
JObject jObject = JObject.Parse(json);
|
||||||
|
|
||||||
|
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||||
{
|
{
|
||||||
JObject jObject = JObject.Parse(json);
|
if (!prop.CanWrite)
|
||||||
|
continue;
|
||||||
|
|
||||||
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
JToken token;
|
||||||
|
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
|
||||||
{
|
{
|
||||||
if (!prop.CanWrite) continue;
|
try
|
||||||
|
{
|
||||||
JToken token;
|
object value = token.ToObject(prop.PropertyType);
|
||||||
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
|
prop.SetValue(obj, value);
|
||||||
|
}
|
||||||
|
catch
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
object value = token.ToObject(prop.PropertyType);
|
|
||||||
prop.SetValue(obj, value);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// leave default if conversion fails
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// else → keep default value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
catch (Exception ex)
|
||||||
Console.WriteLine($"TryConvert failed: {ex.Message}");
|
{
|
||||||
}
|
Console.WriteLine($"TryConvert failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ExtractJson(string text)
|
public string ExtractJson(string text)
|
||||||
@ -241,6 +446,20 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void AddTaskForTest(Task task, string name = "TestTask", CancellationTokenSource cts = null)
|
||||||
|
{
|
||||||
|
taskPool.Add(new CliTaskInfo
|
||||||
|
{
|
||||||
|
Task = task,
|
||||||
|
Cts = cts,
|
||||||
|
Name = name,
|
||||||
|
State = task.IsCompleted
|
||||||
|
? (task.IsCanceled ? CliTaskState.Canceled :
|
||||||
|
task.IsFaulted ? CliTaskState.Faulted :
|
||||||
|
CliTaskState.Completed)
|
||||||
|
: CliTaskState.Running
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
25
TBF/Rig/RegisterReaders/PoseidonCmdStartStop/CliTaskInfo.cs
Normal file
25
TBF/Rig/RegisterReaders/PoseidonCmdStartStop/CliTaskInfo.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||||
|
{
|
||||||
|
public class CliTaskInfo
|
||||||
|
{
|
||||||
|
public Task Task { get; set; }
|
||||||
|
public CancellationTokenSource Cts { get; set; }
|
||||||
|
public Process Process { get; set; }
|
||||||
|
public CliTaskState State { get; set; } = CliTaskState.Running;
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public bool UseResult
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return State == CliTaskState.Completed
|
||||||
|
&& Task != null
|
||||||
|
&& Task.Status == TaskStatus.RanToCompletion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
TBF/Rig/RegisterReaders/PoseidonCmdStartStop/CliTaskState.cs
Normal file
13
TBF/Rig/RegisterReaders/PoseidonCmdStartStop/CliTaskState.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||||
|
{
|
||||||
|
public enum CliTaskState
|
||||||
|
{
|
||||||
|
Running,
|
||||||
|
Completed,
|
||||||
|
TimedOut,
|
||||||
|
Canceled,
|
||||||
|
Faulted
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -336,10 +336,12 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
|||||||
/// for debug purposes what time will consume answer
|
/// for debug purposes what time will consume answer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private long startTimeInMilis = -1, fullTimeInMilis = -1;
|
private long startTimeInMilis = -1, fullTimeInMilis = -1;
|
||||||
|
/// 30 seconds
|
||||||
private static long SafetyTimeOut = 30 * 1000;
|
private static long SafetyTimeOut = 30 * 1000;
|
||||||
private long incommingTime = -1;
|
private long incommingTime = -1;
|
||||||
|
private bool _lastOpTimedOut;
|
||||||
|
|
||||||
|
|
||||||
/// 30 seconds
|
|
||||||
|
|
||||||
public long DeltaTime { get{return fullTimeInMilis;}}
|
public long DeltaTime { get{return fullTimeInMilis;}}
|
||||||
public long IncommingTime { get{return incommingTime;}}
|
public long IncommingTime { get{return incommingTime;}}
|
||||||
@ -356,108 +358,146 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
|||||||
|
|
||||||
if (_currentOp == CurrentPoseidonOp.SendStartDataStream)
|
if (_currentOp == CurrentPoseidonOp.SendStartDataStream)
|
||||||
{
|
{
|
||||||
|
CliRunner.Clear();
|
||||||
|
_lastOpTimedOut = false;
|
||||||
CliRunner.AddSendAsync(serialPort, SerialPortData.EMeterArg.AllParams);
|
CliRunner.AddSendAsync(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||||
return Event.Busy;
|
_currentOp = CurrentPoseidonOp.SendStartDataStream_Runing;
|
||||||
|
return Event.Busy;
|
||||||
}
|
}
|
||||||
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Runing)
|
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Runing)
|
||||||
{
|
{
|
||||||
if (CliRunner.AreTasksDone()
|
if (CliRunner.AreTasksDone())
|
||||||
|| CliRunner.TimeOutReceived(SafetyTimeOut))
|
|
||||||
{
|
{
|
||||||
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
|
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
|
||||||
}
|
}
|
||||||
return Event.Busy;
|
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||||
|
{
|
||||||
|
_lastOpTimedOut = true;
|
||||||
|
CliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Event.Busy;
|
||||||
}
|
}
|
||||||
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Done)
|
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Done)
|
||||||
{
|
{
|
||||||
var firstTask = CliRunner.TaskPool.FindLast(t => t is Task<string>);
|
var firstTaskInfo = CliRunner.TaskPool
|
||||||
|
.FindLast(t => t.Task is Task<string> && t.UseResult);
|
||||||
|
|
||||||
|
if (firstTaskInfo != null)
|
||||||
if (firstTask != null && firstTask is Task<string>)
|
|
||||||
{
|
{
|
||||||
string data = null;
|
var task = (Task<string>)firstTaskInfo.Task;
|
||||||
data = (firstTask as Task<string>).Result;
|
string data = task.Result;
|
||||||
|
|
||||||
if (data != null && TryGetDeviceId(data, out wmSerialNr))
|
if (!string.IsNullOrEmpty(data))
|
||||||
{
|
{
|
||||||
|
TryGetDeviceId(data, out wmSerialNr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (_lastOpTimedOut)
|
||||||
|
{
|
||||||
|
log.Warn(
|
||||||
|
$"PoseidonReader {Name}: SendStartDataStream timed out, no completed result will be used.");
|
||||||
|
}
|
||||||
|
|
||||||
_currentOp = CurrentPoseidonOp.Done;
|
CliRunner.Clear();
|
||||||
|
_currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
|
||||||
return Event.Done;
|
return Event.Done;
|
||||||
}
|
}
|
||||||
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
|
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
|
||||||
|| _currentOp == CurrentPoseidonOp.ReadDataStream_End)
|
|| _currentOp == CurrentPoseidonOp.ReadDataStream_End)
|
||||||
{
|
{
|
||||||
startTimeInMilis = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
|
startTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
incommingTime = -1;
|
incommingTime = -1;
|
||||||
_isReadingStart = (_currentOp == CurrentPoseidonOp.ReadDataStream_Start);
|
_isReadingStart = (_currentOp == CurrentPoseidonOp.ReadDataStream_Start);
|
||||||
CliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
|
||||||
|
CliRunner.Clear();
|
||||||
|
_lastOpTimedOut = false;
|
||||||
|
CliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
|
||||||
|
SerialPortData.EMeterArg.AllParams);
|
||||||
_currentOp = CurrentPoseidonOp.ReadDatastream_Running;
|
_currentOp = CurrentPoseidonOp.ReadDatastream_Running;
|
||||||
return Event.Busy;
|
return Event.Busy;
|
||||||
}else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
|
}
|
||||||
|
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
|
||||||
{
|
{
|
||||||
if (CliRunner.AreTasksDone()
|
if (CliRunner.AreTasksDone())
|
||||||
|| CliRunner.TimeOutReceived(SafetyTimeOut))
|
|
||||||
{
|
{
|
||||||
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
|
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
|
||||||
//set time stamp - end of reading
|
|
||||||
incommingTime = CliRunner.IncommingTime;
|
incommingTime = CliRunner.IncommingTime;
|
||||||
}
|
}
|
||||||
|
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||||
|
{
|
||||||
|
_lastOpTimedOut = true;
|
||||||
|
CliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
|
||||||
|
incommingTime = CliRunner.IncommingTime;
|
||||||
|
}
|
||||||
|
|
||||||
return Event.Busy;
|
return Event.Busy;
|
||||||
}
|
}
|
||||||
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Done)
|
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Done)
|
||||||
{
|
{
|
||||||
fullTimeInMilis = (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - startTimeInMilis;
|
fullTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTimeInMilis;
|
||||||
log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis} ");
|
log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis}");
|
||||||
JsonDataFromPoseidon data = null;
|
|
||||||
var first = CliRunner.TaskPool.FindLast(t => t is Task<JsonDataFromPoseidon>);
|
|
||||||
if (first != null && first is Task<JsonDataFromPoseidon>)
|
|
||||||
{
|
|
||||||
data = (first as Task<JsonDataFromPoseidon>).Result;
|
|
||||||
|
|
||||||
//GET serial number
|
JsonDataFromPoseidon data = null;
|
||||||
|
|
||||||
|
var firstTaskInfo = CliRunner.TaskPool
|
||||||
|
.FindLast(t => t.Task is Task<JsonDataFromPoseidon> && t.UseResult);
|
||||||
|
|
||||||
|
if (firstTaskInfo != null)
|
||||||
|
{
|
||||||
|
var task = (Task<JsonDataFromPoseidon>)firstTaskInfo.Task;
|
||||||
|
data = task.Result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_lastOpTimedOut)
|
||||||
|
{
|
||||||
|
log.Warn($"PoseidonReader {Name}: ReadDatastream timed out, no completed result available.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.Warn("No completed JsonDataFromPoseidon task available.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
if (string.IsNullOrEmpty(wmSerialNr))
|
if (string.IsNullOrEmpty(wmSerialNr))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
wmSerialNr = data?.DeviceId ?? wmSerialNr;
|
wmSerialNr = data.DeviceId ?? wmSerialNr;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
log.Error("Serial Nr - parse error!");
|
log.Error("Serial Nr - parse error!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//GET volume
|
double volume;
|
||||||
if (data != null && Double.TryParse(data.Reading, out double Volume))
|
if (Double.TryParse(data.Reading, out volume))
|
||||||
{
|
{
|
||||||
double VolumeLi = Units.ConvertFrom(Unit.USgal, Volume);
|
double volumeLi = Units.ConvertFrom(Unit.USgal, volume);
|
||||||
//double VolumeM3 = Units.ConvertTo(Unit.m3, VolumeLi);
|
|
||||||
if (_isReadingStart)
|
|
||||||
{
|
|
||||||
//volume
|
|
||||||
beginWMState = VolumeLi;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//volume
|
|
||||||
endWMState = VolumeLi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (_isReadingStart)
|
||||||
|
beginWMState = volumeLi;
|
||||||
|
else
|
||||||
|
endWMState = volumeLi;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Finish reading and loop
|
CliRunner.Clear();
|
||||||
_currentOp = CurrentPoseidonOp.Done;
|
_currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
|
||||||
return Event.Done;
|
return Event.Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Event.None;
|
||||||
|
|
||||||
return Event.None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Stop this operation</summary>
|
/// <summary>Stop this operation</summary>
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1396,18 +1396,18 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
|||||||
chanelXMeterRslt?.CopyContentFrom(meterRslt);
|
chanelXMeterRslt?.CopyContentFrom(meterRslt);
|
||||||
if (iCH == 0)
|
if (iCH == 0)
|
||||||
{
|
{
|
||||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartCh1,
|
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh1,
|
||||||
genesisSmart.TimestampSecEndCh1, genesisSmart.VolumeLtrStartCh1, genesisSmart.VolumeLtrEndCh1);
|
genesisSmart.TimestampSecEndRawCh1, genesisSmart.VolumeLtrStartRawCh1, genesisSmart.VolumeLtrEndRawCh1);
|
||||||
}
|
}
|
||||||
else if (iCH == 1)
|
else if (iCH == 1)
|
||||||
{
|
{
|
||||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartCh2,
|
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh2,
|
||||||
genesisSmart.TimestampSecEndCh2, genesisSmart.VolumeLtrStartCh2, genesisSmart.VolumeLtrEndCh2);
|
genesisSmart.TimestampSecEndRawCh2, genesisSmart.VolumeLtrStartRawCh2, genesisSmart.VolumeLtrEndRawCh2);
|
||||||
}
|
}
|
||||||
else if (iCH == 2)
|
else if (iCH == 2)
|
||||||
{
|
{
|
||||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartCh3,
|
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh3,
|
||||||
genesisSmart.TimestampSecEndCh3, genesisSmart.VolumeLtrStartCh3, genesisSmart.VolumeLtrEndCh3);
|
genesisSmart.TimestampSecEndRawCh3, genesisSmart.VolumeLtrStartRawCh3, genesisSmart.VolumeLtrEndRawCh3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!genesisSmart.EnableShowChanels)
|
if (!genesisSmart.EnableShowChanels)
|
||||||
|
|||||||
@ -1484,6 +1484,8 @@
|
|||||||
<DependentUpon>RRCfgCtrl.cs</DependentUpon>
|
<DependentUpon>RRCfgCtrl.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunner.cs" />
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunner.cs" />
|
||||||
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliTaskInfo.cs" />
|
||||||
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliTaskState.cs" />
|
||||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\Factory.cs" />
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\Factory.cs" />
|
||||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\JsonDataFromPoseidon.cs" />
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\JsonDataFromPoseidon.cs" />
|
||||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonCfg.cs" />
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonCfg.cs" />
|
||||||
|
|||||||
@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using TBF.Rig.RegisterReaders.GenesisRegReader;
|
||||||
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
||||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication;
|
using TBF.Rig.RegisterReaders.GenesisRegReader.communication;
|
||||||
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
||||||
@ -14,10 +15,16 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
public class GenesisReaderTests
|
public class GenesisReaderTests
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private static GenesisCfg CreateCfg()
|
||||||
|
{
|
||||||
|
Factory factory = new Factory();
|
||||||
|
return new GenesisCfg(factory);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Debug_ProcessOptoLine_FormatVariants()
|
public void Debug_ProcessOptoLine_FormatVariants()
|
||||||
{
|
{
|
||||||
var reader = new GenesisSmartReader();
|
var reader = new GenesisSmartReader(CreateCfg());
|
||||||
InitializeReaderForTest(reader);
|
InitializeReaderForTest(reader);
|
||||||
|
|
||||||
string[] variants =
|
string[] variants =
|
||||||
@ -28,26 +35,28 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
"@he \t1\t0\t0A1F59C4\t00017A43\t00115C45\t72E1596B\t00000400\t00001998\t7D91B652\t7D4F37E6\t000191E6\t0C\t062E4A9C\t5331"
|
"@he \t1\t0\t0A1F59C4\t00017A43\t00115C45\t72E1596B\t00000400\t00001998\t7D91B652\t7D4F37E6\t000191E6\t0C\t062E4A9C\t5331"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int[] counts = new int[variants.Length];
|
||||||
|
|
||||||
for (int i = 0; i < variants.Length; i++)
|
for (int i = 0; i < variants.Length; i++)
|
||||||
{
|
{
|
||||||
bool blockCompleted = false;
|
bool blockCompleted = false;
|
||||||
reader.ProcessOptoLine(variants[i], DataStreamState.ProcessAndSave, out blockCompleted);
|
reader.ProcessOptoLine(variants[i], DataStreamState.ProcessAndSave, out blockCompleted);
|
||||||
|
|
||||||
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
counts[i] = GetPrivateField<int>(reader, "optoDataCount");
|
||||||
|
|
||||||
Console.WriteLine($"Variant {i}: {variants[i]}");
|
Console.WriteLine($"Variant {i}: {variants[i]}");
|
||||||
Console.WriteLine($" blockCompleted={blockCompleted}");
|
Console.WriteLine($" blockCompleted={blockCompleted}");
|
||||||
Console.WriteLine($" optoDataCount={optoDataCount}");
|
Console.WriteLine($" optoDataCount={counts[i]}");
|
||||||
Console.WriteLine("--------------------------------");
|
Console.WriteLine("--------------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Fail("Inspect which variant, if any, is accepted.");
|
Assert.IsTrue(counts.Any(c => c > 0), "At least one telegram format variant should be accepted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void RealInput_ShouldCalculate_StartEndVolumes_AndTimes()
|
public void RealInput_ShouldParseCalibrationTelegrams()
|
||||||
{
|
{
|
||||||
var reader = new GenesisSmartReader();
|
var reader = new GenesisSmartReader(CreateCfg());
|
||||||
InitializeReaderForTest(reader);
|
InitializeReaderForTest(reader);
|
||||||
|
|
||||||
string[] realInputLines = LoadRealInputLines();
|
string[] realInputLines = LoadRealInputLines();
|
||||||
@ -110,87 +119,33 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
Console.WriteLine($"Final firstValidIx = {firstValidIx}");
|
Console.WriteLine($"Final firstValidIx = {firstValidIx}");
|
||||||
Console.WriteLine($"Final lastValidIx = {lastValidIx}");
|
Console.WriteLine($"Final lastValidIx = {lastValidIx}");
|
||||||
|
|
||||||
if (processedCount <= 0)
|
Assert.AreEqual(3, processedCount, "Expected all 3 calibration telegrams to be parsed.");
|
||||||
{
|
Assert.AreEqual(0, firstValidIx);
|
||||||
Assert.Fail(
|
Assert.AreEqual(2, lastValidIx);
|
||||||
"No valid calibration telegrams were parsed.\n" +
|
|
||||||
"Check the following:\n" +
|
|
||||||
"1. exact telegram prefix (for example @h vs @he)\n" +
|
|
||||||
"2. exact separators (spaces vs tabs)\n" +
|
|
||||||
"3. exact CRC / checksum\n" +
|
|
||||||
"4. whether ProcessOptoLine expects a complete multi-line block format\n" +
|
|
||||||
"See test output for per-line diagnostics.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
||||||
|
Assert.IsNotNull(optoData);
|
||||||
|
|
||||||
|
Assert.AreEqual(0, optoData[0].IChannel());
|
||||||
|
Assert.AreEqual(1, optoData[1].IChannel());
|
||||||
|
Assert.AreEqual(2, optoData[2].IChannel());
|
||||||
|
|
||||||
|
// With only one telegram per channel, full start/end reconstruction is not expected yet.
|
||||||
reader.TestStartTelegramIx = firstValidIx;
|
reader.TestStartTelegramIx = firstValidIx;
|
||||||
reader.TestEndTelegramIx = lastValidIx;
|
reader.TestEndTelegramIx = lastValidIx;
|
||||||
|
|
||||||
Console.WriteLine("=== BEFORE POST PROCESSING ===");
|
InvokePrivate(reader, "AddTestStartEndMarksToData", new object[] { 0, 0 });
|
||||||
Console.WriteLine($"TestStartTelegramIx = {reader.TestStartTelegramIx}");
|
InvokePrivate(reader, "DataStreamPostProcessing");
|
||||||
Console.WriteLine($"TestEndTelegramIx = {reader.TestEndTelegramIx}");
|
InvokePrivate(reader, "PrepareCalculatedChannelData");
|
||||||
|
|
||||||
object[] markArgs = { 0, 0 };
|
Assert.IsTrue(reader.NoSamples,
|
||||||
|
"With only one telegram per channel, recalculated start/end samples should still be unavailable.");
|
||||||
try
|
|
||||||
{
|
|
||||||
InvokePrivate(reader, "AddTestStartEndMarksToData", markArgs);
|
|
||||||
Console.WriteLine("AddTestStartEndMarksToData OK");
|
|
||||||
|
|
||||||
InvokePrivate(reader, "DataStreamPostProcessing");
|
|
||||||
Console.WriteLine("DataStreamPostProcessing OK");
|
|
||||||
|
|
||||||
InvokePrivate(reader, "PrepareCalculatedChannelData");
|
|
||||||
Console.WriteLine("PrepareCalculatedChannelData OK");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Assert.Fail(
|
|
||||||
"Post-processing failed.\n" +
|
|
||||||
$"Exception: {ex.GetType().Name}: {ex.Message}\n" +
|
|
||||||
$"StackTrace:\n{ex.StackTrace}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("=== CALCULATED VALUES ===");
|
|
||||||
Console.WriteLine($"NoSamples = {reader.NoSamples}");
|
|
||||||
Console.WriteLine($"VolumeLtrStart = {reader.VolumeLtrStart}");
|
|
||||||
Console.WriteLine($"VolumeLtrEnd = {reader.VolumeLtrEnd}");
|
|
||||||
Console.WriteLine($"TimestampSecStart = {reader.TimestampSecStart}");
|
|
||||||
Console.WriteLine($"TimestampSecEnd = {reader.TimestampSecEnd}");
|
|
||||||
|
|
||||||
Assert.IsFalse(double.IsNaN(reader.VolumeLtrStart), "VolumeLtrStart is NaN");
|
|
||||||
Assert.IsFalse(double.IsNaN(reader.VolumeLtrEnd), "VolumeLtrEnd is NaN");
|
|
||||||
Assert.IsFalse(double.IsNaN(reader.TimestampSecStart), "TimestampSecStart is NaN");
|
|
||||||
Assert.IsFalse(double.IsNaN(reader.TimestampSecEnd), "TimestampSecEnd is NaN");
|
|
||||||
|
|
||||||
Assert.IsTrue(reader.TimestampSecEnd >= reader.TimestampSecStart,
|
|
||||||
$"Timestamp ordering invalid: start={reader.TimestampSecStart}, end={reader.TimestampSecEnd}");
|
|
||||||
|
|
||||||
Assert.IsTrue(reader.VolumeLtrEnd >= reader.VolumeLtrStart,
|
|
||||||
$"Volume ordering invalid: start={reader.VolumeLtrStart}, end={reader.VolumeLtrEnd}");
|
|
||||||
|
|
||||||
// When parser input is finally correct, replace these expected values:
|
|
||||||
const double expectedVolumeStart = 0.0;
|
|
||||||
const double expectedVolumeEnd = 0.0;
|
|
||||||
const double expectedTimeStart = 0.0;
|
|
||||||
const double expectedTimeEnd = 0.0;
|
|
||||||
const double tolerance = 0.000001;
|
|
||||||
|
|
||||||
Console.WriteLine("=== EXPECTED VS ACTUAL ===");
|
|
||||||
Console.WriteLine($"expectedVolumeStart = {expectedVolumeStart}, actual = {reader.VolumeLtrStart}");
|
|
||||||
Console.WriteLine($"expectedVolumeEnd = {expectedVolumeEnd}, actual = {reader.VolumeLtrEnd}");
|
|
||||||
Console.WriteLine($"expectedTimeStart = {expectedTimeStart}, actual = {reader.TimestampSecStart}");
|
|
||||||
Console.WriteLine($"expectedTimeEnd = {expectedTimeEnd}, actual = {reader.TimestampSecEnd}");
|
|
||||||
|
|
||||||
Assert.AreEqual(expectedVolumeStart, reader.VolumeLtrStart, tolerance, "VolumeLtrStart mismatch");
|
|
||||||
Assert.AreEqual(expectedVolumeEnd, reader.VolumeLtrEnd, tolerance, "VolumeLtrEnd mismatch");
|
|
||||||
Assert.AreEqual(expectedTimeStart, reader.TimestampSecStart, tolerance, "TimestampSecStart mismatch");
|
|
||||||
Assert.AreEqual(expectedTimeEnd, reader.TimestampSecEnd, tolerance, "TimestampSecEnd mismatch");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void RealInput_ShouldSupport_RolloverNormalization()
|
public void RealInput_ShouldParseRolloverInputLines()
|
||||||
{
|
{
|
||||||
var reader = new GenesisSmartReader();
|
var reader = new GenesisSmartReader(CreateCfg());
|
||||||
InitializeReaderForTest(reader);
|
InitializeReaderForTest(reader);
|
||||||
|
|
||||||
string[] realInputLines = LoadRealInputLinesWithRollover();
|
string[] realInputLines = LoadRealInputLinesWithRollover();
|
||||||
@ -211,29 +166,25 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
|
|
||||||
int finalOptoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
int finalOptoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||||
|
|
||||||
Assert.IsTrue(
|
Assert.AreEqual(3, finalOptoDataCount,
|
||||||
finalOptoDataCount > 1,
|
"Expected all provided rollover input lines to be parsed as telegrams.");
|
||||||
"Need at least 2 valid telegrams. Check exact telegram format / CRC in LoadRealInputLinesWithRollover().");
|
|
||||||
|
|
||||||
reader.TestStartTelegramIx = 0;
|
|
||||||
reader.TestEndTelegramIx = finalOptoDataCount - 1;
|
|
||||||
|
|
||||||
object[] markArgs = { 0, 0 };
|
|
||||||
InvokePrivate(reader, "AddTestStartEndMarksToData", markArgs);
|
|
||||||
InvokePrivate(reader, "DataStreamPostProcessing");
|
|
||||||
InvokePrivate(reader, "PrepareCalculatedChannelData");
|
|
||||||
|
|
||||||
Console.WriteLine($"VolumeLtrStart={reader.VolumeLtrStart}");
|
|
||||||
Console.WriteLine($"VolumeLtrEnd={reader.VolumeLtrEnd}");
|
|
||||||
Console.WriteLine($"TimestampSecStart={reader.TimestampSecStart}");
|
|
||||||
Console.WriteLine($"TimestampSecEnd={reader.TimestampSecEnd}");
|
|
||||||
|
|
||||||
Assert.IsTrue(reader.TimestampSecEnd >= reader.TimestampSecStart,
|
|
||||||
"Normalized end time should be >= start time");
|
|
||||||
Assert.IsTrue(reader.VolumeLtrEnd >= reader.VolumeLtrStart,
|
|
||||||
"Normalized end volume should be >= start volume");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void PrepareCalculatedChannelDataSimulationForTest_ShouldProduceUsableSamples()
|
||||||
|
{
|
||||||
|
var reader = new GenesisSmartReader(CreateCfg());
|
||||||
|
InitializeReaderForTest(reader);
|
||||||
|
|
||||||
|
reader.PrepareCalculatedChannelDataSimulationForTest();
|
||||||
|
|
||||||
|
Assert.IsFalse(reader.NoSamples);
|
||||||
|
Assert.AreEqual(105.0, reader.VolumeLtrStartAverage, 0.0001);
|
||||||
|
Assert.AreEqual(157.0, reader.VolumeLtrEndAwerage, 0.0001);
|
||||||
|
Assert.AreEqual(10.0, reader.TimestampSecStart, 0.0001);
|
||||||
|
Assert.AreEqual(20.0, reader.TimestampSecEnd, 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
private static void InitializeReaderForTest(GenesisSmartReader reader)
|
private static void InitializeReaderForTest(GenesisSmartReader reader)
|
||||||
{
|
{
|
||||||
const int channelCount = 3;
|
const int channelCount = 3;
|
||||||
@ -264,6 +215,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
SetPrivateField(reader, "partOfTelegram", string.Empty);
|
SetPrivateField(reader, "partOfTelegram", string.Empty);
|
||||||
SetPrivateField(reader, "startDataProcessing", true);
|
SetPrivateField(reader, "startDataProcessing", true);
|
||||||
|
|
||||||
|
reader.StopQueueData = false;
|
||||||
|
|
||||||
reader.TestStartTelegramIx = 0;
|
reader.TestStartTelegramIx = 0;
|
||||||
reader.TestEndTelegramIx = 0;
|
reader.TestEndTelegramIx = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,13 +133,13 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
InvokePrepareCalculatedChannelData(reader);
|
InvokePrepareCalculatedChannelData(reader);
|
||||||
|
|
||||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
|
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
|
||||||
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
|
Assert.AreEqual(219.666666666667, reader.VolumeLtrEnd, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
||||||
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
|
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartRaw, 1e-9);
|
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartAverage, 1e-9);
|
||||||
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
|
Assert.AreEqual(219.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -162,11 +162,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// channel 1 loses its last two valid records
|
|
||||||
// valid ch1 becomes: 200..207 with timestamps 1..8
|
|
||||||
// longest channel deltaTime is 9 (channels 0 and 2)
|
|
||||||
// ch1 deltaVolume = 7, deltaTime = 7 => recalculated deltaVolume = 7 * 9 / 7 = 9
|
|
||||||
// recalculated end ch1 = 200 + 9 = 209
|
|
||||||
optoData[25].Flags = OptoTelegramFlags.InvalidTelegram;
|
optoData[25].Flags = OptoTelegramFlags.InvalidTelegram;
|
||||||
optoData[28].Flags = OptoTelegramFlags.InvalidTelegram;
|
optoData[28].Flags = OptoTelegramFlags.InvalidTelegram;
|
||||||
|
|
||||||
@ -177,14 +172,14 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
InvokePrepareCalculatedChannelData(reader);
|
InvokePrepareCalculatedChannelData(reader);
|
||||||
|
|
||||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
|
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
|
||||||
Assert.AreEqual(208.33333333333334, reader.VolumeLtrEnd, 1e-9);
|
Assert.AreEqual(209.666666666667, reader.VolumeLtrEnd, 1e-9);
|
||||||
Assert.AreEqual(208.33333333333334, reader.VolumeLtrEndRaw, 1e-9);
|
Assert.AreEqual(209.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
|
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
|
||||||
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
|
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
|
||||||
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
|
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartRaw, 1e-9);
|
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartAverage, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(110.0, reader.VolumeLtrEndCh1, 1e-9);
|
Assert.AreEqual(110.0, reader.VolumeLtrEndCh1, 1e-9);
|
||||||
Assert.AreEqual(210.0, reader.VolumeLtrEndCh2, 1e-9);
|
Assert.AreEqual(210.0, reader.VolumeLtrEndCh2, 1e-9);
|
||||||
@ -272,24 +267,13 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
Assert.AreEqual(20.0, reader.TimestampSecEndCh2, 1e-9);
|
Assert.AreEqual(20.0, reader.TimestampSecEndCh2, 1e-9);
|
||||||
Assert.AreEqual(20.0, reader.TimestampSecEndCh3, 1e-9);
|
Assert.AreEqual(20.0, reader.TimestampSecEndCh3, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
|
Assert.AreEqual(219.666666666667, reader.VolumeLtrEnd, 1e-9);
|
||||||
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
|
|
||||||
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
|
|
||||||
|
|
||||||
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
|
|
||||||
|
|
||||||
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
||||||
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
|
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartRaw, 1e-9);
|
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartAverage, 1e-9);
|
||||||
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
|
Assert.AreEqual(219.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(120.0, reader.VolumeLtrEndCh1, 1e-9);
|
|
||||||
Assert.AreEqual(220.0, reader.VolumeLtrEndCh2, 1e-9);
|
|
||||||
Assert.AreEqual(319.0, reader.VolumeLtrEndCh3, 1e-9);
|
|
||||||
|
|
||||||
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
|
|
||||||
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -313,7 +297,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
InvokePrepareCalculatedChannelData(reader);
|
InvokePrepareCalculatedChannelData(reader);
|
||||||
|
|
||||||
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
|
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
|
||||||
Assert.AreEqual(19.0, reader.VolumeLtrEnd, 1e-9);
|
Assert.AreEqual(19.5, reader.VolumeLtrEnd, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(11.0, reader.VolumeLtrStartCh1, 1e-9);
|
Assert.AreEqual(11.0, reader.VolumeLtrStartCh1, 1e-9);
|
||||||
Assert.AreEqual(20.0, reader.VolumeLtrStartCh2, 1e-9);
|
Assert.AreEqual(20.0, reader.VolumeLtrStartCh2, 1e-9);
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using TBF.Rig.Generic;
|
||||||
using TBF.Rig.RegisterReaders.GenesisRegReader;
|
using TBF.Rig.RegisterReaders.GenesisRegReader;
|
||||||
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
||||||
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
||||||
@ -36,6 +38,15 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
return (T)field.GetValue(target);
|
return (T)field.GetValue(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void WaitForStopToFinish(GenesisSmartReader reader, int timeoutMs = 3000)
|
||||||
|
{
|
||||||
|
var stopTask = GetPrivateField<Task>(reader, "_backgroundStopTask");
|
||||||
|
if (stopTask != null)
|
||||||
|
{
|
||||||
|
stopTask.Wait(timeoutMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
|
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
|
||||||
{
|
{
|
||||||
return new OptoTelegramRaw
|
return new OptoTelegramRaw
|
||||||
@ -87,11 +98,10 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
Assert.AreEqual(DataStreamState.Flush, finalState, "Stop() should finish in Flush state.");
|
Assert.AreEqual(DataStreamState.Flush, finalState, "Stop() should finish in Flush state.");
|
||||||
Assert.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
|
Assert.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
|
||||||
|
|
||||||
// These values are populated only if DataStreamPostProcessing() ran.
|
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartAverage, 1e-9,
|
||||||
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRaw, 1e-9,
|
"Expected post-processing to prepare raw/recalculated start values.");
|
||||||
"Expected post-processing to prepare raw start values.");
|
Assert.AreEqual((15.0 + 25.0 + 36.0) / 3.0, reader.VolumeLtrEndAwerage, 1e-9,
|
||||||
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRaw, 1e-9,
|
"Expected post-processing to prepare recalculated end values.");
|
||||||
"Expected post-processing to prepare raw end values.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -111,6 +121,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
|
|
||||||
reader.Stop();
|
reader.Stop();
|
||||||
|
WaitForStopToFinish(reader);
|
||||||
|
|
||||||
var finalState = GetPrivateField<DataStreamState>(reader, "dataStreamState");
|
var finalState = GetPrivateField<DataStreamState>(reader, "dataStreamState");
|
||||||
var stopQueueData = GetPrivateField<bool>(reader, "_stopQueueData");
|
var stopQueueData = GetPrivateField<bool>(reader, "_stopQueueData");
|
||||||
@ -119,10 +130,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
Assert.IsTrue(stopQueueData, "Immediate stop path should stop queueing.");
|
Assert.IsTrue(stopQueueData, "Immediate stop path should stop queueing.");
|
||||||
Assert.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
|
Assert.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
|
||||||
|
|
||||||
// Should still be default because DataStreamPostProcessing() must NOT run.
|
Assert.AreEqual(0.0, reader.VolumeLtrStartAverage, 1e-9,
|
||||||
Assert.AreEqual(0.0, reader.VolumeLtrStartRaw, 1e-9,
|
|
||||||
"Post-processing should not run when previous state was ProcessAndSave.");
|
"Post-processing should not run when previous state was ProcessAndSave.");
|
||||||
Assert.AreEqual(0.0, reader.VolumeLtrEndRaw, 1e-9,
|
Assert.AreEqual(0.0, reader.VolumeLtrEndAwerage, 1e-9,
|
||||||
"Post-processing should not run when previous state was ProcessAndSave.");
|
"Post-processing should not run when previous state was ProcessAndSave.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -79,6 +79,15 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
reader.StopQueueData = false;
|
reader.StopQueueData = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void WaitForStopToFinish(GenesisSmartReader reader, int timeoutMs = 3000)
|
||||||
|
{
|
||||||
|
var stopTask = GetPrivateField<Task>(reader, "_backgroundStopTask");
|
||||||
|
if (stopTask != null)
|
||||||
|
{
|
||||||
|
stopTask.Wait(timeoutMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Start_ShouldOpenPort_AndEnableProcessing()
|
public void Start_ShouldOpenPort_AndEnableProcessing()
|
||||||
{
|
{
|
||||||
@ -129,9 +138,10 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
reader.TestEndTelegramIx = 1;
|
reader.TestEndTelegramIx = 1;
|
||||||
|
|
||||||
reader.Stop();
|
reader.Stop();
|
||||||
|
WaitForStopToFinish(reader);
|
||||||
|
|
||||||
Assert.IsFalse(fake.IsOpen);
|
Assert.IsFalse(fake.IsOpen);
|
||||||
Assert.AreEqual(2, fake.CloseCalls);
|
Assert.IsTrue(fake.CloseCalls >= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -165,6 +175,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
reader.Initialize();
|
reader.Initialize();
|
||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
|
|
||||||
|
InitializeThreeChannelState(reader);
|
||||||
|
EnableProcessingLoopForTests(reader);
|
||||||
|
|
||||||
bool reset;
|
bool reset;
|
||||||
|
|
||||||
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
||||||
@ -199,6 +212,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
|
|
||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
InitializeThreeChannelState(reader);
|
InitializeThreeChannelState(reader);
|
||||||
|
EnableProcessingLoopForTests(reader);
|
||||||
|
|
||||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
||||||
|
|
||||||
@ -254,6 +268,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
|
|
||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
InitializeThreeChannelState(reader);
|
InitializeThreeChannelState(reader);
|
||||||
|
EnableProcessingLoopForTests(reader);
|
||||||
|
|
||||||
var line =
|
var line =
|
||||||
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD";
|
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD";
|
||||||
@ -265,9 +280,15 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
[DataTestMethod]
|
[DataTestMethod]
|
||||||
[DataRow("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", 0)]
|
[DataRow(
|
||||||
[DataRow("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", 1)]
|
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
||||||
[DataRow("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", 2)]
|
0)]
|
||||||
|
[DataRow(
|
||||||
|
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
||||||
|
1)]
|
||||||
|
[DataRow(
|
||||||
|
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
||||||
|
2)]
|
||||||
public void ProcessOptoLine_ShouldInsertTelegramAndUpdateExpectedChannel(string line, int expectedChannelIndex)
|
public void ProcessOptoLine_ShouldInsertTelegramAndUpdateExpectedChannel(string line, int expectedChannelIndex)
|
||||||
{
|
{
|
||||||
var fake = new FakeSerialDriver();
|
var fake = new FakeSerialDriver();
|
||||||
@ -277,6 +298,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
reader.Initialize();
|
reader.Initialize();
|
||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
|
|
||||||
|
InitializeThreeChannelState(reader);
|
||||||
|
EnableProcessingLoopForTests(reader);
|
||||||
|
|
||||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
||||||
|
|
||||||
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
|
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
|
||||||
@ -284,27 +308,25 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
||||||
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||||
|
|
||||||
Assert.AreEqual(1, optoDataCount, "Exactly one telegram should be inserted.");
|
Assert.AreEqual(1, optoDataCount);
|
||||||
|
|
||||||
var inserted = optoData[0];
|
var inserted = optoData[0];
|
||||||
Assert.IsNotNull(inserted, "Inserted telegram should not be null.");
|
Assert.IsNotNull(inserted);
|
||||||
|
|
||||||
Assert.AreEqual(expectedChannelIndex, inserted.IChannel(), "Inserted telegram channel does not match expected channel.");
|
Assert.AreEqual(expectedChannelIndex, inserted.IChannel());
|
||||||
Assert.AreEqual(0, inserted.Counter, "First inserted telegram should have counter 0.");
|
Assert.AreEqual(0, inserted.Counter);
|
||||||
Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags, "Inserted telegram should be marked OK.");
|
Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags);
|
||||||
Assert.AreNotEqual(default(DateTime), inserted.DateTime, "Inserted telegram DateTime should be initialized.");
|
|
||||||
|
|
||||||
Assert.AreNotEqual(0.0, inserted.VolumeRaw, "Inserted telegram VolumeRaw should be set.");
|
Assert.AreNotEqual(0.0, inserted.VolumeRawExt);
|
||||||
Assert.AreNotEqual(0.0, inserted.VolumeRawExt, "Inserted telegram VolumeRawExt should be set.");
|
Assert.AreNotEqual(0.0, inserted.TimestampExt);
|
||||||
Assert.AreNotEqual(0.0, inserted.Timestamp, "Inserted telegram Timestamp should be set.");
|
|
||||||
Assert.AreNotEqual(0.0, inserted.TimestampExt, "Inserted telegram TimestampExt should be set.");
|
|
||||||
|
|
||||||
for (int i = 0; i < ChannelCount; i++)
|
for (int i = 0; i < ChannelCount; i++)
|
||||||
{
|
{
|
||||||
if (i == expectedChannelIndex)
|
if (i == expectedChannelIndex)
|
||||||
{
|
{
|
||||||
Assert.AreNotEqual(0.0, volumeRawExtLast[i], $"Expected channel {i} volume cache was not updated.");
|
Assert.AreNotEqual(0.0, volumeRawExtLast[i], $"Expected channel {i} volume cache was not updated.");
|
||||||
Assert.AreNotEqual(0.0, timestampExtLast[i], $"Expected channel {i} timestamp cache was not updated.");
|
Assert.AreNotEqual(0.0, timestampExtLast[i],
|
||||||
|
$"Expected channel {i} timestamp cache was not updated.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -313,11 +335,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9,
|
Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9);
|
||||||
"Inserted telegram VolumeRawExt should match updated channel cache.");
|
Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9,
|
|
||||||
"Inserted telegram TimestampExt should match updated channel cache.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -344,13 +363,16 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
InvokePrepareCalculatedChannelData(reader);
|
InvokePrepareCalculatedChannelData(reader);
|
||||||
|
|
||||||
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStart, 1e-9);
|
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStart, 1e-9);
|
||||||
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEnd, 1e-9);
|
Assert.AreEqual((15.0 + 25.0 + 36.0) / 3.0, reader.VolumeLtrEnd, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(100.0, reader.TimestampSecStart, 1e-9);
|
Assert.AreEqual(100.0, reader.TimestampSecStart, 1e-9);
|
||||||
Assert.AreEqual(105.0, reader.TimestampSecEnd, 1e-9);
|
Assert.AreEqual(105.0, reader.TimestampSecEnd, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRaw, 1e-9);
|
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartAverage, 1e-9);
|
||||||
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRaw, 1e-9);
|
Assert.AreEqual((15.0 + 25.0 + 36.0) / 3.0, reader.VolumeLtrEndAwerage, 1e-9);
|
||||||
|
|
||||||
|
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRawRaw, 1e-9);
|
||||||
|
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRawRaw, 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
|
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
|
||||||
@ -388,7 +410,21 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
InvokePrepareCalculatedChannelData(reader);
|
InvokePrepareCalculatedChannelData(reader);
|
||||||
|
|
||||||
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
|
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
|
||||||
Assert.AreEqual(19.0, reader.VolumeLtrEnd, 1e-9);
|
Assert.AreEqual(19.5, reader.VolumeLtrEnd, 1e-9);
|
||||||
|
|
||||||
|
Assert.AreEqual(15.5, reader.VolumeLtrStartAverage, 1e-9);
|
||||||
|
Assert.AreEqual(19.5, reader.VolumeLtrEndAwerage, 1e-9);
|
||||||
|
|
||||||
|
Assert.AreEqual(15.5, reader.VolumeLtrStartRawRaw, 1e-9);
|
||||||
|
Assert.AreEqual(19.0, reader.VolumeLtrEndRawRaw, 1e-9);
|
||||||
|
|
||||||
|
Assert.AreEqual(11.0, reader.VolumeLtrStartRawCh1, 1e-9);
|
||||||
|
Assert.AreEqual(20.0, reader.VolumeLtrStartRawCh2, 1e-9);
|
||||||
|
Assert.AreEqual(0.0, reader.VolumeLtrStartRawCh3, 1e-9);
|
||||||
|
|
||||||
|
Assert.AreEqual(14.0, reader.VolumeLtrEndRawCh1, 1e-9);
|
||||||
|
Assert.AreEqual(24.0, reader.VolumeLtrEndRawCh2, 1e-9);
|
||||||
|
Assert.AreEqual(0.0, reader.VolumeLtrEndRawCh3, 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -425,39 +461,37 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void ReadOptoData_FullBlock_ShouldDiscardBuffersAfterClosingF()
|
public void ProcessingLoop_FullBlock_ShouldDiscardBuffersAfterClosingF()
|
||||||
{
|
{
|
||||||
var fake = new FakeSerialDriver();
|
var fake = new FakeSerialDriver();
|
||||||
|
fake.Open();
|
||||||
fake.EnqueueLine("@f AA754B 4D0CEE78 5D89");
|
|
||||||
fake.EnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
|
|
||||||
fake.EnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
|
|
||||||
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
|
|
||||||
fake.EnqueueLine("@f AA7C01 4D0CFE76 B08F");
|
|
||||||
|
|
||||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||||
reader.Initialize();
|
reader.Initialize();
|
||||||
|
|
||||||
fake.Open();
|
|
||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
reader.ResetAfterBlockRepetitions = 1;
|
reader.ResetAfterBlockRepetitions = 1;
|
||||||
|
|
||||||
var readMethod = typeof(GenesisSmartReader).GetMethod(
|
InitializeThreeChannelState(reader);
|
||||||
"ReadOptoData",
|
EnableProcessingLoopForTests(reader);
|
||||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
|
||||||
null,
|
|
||||||
new[] { typeof(DataStreamState) },
|
|
||||||
null);
|
|
||||||
|
|
||||||
Assert.IsNotNull(readMethod);
|
reader.StartProcessingLoop();
|
||||||
|
try
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
{
|
||||||
readMethod.Invoke(reader, new object[] { DataStreamState.ProcessAndSave });
|
reader.TestEnqueueLine("@f AA754B 4D0CEE78 5D89");
|
||||||
}
|
reader.TestEnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
|
||||||
|
reader.TestEnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
|
||||||
|
reader.TestEnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
|
||||||
|
reader.TestEnqueueLine("@f AA7C01 4D0CFE76 B08F");
|
||||||
|
|
||||||
Assert.AreEqual(1, fake.DiscardInCalls, "Input buffer should be discarded once after completed block.");
|
Thread.Sleep(300);
|
||||||
Assert.AreEqual(1, fake.DiscardOutCalls, "Output buffer should be discarded once after completed block.");
|
|
||||||
|
Assert.AreEqual(1, fake.DiscardInCalls, "Input buffer should be discarded once after completed block.");
|
||||||
|
Assert.AreEqual(1, fake.DiscardOutCalls, "Output buffer should be discarded once after completed block.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
reader.StopProcessingLoop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -471,10 +505,13 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
reader.ResetAfterBlockRepetitions = 1;
|
reader.ResetAfterBlockRepetitions = 1;
|
||||||
|
|
||||||
|
InitializeThreeChannelState(reader);
|
||||||
|
EnableProcessingLoopForTests(reader);
|
||||||
|
|
||||||
bool reset;
|
bool reset;
|
||||||
|
|
||||||
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
||||||
Assert.IsFalse(reset, "Opening @f must not reset buffers.");
|
Assert.IsFalse(reset);
|
||||||
|
|
||||||
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
||||||
Assert.IsFalse(reset);
|
Assert.IsFalse(reset);
|
||||||
@ -529,24 +566,24 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
reader.ResetAfterBlockRepetitions = 3;
|
reader.ResetAfterBlockRepetitions = 3;
|
||||||
|
|
||||||
|
InitializeThreeChannelState(reader);
|
||||||
|
EnableProcessingLoopForTests(reader);
|
||||||
|
|
||||||
bool reset;
|
bool reset;
|
||||||
|
|
||||||
for (int repetition = 1; repetition <= 3; repetition++)
|
for (int repetition = 1; repetition <= 3; repetition++)
|
||||||
{
|
{
|
||||||
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
||||||
Assert.IsFalse(reset, "Reset must not happen on opening @f, repetition " + repetition);
|
Assert.IsFalse(reset);
|
||||||
|
|
||||||
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
||||||
DataStreamState.ProcessAndSave, out reset);
|
Assert.IsFalse(reset);
|
||||||
Assert.IsFalse(reset, "Reset must not happen after @h1, repetition " + repetition);
|
|
||||||
|
|
||||||
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
|
||||||
DataStreamState.ProcessAndSave, out reset);
|
Assert.IsFalse(reset);
|
||||||
Assert.IsFalse(reset, "Reset must not happen after @h2, repetition " + repetition);
|
|
||||||
|
|
||||||
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
|
||||||
DataStreamState.ProcessAndSave, out reset);
|
Assert.IsFalse(reset);
|
||||||
Assert.IsFalse(reset, "Reset must not happen after @h3, repetition " + repetition);
|
|
||||||
|
|
||||||
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
||||||
|
|
||||||
@ -568,24 +605,24 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
reader.optoSerialPort = fake;
|
reader.optoSerialPort = fake;
|
||||||
reader.ResetAfterBlockRepetitions = 5;
|
reader.ResetAfterBlockRepetitions = 5;
|
||||||
|
|
||||||
|
InitializeThreeChannelState(reader);
|
||||||
|
EnableProcessingLoopForTests(reader);
|
||||||
|
|
||||||
bool reset;
|
bool reset;
|
||||||
|
|
||||||
for (int repetition = 1; repetition <= 5; repetition++)
|
for (int repetition = 1; repetition <= 5; repetition++)
|
||||||
{
|
{
|
||||||
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
||||||
Assert.IsFalse(reset, "Reset must not happen on opening @f, repetition " + repetition);
|
Assert.IsFalse(reset);
|
||||||
|
|
||||||
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
||||||
DataStreamState.ProcessAndSave, out reset);
|
Assert.IsFalse(reset);
|
||||||
Assert.IsFalse(reset, "Reset must not happen after @h1, repetition " + repetition);
|
|
||||||
|
|
||||||
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
|
||||||
DataStreamState.ProcessAndSave, out reset);
|
Assert.IsFalse(reset);
|
||||||
Assert.IsFalse(reset, "Reset must not happen after @h2, repetition " + repetition);
|
|
||||||
|
|
||||||
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
|
||||||
DataStreamState.ProcessAndSave, out reset);
|
Assert.IsFalse(reset);
|
||||||
Assert.IsFalse(reset, "Reset must not happen after @h3, repetition " + repetition);
|
|
||||||
|
|
||||||
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
||||||
|
|
||||||
@ -742,18 +779,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
Assert.AreEqual(20.0, sut.TimestampSecEndCh2, 0.0001);
|
Assert.AreEqual(20.0, sut.TimestampSecEndCh2, 0.0001);
|
||||||
Assert.AreEqual(20.0, sut.TimestampSecEndCh3, 0.0001);
|
Assert.AreEqual(20.0, sut.TimestampSecEndCh3, 0.0001);
|
||||||
|
|
||||||
Assert.AreEqual(105.0, sut.VolumeLtrStartRaw, 0.0001);
|
Assert.AreEqual(105.0, sut.VolumeLtrStartAverage, 0.0001);
|
||||||
Assert.AreEqual(157.0, sut.VolumeLtrEndRaw, 0.0001);
|
Assert.AreEqual(157.0, sut.VolumeLtrEndAwerage, 0.0001);
|
||||||
|
|
||||||
Assert.AreEqual(10.0, sut.TimestampSecStart, 0.0001);
|
Assert.AreEqual(10.0, sut.TimestampSecStart, 0.0001);
|
||||||
Assert.AreEqual(20.0, sut.TimestampSecEnd, 0.0001);
|
Assert.AreEqual(20.0, sut.TimestampSecEnd, 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void METHOD()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10,22 +10,33 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public class GenesisSmartReader_Q3Test
|
public class GenesisSmartReader_Q3Test
|
||||||
{
|
{
|
||||||
|
private static readonly double[] ValidInitFactors = { 15625.0, 15625.0, 15625.0 };
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRawDataIsNull()
|
public void GetQ3Calibration_ShouldRemainInvalid_WhenRawDataIsNull()
|
||||||
{
|
{
|
||||||
var sut = new GenesisSmartReader();
|
var sut = new GenesisSmartReader();
|
||||||
InitializeReaderForQ3Test(sut);
|
InitializeReaderForQ3Test(sut);
|
||||||
|
|
||||||
SetPrivateField(sut, "_rawStartEndByChannel", null);
|
SetPrivateField(sut, "_rawStartEndByChannel", null);
|
||||||
|
|
||||||
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 15625.0);
|
var valid = new bool[3];
|
||||||
|
var calib = new double[3];
|
||||||
|
|
||||||
|
sut.GetQ3Calibration(
|
||||||
|
refVolume: 1000.0,
|
||||||
|
refTime: 120.0,
|
||||||
|
initCalibFactor: ValidInitFactors,
|
||||||
|
ref valid,
|
||||||
|
ref calib);
|
||||||
|
|
||||||
Assert.IsFalse(sut.Q3CalibValid);
|
Assert.IsFalse(sut.Q3CalibValid);
|
||||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
|
||||||
|
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenNoSamplesConditionIsTrue()
|
public void GetQ3Calibration_ShouldRemainInvalid_WhenNoSamplesIsTrue()
|
||||||
{
|
{
|
||||||
var sut = new GenesisSmartReader();
|
var sut = new GenesisSmartReader();
|
||||||
InitializeReaderForQ3Test(sut);
|
InitializeReaderForQ3Test(sut);
|
||||||
@ -34,69 +45,255 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
|
|
||||||
Assert.IsTrue(sut.NoSamples, "Expected NoSamples to be true for this test.");
|
Assert.IsTrue(sut.NoSamples, "Expected NoSamples to be true for this test.");
|
||||||
|
|
||||||
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 15625.0);
|
var valid = new bool[3];
|
||||||
|
var calib = new double[3];
|
||||||
|
|
||||||
|
sut.GetQ3Calibration(
|
||||||
|
refVolume: 1000.0,
|
||||||
|
refTime: 120.0,
|
||||||
|
initCalibFactor: ValidInitFactors,
|
||||||
|
ref valid,
|
||||||
|
ref calib);
|
||||||
|
|
||||||
Assert.IsFalse(sut.Q3CalibValid);
|
Assert.IsFalse(sut.Q3CalibValid);
|
||||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
|
||||||
|
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRefVolumeIsZero()
|
public void GetQ3Calibration_ShouldRemainInvalid_WhenRefVolumeIsZero()
|
||||||
{
|
{
|
||||||
var sut = new GenesisSmartReader();
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||||
|
Assert.IsFalse(sut.NoSamples);
|
||||||
|
|
||||||
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
|
var valid = new bool[3];
|
||||||
|
var calib = new double[3];
|
||||||
|
|
||||||
sut.GetQ3Calibration(refVolume: 0.0, refTime: 120.0, initCalibFactor: 15625.0);
|
sut.GetQ3Calibration(
|
||||||
|
refVolume: 0.0,
|
||||||
|
refTime: 120.0,
|
||||||
|
initCalibFactor: ValidInitFactors,
|
||||||
|
ref valid,
|
||||||
|
ref calib);
|
||||||
|
|
||||||
Assert.IsFalse(sut.Q3CalibValid);
|
Assert.IsFalse(sut.Q3CalibValid);
|
||||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
|
||||||
|
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRefTimeIsZero()
|
public void GetQ3Calibration_ShouldRemainInvalid_WhenRefTimeIsZero()
|
||||||
{
|
{
|
||||||
var sut = new GenesisSmartReader();
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||||
|
Assert.IsFalse(sut.NoSamples);
|
||||||
|
|
||||||
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
|
var valid = new bool[3];
|
||||||
|
var calib = new double[3];
|
||||||
|
|
||||||
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 0.0, initCalibFactor: 15625.0);
|
sut.GetQ3Calibration(
|
||||||
|
refVolume: 1000.0,
|
||||||
|
refTime: 0.0,
|
||||||
|
initCalibFactor: ValidInitFactors,
|
||||||
|
ref valid,
|
||||||
|
ref calib);
|
||||||
|
|
||||||
Assert.IsFalse(sut.Q3CalibValid);
|
Assert.IsFalse(sut.Q3CalibValid);
|
||||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
|
||||||
|
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenInitCalibFactorIsZero()
|
public void GetQ3Calibration_ShouldMarkInvalid_WhenInitFactorsAreZero()
|
||||||
{
|
{
|
||||||
var sut = new GenesisSmartReader();
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||||
|
Assert.IsFalse(sut.NoSamples);
|
||||||
|
|
||||||
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
|
var init = new[] { 0.0, 0.0, 0.0 };
|
||||||
|
var valid = new bool[3];
|
||||||
|
var calib = new double[3];
|
||||||
|
|
||||||
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 0.0);
|
sut.GetQ3Calibration(
|
||||||
|
refVolume: 1000.0,
|
||||||
|
refTime: 120.0,
|
||||||
|
initCalibFactor: init,
|
||||||
|
ref valid,
|
||||||
|
ref calib);
|
||||||
|
|
||||||
Assert.IsFalse(sut.Q3CalibValid);
|
Assert.IsFalse(sut.Q3CalibValid);
|
||||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
|
||||||
|
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetQ3Calibration_ShouldProduceNonNegativeResult_WithPreparedSimulationData()
|
public void CalculateQ3Calibration_ShouldPopulatePerChannelValues_WithPreparedSimulationData()
|
||||||
{
|
{
|
||||||
var sut = new GenesisSmartReader();
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||||
|
InvokePrivateByName(sut, "SetQ3Calibration", new object[] { ValidInitFactors });
|
||||||
|
|
||||||
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
|
Assert.IsFalse(sut.NoSamples);
|
||||||
|
|
||||||
sut.GetQ3Calibration(refVolume: 1.0, refTime: 120.0, initCalibFactor: 15625.0);
|
sut.CalculateQ3Calibration(200.0, 120.0);
|
||||||
|
|
||||||
Assert.IsTrue(sut.Q3CalibValue >= 0.0);
|
Assert.IsNotNull(sut.Q3CalibValue);
|
||||||
|
Assert.AreEqual(3, sut.Q3CalibValue.Length);
|
||||||
|
|
||||||
|
Assert.IsTrue(sut.Q3CalibValue.All(v => !Double.IsNaN(v)));
|
||||||
|
Assert.IsTrue(sut.Q3CalibValue.All(v => v >= 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void GetQ3Calibration_ShouldCalculateExpectedValues_ForKnownInput()
|
||||||
|
{
|
||||||
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
|
var raw = CreateKnownStartEndData(
|
||||||
|
// start volume / end volume / start time / end time
|
||||||
|
(100.0, 150.0, 10.0, 20.0), // dV = 50, dT = 10
|
||||||
|
(105.0, 165.0, 10.0, 22.0), // dV = 60, dT = 12
|
||||||
|
(110.0, 180.0, 10.0, 25.0) // dV = 70, dT = 15
|
||||||
|
);
|
||||||
|
|
||||||
|
SetPrivateField(sut, "_rawStartEndByChannel", raw);
|
||||||
|
SetPrivateField(sut, "_recalculatedStartEndByChannel", raw); // important for NoSamples == false
|
||||||
|
SetPrivateField(sut, "optoDataCount", 2);
|
||||||
|
|
||||||
|
sut.TestStartTelegramIx = 0;
|
||||||
|
sut.TestEndTelegramIx = 1;
|
||||||
|
|
||||||
|
Assert.IsFalse(sut.NoSamples, "Expected prepared data to produce valid samples.");
|
||||||
|
|
||||||
|
var init = new[] { 15625.0, 15625.0, 15625.0 };
|
||||||
|
var valid = new bool[3];
|
||||||
|
var calib = new double[3];
|
||||||
|
|
||||||
|
sut.GetQ3Calibration(
|
||||||
|
refVolume: 200.0,
|
||||||
|
refTime: 120.0,
|
||||||
|
initCalibFactor: init,
|
||||||
|
ref valid,
|
||||||
|
ref calib);
|
||||||
|
|
||||||
|
double expectedCh1 = (200.0 / 600.0) * 15625.0;
|
||||||
|
double expectedCh2 = (200.0 / 600.0) * 15625.0;
|
||||||
|
double expectedCh3 = (200.0 / 560.0) * 15625.0;
|
||||||
|
|
||||||
|
Assert.AreEqual(expectedCh1, calib[0], 0.0001);
|
||||||
|
Assert.AreEqual(expectedCh2, calib[1], 0.0001);
|
||||||
|
Assert.AreEqual(expectedCh3, calib[2], 0.0001);
|
||||||
|
|
||||||
|
Assert.IsFalse(valid[0]);
|
||||||
|
Assert.IsFalse(valid[1]);
|
||||||
|
Assert.IsFalse(valid[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void GetQ3Calibration_ShouldMarkChannelValid_WhenDifferenceIsWithinFivePercent()
|
||||||
|
{
|
||||||
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
|
// We want recalculatedDeltaVolume == refVolume, so calculated factor == initial factor.
|
||||||
|
// refVolume = 200, refTime = 120
|
||||||
|
// choose dT = 10, dV = 16.6666666666667 => coef = 12 => recalculated dV = 200
|
||||||
|
|
||||||
|
var raw = CreateKnownStartEndData(
|
||||||
|
(100.0, 116.6666666666667, 10.0, 20.0),
|
||||||
|
(200.0, 216.6666666666667, 10.0, 20.0),
|
||||||
|
(300.0, 316.6666666666667, 10.0, 20.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
SetPrivateField(sut, "_rawStartEndByChannel", raw);
|
||||||
|
SetPrivateField(sut, "_recalculatedStartEndByChannel", raw);
|
||||||
|
SetPrivateField(sut, "optoDataCount", 2);
|
||||||
|
sut.TestStartTelegramIx = 0;
|
||||||
|
sut.TestEndTelegramIx = 1;
|
||||||
|
|
||||||
|
Assert.IsFalse(sut.NoSamples, "Expected prepared data to produce valid samples.");
|
||||||
|
|
||||||
|
var init = new[] { 15625.0, 15625.0, 15625.0 };
|
||||||
|
var valid = new bool[3];
|
||||||
|
var calib = new double[3];
|
||||||
|
|
||||||
|
sut.GetQ3Calibration(
|
||||||
|
refVolume: 200.0,
|
||||||
|
refTime: 120.0,
|
||||||
|
initCalibFactor: init,
|
||||||
|
ref valid,
|
||||||
|
ref calib);
|
||||||
|
|
||||||
|
Assert.IsTrue(valid[0], $"Ch1 invalid, value={calib[0]}");
|
||||||
|
Assert.IsTrue(valid[1], $"Ch2 invalid, value={calib[1]}");
|
||||||
|
Assert.IsTrue(valid[2], $"Ch3 invalid, value={calib[2]}");
|
||||||
|
|
||||||
|
Assert.AreEqual(15625.0, calib[0], 0.001);
|
||||||
|
Assert.AreEqual(15625.0, calib[1], 0.001);
|
||||||
|
Assert.AreEqual(15625.0, calib[2], 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CalculateQ3Calibration_ShouldComputeExpectedChannelValues_FromSimulationData()
|
||||||
|
{
|
||||||
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
|
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||||
|
InvokePrivateByName(sut, "SetQ3Calibration", new object[] { ValidInitFactors });
|
||||||
|
|
||||||
|
sut.CalculateQ3Calibration(200.0, 120.0);
|
||||||
|
|
||||||
|
// initial values remain exposed through Q3CalibValue
|
||||||
|
Assert.AreEqual(15625.0, sut.Q3CalibValue[0], 0.000001);
|
||||||
|
Assert.AreEqual(15625.0, sut.Q3CalibValue[1], 0.000001);
|
||||||
|
Assert.AreEqual(15625.0, sut.Q3CalibValue[2], 0.000001);
|
||||||
|
|
||||||
|
// calculated values
|
||||||
|
Assert.AreEqual(5208.33333333333, sut.Q3Calib_Ch1Value, 0.000001);
|
||||||
|
Assert.AreEqual(5008.01282051282, sut.Q3Calib_Ch2Value, 0.000001);
|
||||||
|
Assert.AreEqual(4822.53086419753, sut.Q3Calib_Ch3Value, 0.000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CalculateQ3Calibration_ShouldKeepInitialArray_AndUpdateCalculatedChannelProperties()
|
||||||
|
{
|
||||||
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
|
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||||
|
InvokePrivateByName(sut, "SetQ3Calibration", new object[] { ValidInitFactors });
|
||||||
|
|
||||||
|
sut.CalculateQ3Calibration(200.0, 120.0);
|
||||||
|
|
||||||
|
// Q3CalibValue currently exposes INITIAL calibration values, not calculated ones
|
||||||
|
Assert.AreEqual(15625.0, sut.Q3CalibValue[0], 0.000001);
|
||||||
|
Assert.AreEqual(15625.0, sut.Q3CalibValue[1], 0.000001);
|
||||||
|
Assert.AreEqual(15625.0, sut.Q3CalibValue[2], 0.000001);
|
||||||
|
|
||||||
|
// Per-channel public properties expose calculated values
|
||||||
|
Assert.IsTrue(sut.Q3Calib_Ch1Value > 0.0);
|
||||||
|
Assert.IsTrue(sut.Q3Calib_Ch2Value > 0.0);
|
||||||
|
Assert.IsTrue(sut.Q3Calib_Ch3Value > 0.0);
|
||||||
|
|
||||||
|
Assert.AreNotEqual(sut.Q3CalibValue[0], sut.Q3Calib_Ch1Value, 0.000001);
|
||||||
|
Assert.AreNotEqual(sut.Q3CalibValue[1], sut.Q3Calib_Ch2Value, 0.000001);
|
||||||
|
Assert.AreNotEqual(sut.Q3CalibValue[2], sut.Q3Calib_Ch3Value, 0.000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void PrepareCalculatedChannelDataSimulationForTest_ShouldPrepareUsableData()
|
||||||
|
{
|
||||||
|
var sut = new GenesisSmartReader();
|
||||||
|
|
||||||
|
InvokePrivateByName(sut, "PrepareCalculatedChannelDataSimulationForTest");
|
||||||
|
|
||||||
|
Assert.IsFalse(sut.NoSamples);
|
||||||
|
Assert.IsTrue(sut.VolumeLtrStart >= 0.0);
|
||||||
|
Assert.IsTrue(sut.VolumeLtrEnd >= 0.0);
|
||||||
|
Assert.IsTrue(sut.TimestampSecEnd >= sut.TimestampSecStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitializeReaderForQ3Test(GenesisSmartReader reader)
|
private static void InitializeReaderForQ3Test(GenesisSmartReader reader)
|
||||||
@ -115,20 +312,55 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|||||||
{
|
{
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
CreateRecord(0.0, 0.0),
|
CreateRecord(0.0, 0.0, 1),
|
||||||
CreateRecord(15625.0, 120.0)
|
CreateRecord(15625.0, 120.0, 1)
|
||||||
},
|
},
|
||||||
new OptoTelegramRaw[2],
|
new[]
|
||||||
new OptoTelegramRaw[2]
|
{
|
||||||
|
CreateRecord(0.0, 0.0, 2),
|
||||||
|
CreateRecord(15625.0, 120.0, 2)
|
||||||
|
},
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
CreateRecord(0.0, 0.0, 3),
|
||||||
|
CreateRecord(15625.0, 120.0, 3)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OptoTelegramRaw CreateRecord(double volumeRawExt, double timestampExt)
|
private static OptoTelegramRaw[][] CreateKnownStartEndData(
|
||||||
|
(double startVolume, double endVolume, double startTime, double endTime) ch1,
|
||||||
|
(double startVolume, double endVolume, double startTime, double endTime) ch2,
|
||||||
|
(double startVolume, double endVolume, double startTime, double endTime) ch3)
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
CreateRecord(ch1.startVolume, ch1.startTime, 1),
|
||||||
|
CreateRecord(ch1.endVolume, ch1.endTime, 1)
|
||||||
|
},
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
CreateRecord(ch2.startVolume, ch2.startTime, 2),
|
||||||
|
CreateRecord(ch2.endVolume, ch2.endTime, 2)
|
||||||
|
},
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
CreateRecord(ch3.startVolume, ch3.startTime, 3),
|
||||||
|
CreateRecord(ch3.endVolume, ch3.endTime, 3)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OptoTelegramRaw CreateRecord(double volumeRawExt, double timestampExt, int channel)
|
||||||
{
|
{
|
||||||
return new OptoTelegramRaw
|
return new OptoTelegramRaw
|
||||||
{
|
{
|
||||||
VolumeRawExt = volumeRawExt,
|
VolumeRawExt = volumeRawExt,
|
||||||
TimestampExt = timestampExt
|
TimestampExt = timestampExt,
|
||||||
|
iChannel = channel,
|
||||||
|
Flags = OptoTelegramFlags.OK
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,35 +12,36 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
|||||||
[TestSubject(typeof(CliRunner))]
|
[TestSubject(typeof(CliRunner))]
|
||||||
public class CliRunnerTest
|
public class CliRunnerTest
|
||||||
{
|
{
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void ExtractJson_Test()
|
public void ExtractJson_Test()
|
||||||
{
|
{
|
||||||
String allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
|
string allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
|
||||||
CliRunner cliRunner = new CliRunner(false);
|
CliRunner cliRunner = new CliRunner(false);
|
||||||
string json = cliRunner.ExtractJson(allOutput);
|
string json = cliRunner.ExtractJson(allOutput);
|
||||||
|
|
||||||
Assert.IsTrue(!string.IsNullOrEmpty(json));
|
Assert.IsFalse(string.IsNullOrEmpty(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void ExtractJson_Test2()
|
public void ExtractJson_Test2()
|
||||||
{
|
{
|
||||||
String allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
|
string allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
|
||||||
CliRunner cliRunner = new CliRunner(false);
|
CliRunner cliRunner = new CliRunner(false);
|
||||||
string json = cliRunner.ExtractJson(allOutput);
|
string json = cliRunner.ExtractJson(allOutput);
|
||||||
|
|
||||||
Assert.IsTrue(!string.IsNullOrEmpty(json));
|
Assert.IsFalse(string.IsNullOrEmpty(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TryJsonStringDeserialize_Test()
|
public void TryJsonStringDeserialize_Test()
|
||||||
{
|
{
|
||||||
String allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
|
string allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
|
||||||
|
|
||||||
CliRunner cliRunner = new CliRunner(false);
|
CliRunner cliRunner = new CliRunner(false);
|
||||||
string json = cliRunner.ExtractJson(allOutput);
|
string json = cliRunner.ExtractJson(allOutput);
|
||||||
|
|
||||||
var ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
|
bool ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
|
||||||
|
|
||||||
Assert.IsTrue(ok, "Deserialization failed");
|
Assert.IsTrue(ok, "Deserialization failed");
|
||||||
Assert.IsNotNull(dto);
|
Assert.IsNotNull(dto);
|
||||||
Assert.AreEqual("1000000267", dto.DeviceId);
|
Assert.AreEqual("1000000267", dto.DeviceId);
|
||||||
@ -53,120 +54,165 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void RunMultipleTimesTestProgram_CheckParalelWork()
|
public void RunMultipleTimesTestProgram_CheckParalelWork()
|
||||||
{
|
{
|
||||||
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74);
|
SerialPortData serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
|
||||||
|
|
||||||
// Check if the executable exists in current directory
|
|
||||||
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
|
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
|
||||||
{
|
{
|
||||||
Assert.Inconclusive($"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
|
Assert.Inconclusive(
|
||||||
|
$"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CliRunner cliRunner = new CliRunner(false);
|
CliRunner cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
CliRunner cliRunnerOne = new CliRunner(false);
|
CliRunner cliRunnerOne = new CliRunner(false);
|
||||||
DateTime StartTime = DateTime.Now;
|
DateTime startTime = DateTime.Now;
|
||||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
|
||||||
|
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||||
|
serialPort,
|
||||||
|
SerialPortData.EMeterArg.AllParams);
|
||||||
|
|
||||||
cliRunnerOne.WaitAll();
|
cliRunnerOne.WaitAll();
|
||||||
TimeSpan OneEndTime = DateTime.Now - StartTime;
|
|
||||||
|
TimeSpan oneEndTime = DateTime.Now - startTime;
|
||||||
StartTime = DateTime.Now;
|
|
||||||
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
startTime = DateTime.Now;
|
||||||
|
Console.WriteLine($"Start Time Loop: {startTime:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++)
|
for (int i = 0; i < 100; i++)
|
||||||
{
|
{
|
||||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||||
|
serialPort,
|
||||||
|
SerialPortData.EMeterArg.AllParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
cliRunner.WaitAll();
|
cliRunner.WaitAll();
|
||||||
|
|
||||||
DateTime EndTime = DateTime.Now;
|
DateTime endTime = DateTime.Now;
|
||||||
Console.WriteLine($"End Time Loop: {EndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
Console.WriteLine($"End Time Loop: {endTime:yyyy-MM-dd HH:mm:ss}");
|
||||||
TimeSpan delta = EndTime - StartTime;
|
|
||||||
Console.WriteLine($"Delta Time One process {OneEndTime.Hours:D2}:{OneEndTime.Minutes:D2}:{OneEndTime.Seconds:D2}.{OneEndTime.Milliseconds:D3} " +
|
TimeSpan delta = endTime - startTime;
|
||||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
Console.WriteLine(
|
||||||
|
$"Delta Time One process {oneEndTime.Hours:D2}:{oneEndTime.Minutes:D2}:{oneEndTime.Seconds:D2}.{oneEndTime.Milliseconds:D3} " +
|
||||||
|
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||||
Console.WriteLine($"Total tasks: {cliRunner.TaskPool.Count}");
|
Console.WriteLine($"Total tasks: {cliRunner.TaskPool.Count}");
|
||||||
|
|
||||||
|
foreach (CliTaskInfo taskInfo in cliRunner.TaskPool)
|
||||||
foreach (Task<JsonDataFromPoseidon> task in cliRunner.TaskPool)
|
|
||||||
{
|
{
|
||||||
if (task != null)
|
Assert.IsNotNull(taskInfo);
|
||||||
|
Assert.IsNotNull(taskInfo.Task);
|
||||||
|
|
||||||
|
if (taskInfo.UseResult)
|
||||||
{
|
{
|
||||||
|
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
||||||
|
Assert.IsNotNull(task, "Expected Task<JsonDataFromPoseidon>.");
|
||||||
|
|
||||||
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
|
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
|
||||||
Console.WriteLine($"Result: {jsonDataFromPoseidon.DeviceId}");
|
Console.WriteLine($"Result: {jsonDataFromPoseidon?.DeviceId}");
|
||||||
Assert.IsNotNull(jsonDataFromPoseidon);
|
Assert.IsNotNull(jsonDataFromPoseidon);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Fail($"Task did not complete successfully. State={taskInfo.State}, Status={taskInfo.Task.Status}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the total time is greater than the sum of the individual times
|
Assert.IsTrue((cliRunner.TaskPool.Count * oneEndTime.Ticks) > delta.Ticks);
|
||||||
Assert.IsTrue((cliRunner.TaskPool.Count*OneEndTime.Ticks) > delta.Ticks);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task RunMultipleTimesTestProgram_CheckParalelWorkCumulative()
|
public async Task RunMultipleTimesTestProgram_CheckParalelWorkCumulative()
|
||||||
{
|
{
|
||||||
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74);
|
SerialPortData serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
|
||||||
|
|
||||||
// Check if the executable exists in current directory
|
|
||||||
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
|
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
|
||||||
{
|
{
|
||||||
Assert.Inconclusive($"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
|
Assert.Inconclusive(
|
||||||
|
$"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<CliRunner> cliRunnerList = new List<CliRunner>();
|
List<CliRunner> cliRunnerList = new List<CliRunner>();
|
||||||
|
|
||||||
for (int i = 0; i < 20; i++)
|
for (int i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
cliRunnerList.Add(new CliRunner(false));
|
cliRunnerList.Add(new CliRunner(false));
|
||||||
|
}
|
||||||
|
|
||||||
CliRunner cliRunnerOne = new CliRunner(false);
|
CliRunner cliRunnerOne = new CliRunner(false);
|
||||||
DateTime StartTime = DateTime.Now;
|
DateTime startTime = DateTime.Now;
|
||||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
|
||||||
|
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||||
|
serialPort,
|
||||||
|
SerialPortData.EMeterArg.AllParams);
|
||||||
|
|
||||||
cliRunnerOne.WaitAll();
|
cliRunnerOne.WaitAll();
|
||||||
TimeSpan OneEndTime = DateTime.Now - StartTime;
|
|
||||||
|
TimeSpan oneEndTime = DateTime.Now - startTime;
|
||||||
StartTime = DateTime.Now;
|
|
||||||
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
startTime = DateTime.Now;
|
||||||
|
Console.WriteLine($"Start Time Loop: {startTime:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
|
||||||
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
|
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
|
||||||
{
|
{
|
||||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
|
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||||
|
serialPort,
|
||||||
SerialPortData.EMeterArg.AllParams);
|
SerialPortData.EMeterArg.AllParams);
|
||||||
|
|
||||||
for (int i = 0; i < 20; i++)
|
for (int i = 0; i < 20; i++)
|
||||||
{
|
{
|
||||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
|
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||||
|
serialPort,
|
||||||
SerialPortData.EMeterArg.AllParams);
|
SerialPortData.EMeterArg.AllParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for ALL tasks from ALL runners
|
Task[] allTasks = cliRunnerList
|
||||||
var allTasks = cliRunnerList.SelectMany(r => r.TaskPool).ToArray();
|
.SelectMany(r => r.TaskPool)
|
||||||
|
.Where(ti => ti != null && ti.Task != null)
|
||||||
|
.Select(ti => ti.Task)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
await Task.WhenAll(allTasks);
|
await Task.WhenAll(allTasks);
|
||||||
|
|
||||||
DateTime EndTime = DateTime.Now;
|
DateTime endTime = DateTime.Now;
|
||||||
Console.WriteLine($"End Time Loop: {EndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
Console.WriteLine($"End Time Loop: {endTime:yyyy-MM-dd HH:mm:ss}");
|
||||||
TimeSpan delta = EndTime - StartTime;
|
|
||||||
Console.WriteLine($"Delta Time One process {OneEndTime.Hours:D2}:{OneEndTime.Minutes:D2}:{OneEndTime.Seconds:D2}.{OneEndTime.Milliseconds:D3} " +
|
TimeSpan delta = endTime - startTime;
|
||||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
Console.WriteLine(
|
||||||
|
$"Delta Time One process {oneEndTime.Hours:D2}:{oneEndTime.Minutes:D2}:{oneEndTime.Seconds:D2}.{oneEndTime.Milliseconds:D3} " +
|
||||||
|
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||||
Console.WriteLine($"Total tasks: {cliRunnerList.Sum(runner => runner.TaskPool.Count)}");
|
Console.WriteLine($"Total tasks: {cliRunnerList.Sum(runner => runner.TaskPool.Count)}");
|
||||||
|
|
||||||
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
|
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
|
||||||
{
|
{
|
||||||
string results = $"iCliRunner: {iCliRunner}";
|
string results = $"iCliRunner: {iCliRunner}";
|
||||||
foreach (Task<JsonDataFromPoseidon> task in cliRunnerList[iCliRunner].TaskPool)
|
|
||||||
|
foreach (CliTaskInfo taskInfo in cliRunnerList[iCliRunner].TaskPool)
|
||||||
{
|
{
|
||||||
if (task != null)
|
Assert.IsNotNull(taskInfo);
|
||||||
|
Assert.IsNotNull(taskInfo.Task);
|
||||||
|
|
||||||
|
if (taskInfo.UseResult)
|
||||||
{
|
{
|
||||||
|
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
||||||
|
Assert.IsNotNull(task, "Expected Task<JsonDataFromPoseidon>.");
|
||||||
|
|
||||||
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
|
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
|
||||||
results += ($" ID: {jsonDataFromPoseidon.DeviceId}");
|
results += $" ID: {jsonDataFromPoseidon?.DeviceId}";
|
||||||
Assert.IsNotNull(jsonDataFromPoseidon);
|
Assert.IsNotNull(jsonDataFromPoseidon);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Fail(
|
||||||
|
$"Task did not complete successfully. Runner={iCliRunner}, State={taskInfo.State}, Status={taskInfo.Task.Status}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(results);
|
Console.WriteLine(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the total time is greater than the sum of the individual times
|
Assert.IsTrue((cliRunnerList.Count * oneEndTime.Ticks) > delta.Ticks);
|
||||||
Assert.IsTrue((cliRunnerList.Count*OneEndTime.Ticks) > delta.Ticks);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,249 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
|
||||||
|
|
||||||
|
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[TestSubject(typeof(CliRunner))]
|
||||||
|
public class CliRunnerTimeoutTest
|
||||||
|
{
|
||||||
|
private static string GetCmdSleepTestPath()
|
||||||
|
{
|
||||||
|
var serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
|
||||||
|
return serialPort.SerialPortCmdClientPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureExeExists(string exePath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(exePath))
|
||||||
|
{
|
||||||
|
Assert.Inconclusive(
|
||||||
|
$"Test executable '{exePath}' not found. " +
|
||||||
|
"Please ensure cmdSleepTest.exe exists in the test directory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Timeout_ShouldBeDetected_ForLongRunningTask()
|
||||||
|
{
|
||||||
|
string exePath = GetCmdSleepTestPath();
|
||||||
|
EnsureExeExists(exePath);
|
||||||
|
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||||
|
|
||||||
|
await Task.Delay(200);
|
||||||
|
|
||||||
|
bool timedOut = cliRunner.TimeOutReceived(100);
|
||||||
|
|
||||||
|
Assert.IsTrue(timedOut, "Timeout should have been detected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task CancelUndoneTasksAsTimedOut_ShouldMarkRunningTaskAsTimedOut()
|
||||||
|
{
|
||||||
|
string exePath = GetCmdSleepTestPath();
|
||||||
|
EnsureExeExists(exePath);
|
||||||
|
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||||
|
|
||||||
|
await Task.Delay(150);
|
||||||
|
|
||||||
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
||||||
|
|
||||||
|
var taskInfo = cliRunner.TaskPool.Single();
|
||||||
|
|
||||||
|
Assert.IsNotNull(taskInfo);
|
||||||
|
Assert.AreEqual(CliTaskState.TimedOut, taskInfo.State);
|
||||||
|
Assert.IsFalse(taskInfo.UseResult, "Timed out task must not be used.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CancelUndoneTasksAsTimedOut_ShouldKeepCompletedTaskCompleted()
|
||||||
|
{
|
||||||
|
string exePath = GetCmdSleepTestPath();
|
||||||
|
EnsureExeExists(exePath);
|
||||||
|
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cliRunner.WaitAll();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// let assertions inspect states
|
||||||
|
}
|
||||||
|
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
||||||
|
|
||||||
|
var taskInfo = cliRunner.TaskPool.Single();
|
||||||
|
|
||||||
|
Assert.IsNotNull(taskInfo);
|
||||||
|
Assert.AreEqual(CliTaskState.Completed, taskInfo.State);
|
||||||
|
Assert.IsTrue(taskInfo.UseResult, "Completed task should remain usable.");
|
||||||
|
|
||||||
|
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
||||||
|
Assert.IsNotNull(task);
|
||||||
|
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
|
||||||
|
Assert.IsNotNull(task.Result);
|
||||||
|
Assert.IsFalse(string.IsNullOrEmpty(task.Result.DeviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task CancelUndoneTasksAsTimedOut_ShouldKeepDoneTask_AndMarkUndoneTask()
|
||||||
|
{
|
||||||
|
string exePath = GetCmdSleepTestPath();
|
||||||
|
EnsureExeExists(exePath);
|
||||||
|
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50");
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||||
|
|
||||||
|
await Task.Delay(250);
|
||||||
|
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
Assert.AreEqual(2, cliRunner.TaskPool.Count);
|
||||||
|
Assert.AreEqual(1, cliRunner.TaskPool.Count(t => t.State == CliTaskState.Completed),
|
||||||
|
"Exactly one fast task should be completed before timeout handling.");
|
||||||
|
|
||||||
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
var doneTask = cliRunner.TaskPool.Single(t => t.State == CliTaskState.Completed);
|
||||||
|
var timedOutTask = cliRunner.TaskPool.Single(t => t.State == CliTaskState.TimedOut);
|
||||||
|
|
||||||
|
Assert.IsTrue(doneTask.UseResult);
|
||||||
|
Assert.IsFalse(timedOutTask.UseResult);
|
||||||
|
|
||||||
|
var completed = doneTask.Task as Task<JsonDataFromPoseidon>;
|
||||||
|
Assert.IsNotNull(completed);
|
||||||
|
Assert.AreEqual(TaskStatus.RanToCompletion, completed.Status);
|
||||||
|
Assert.IsNotNull(completed.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task AreTasksDone_ShouldReturnTrue_AfterTimeoutCancellation()
|
||||||
|
{
|
||||||
|
string exePath = GetCmdSleepTestPath();
|
||||||
|
EnsureExeExists(exePath);
|
||||||
|
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||||
|
|
||||||
|
await Task.Delay(150);
|
||||||
|
|
||||||
|
Assert.IsFalse(cliRunner.AreTasksDone(), "Task should still be running before timeout cancellation.");
|
||||||
|
|
||||||
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
Assert.IsTrue(cliRunner.AreTasksDone(),
|
||||||
|
"After timed out tasks are marked, runner should report no running tasks.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task TimedOutTask_ShouldNotHaveUseResult()
|
||||||
|
{
|
||||||
|
string exePath = GetCmdSleepTestPath();
|
||||||
|
EnsureExeExists(exePath);
|
||||||
|
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||||
|
|
||||||
|
await Task.Delay(150);
|
||||||
|
|
||||||
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
var taskInfo = cliRunner.TaskPool.Single();
|
||||||
|
|
||||||
|
Assert.AreEqual(CliTaskState.TimedOut, taskInfo.State);
|
||||||
|
Assert.IsFalse(taskInfo.UseResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task MultipleSlowTasks_ShouldAllBeMarkedTimedOut()
|
||||||
|
{
|
||||||
|
string exePath = GetCmdSleepTestPath();
|
||||||
|
EnsureExeExists(exePath);
|
||||||
|
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(200);
|
||||||
|
|
||||||
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
Assert.AreEqual(5, cliRunner.TaskPool.Count);
|
||||||
|
Assert.AreEqual(5, cliRunner.TaskPool.Count(t => t.State == CliTaskState.TimedOut));
|
||||||
|
Assert.AreEqual(0, cliRunner.TaskPool.Count(t => t.UseResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void FastTask_WithInvalidJson_ShouldFinishButNotProduceUsefulResult()
|
||||||
|
{
|
||||||
|
string exePath = GetCmdSleepTestPath();
|
||||||
|
EnsureExeExists(exePath);
|
||||||
|
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50 invalidjson=true");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cliRunner.WaitAll();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
||||||
|
|
||||||
|
var taskInfo = cliRunner.TaskPool.Single();
|
||||||
|
Assert.AreEqual(CliTaskState.Completed, taskInfo.State);
|
||||||
|
|
||||||
|
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
||||||
|
Assert.IsNotNull(task);
|
||||||
|
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
|
||||||
|
|
||||||
|
// depending on your TryJsonStringDeserialize fallback,
|
||||||
|
// result may be null or an empty/default object
|
||||||
|
// so avoid asserting UseResult=false here
|
||||||
|
// instead assert no valid device id
|
||||||
|
if (task.Result != null)
|
||||||
|
{
|
||||||
|
Assert.IsTrue(string.IsNullOrEmpty(task.Result.DeviceId),
|
||||||
|
"Invalid JSON should not produce a valid DeviceId.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
|
||||||
|
|
||||||
|
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[TestSubject(typeof(CliRunner))]
|
||||||
|
public class CliRunnerTimeoutUnitTest
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task CancelUndoneTasksAsTimedOut_ShouldMarkOnlyRunningTasks()
|
||||||
|
{
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
var completedTask = Task.FromResult("done");
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
var runningTask = Task.Delay(TimeSpan.FromSeconds(30), cts.Token);
|
||||||
|
|
||||||
|
cliRunner.AddTaskForTest(completedTask, "completed");
|
||||||
|
cliRunner.AddTaskForTest(runningTask, "running", cts);
|
||||||
|
|
||||||
|
await Task.Delay(50);
|
||||||
|
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
Assert.AreEqual(2, cliRunner.TaskPool.Count);
|
||||||
|
|
||||||
|
var completed = cliRunner.TaskPool.First(t => t.Name == "completed");
|
||||||
|
var timedOut = cliRunner.TaskPool.First(t => t.Name == "running");
|
||||||
|
|
||||||
|
Assert.AreEqual(CliTaskState.Completed, completed.State);
|
||||||
|
Assert.IsTrue(completed.UseResult);
|
||||||
|
|
||||||
|
Assert.AreEqual(CliTaskState.TimedOut, timedOut.State);
|
||||||
|
Assert.IsFalse(timedOut.UseResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TimeOutReceived_ShouldReturnTrue_WhenElapsedExceedsTimeout()
|
||||||
|
{
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
Thread.Sleep(30);
|
||||||
|
|
||||||
|
Assert.IsTrue(cliRunner.TimeOutReceived(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task AreTasksDone_ShouldReturnTrue_WhenTimedOutTasksAreMarked()
|
||||||
|
{
|
||||||
|
var cliRunner = new CliRunner(false);
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
var runningTask = Task.Delay(TimeSpan.FromSeconds(30), cts.Token);
|
||||||
|
|
||||||
|
cliRunner.AddTaskForTest(runningTask, "running", cts);
|
||||||
|
|
||||||
|
await Task.Delay(50);
|
||||||
|
|
||||||
|
Assert.IsFalse(cliRunner.AreTasksDone());
|
||||||
|
|
||||||
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||||
|
cliRunner.RefreshTaskStates();
|
||||||
|
|
||||||
|
Assert.IsTrue(cliRunner.AreTasksDone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -125,6 +125,8 @@
|
|||||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadBaudRateDetectionTests.cs" />
|
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadBaudRateDetectionTests.cs" />
|
||||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrameBuilderTest.cs" />
|
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrameBuilderTest.cs" />
|
||||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTest.cs" />
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTest.cs" />
|
||||||
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutTest.cs" />
|
||||||
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutUnitTest.cs" />
|
||||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
|
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
|
||||||
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />
|
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />
|
||||||
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />
|
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user