Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
842f009b42 | ||
|
|
7f30ca0cd1 | ||
|
|
93e207c211 | ||
|
|
64cafc088a | ||
|
|
8a0da27a07 | ||
|
|
5a8478e782 | ||
|
|
368cd95114 | ||
|
|
3376ac41b4 | ||
|
|
f9181fee74 | ||
|
|
ff5954d85b | ||
|
|
c09e266b2c |
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("3.9.3056.1")]
|
||||
[assembly: AssemblyFileVersion("3.9.3056.1")]
|
||||
[assembly: AssemblyVersion("3.9.3058.1")]
|
||||
[assembly: AssemblyFileVersion("3.9.3058.1")]
|
||||
|
||||
@@ -175,8 +175,8 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
double reqFlowLo = (0.7 * qFrom) + (0.3 * reqFlowAve); /// Move the lower limit 15% of the range up
|
||||
double reqFlowHi = (0.7 * qTo) + (0.3 * reqFlowAve); /// Move the upper limit 15% of the range down
|
||||
///
|
||||
uniCB.SetFlow(false, regV.Idx1, 2000.0 * reqFlowLo / flowMeter.NominalFlow,
|
||||
2000.0 * reqFlowHi / flowMeter.NominalFlow);
|
||||
uniCB.SetFlow(false, regV.Idx1, flowMeter.NominalFreq * reqFlowLo / flowMeter.NominalFlow,
|
||||
flowMeter.NominalFreq * reqFlowHi / flowMeter.NominalFlow);
|
||||
if (readDivTransition)
|
||||
{
|
||||
/// Schedule reading diverter transition data
|
||||
|
||||
@@ -803,12 +803,60 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
/// </summary>
|
||||
public void SetFlow(bool isFromUI, int regVId, double freqLo, double freqHi, int regulationMinStep = 0)
|
||||
{
|
||||
if (IsUIBlocked && isFromUI) return;
|
||||
LiveLogDiag.Log1("----------------------------------------");
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> ENTER isFromUI={0} regVId={1} freqLo={2} freqHi={3} regulationMinStep={4} IsUIBlocked={5}",
|
||||
isFromUI, regVId, freqLo, freqHi, regulationMinStep, IsUIBlocked);
|
||||
|
||||
actionQueue.Enqueue(Action.SetFlow(isFromUI, regVId, freqLo, freqHi, Convert.ToInt32(Math.Round(Devices.PidCoef)), 200, regulationMinStep));
|
||||
log.InfoFormat("Enqueue( SetFlow(rv={0}, fLo={1}, fHi={2}, pid={3}) )", regVId, freqLo, freqHi, Convert.ToInt32(Math.Round(Devices.PidCoef)));
|
||||
if (IsUIBlocked && isFromUI)
|
||||
{
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> EXIT (UI BLOCKED) isFromUI={0}",
|
||||
isFromUI);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
||||
int pid = Convert.ToInt32(Math.Round(Devices.PidCoef));
|
||||
int fixedParam = 200;
|
||||
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> PREPARE pid={0} fixedParam={1} regulationMinStep={2}",
|
||||
pid, fixedParam, regulationMinStep);
|
||||
|
||||
var action = Action.SetFlow(
|
||||
isFromUI,
|
||||
regVId,
|
||||
freqLo,
|
||||
freqHi,
|
||||
pid,
|
||||
fixedParam,
|
||||
regulationMinStep);
|
||||
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> ACTION CREATED: rv={0} fLo={1} fHi={2} pid={3} p6={4} minStep={5}",
|
||||
regVId, freqLo, freqHi, pid, fixedParam, regulationMinStep);
|
||||
|
||||
actionQueue.Enqueue(action);
|
||||
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> ENQUEUED actionQueue.Count={0}",
|
||||
actionQueue.Count);
|
||||
|
||||
log.InfoFormat(
|
||||
"Enqueue( SetFlow(rv={0}, fLo={1}, fHi={2}, pid={3}) )",
|
||||
regVId, freqLo, freqHi, pid);
|
||||
|
||||
int i = 0;
|
||||
foreach (var a in actionQueue)
|
||||
{
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> QUEUE[{0}] = {1}",
|
||||
i++, a);
|
||||
log.DebugFormat(" {0}", a);
|
||||
}
|
||||
|
||||
LiveLogDiag.Log1("SetFlow() >> EXIT");
|
||||
LiveLogDiag.Log1("----------------------------------------");
|
||||
}
|
||||
|
||||
public void StartTest(bool isFromUI, int flowMId, int divId, int divThreshold,
|
||||
@@ -981,5 +1029,18 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
{
|
||||
/// TODO
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logging
|
||||
/// For activation use compilation condition: LIVELOGDIAG_FlowMeter_cs
|
||||
/// </summary>
|
||||
static class LiveLogDiag
|
||||
{
|
||||
[Conditional("LIVELOGDIAG_UniCB_cs")]
|
||||
public static void Log1(string format, params object[] args)
|
||||
{
|
||||
LiveLogCache.Instance.AddLog("UniCB.cs LOG>> " + string.Format(format, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,8 +633,31 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
public double TimestampSecEndCh1 => NoSamples ? 0 : GetCachedTime(_recalculatedStartEndByChannel, 0, 1);
|
||||
public double TimestampSecEndCh2 => NoSamples ? 0 : GetCachedTime(_recalculatedStartEndByChannel, 1, 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
|
||||
{
|
||||
return VolumeLtrStartRaw;
|
||||
return VolumeLtrStartAverage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,7 +675,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
{
|
||||
get
|
||||
{
|
||||
return VolumeLtrEndRaw;
|
||||
return VolumeLtrEndAwerage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1302,7 +1325,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
try
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -3885,20 +3909,61 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
private bool isQ3CalibValid = false;
|
||||
private double q3Calib = 0;
|
||||
private double[] q3CalibInitial = {Double.NaN,Double.NaN,Double.NaN};
|
||||
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 ===");
|
||||
|
||||
isQ3CalibValid = false;
|
||||
q3Calib = 0.0;
|
||||
//initialisation
|
||||
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}");
|
||||
|
||||
@@ -3914,7 +3979,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
return;
|
||||
}
|
||||
|
||||
if (refVolume <= 0 || refTime <= 0 || initCalibFactor <= 0)
|
||||
if (refVolume <= 0 || refTime <= 0)
|
||||
{
|
||||
log.Debug("EXIT: Invalid input values (<= 0)");
|
||||
return;
|
||||
@@ -3931,6 +3996,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
|
||||
{
|
||||
|
||||
var channelData = _rawStartEndByChannel[iChannel];
|
||||
|
||||
if (channelData == null || channelData.Length < 2)
|
||||
@@ -3938,7 +4004,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
log.Debug($"Channel {iChannel}: SKIPPED (no data)");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var start = channelData[0];
|
||||
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($" Delta: dT={deltaTime}, dV={deltaVolume}");
|
||||
|
||||
double recalculatedDeltaVolume = 0.0f;
|
||||
if (deltaTime > 0)
|
||||
{
|
||||
double timeCoef = refTime / deltaTime;
|
||||
double recalculatedDeltaVolume = deltaVolume * timeCoef;
|
||||
double timeCoef = refTime / deltaTime; //?
|
||||
recalculatedDeltaVolume = deltaVolume * timeCoef;
|
||||
|
||||
recalculatedVariablesByChannel[iChannel][1].VolumeRawExt =
|
||||
recalculatedVariablesByChannel[iChannel][0].VolumeRawExt + recalculatedDeltaVolume;
|
||||
@@ -3979,52 +4046,18 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
recalculatedVariablesByChannel[iChannel][0].TimestampExt = 0;
|
||||
recalculatedVariablesByChannel[iChannel][1].TimestampExt = refTime;
|
||||
}
|
||||
|
||||
double scaling = 15625.0;
|
||||
log.Debug($"Scaling={scaling}");
|
||||
|
||||
if (scaling <= 0)
|
||||
{
|
||||
log.Debug("EXIT: scaling <= 0");
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
if (Math.Abs(recalculatedDeltaVolume) <= Double.Epsilon)
|
||||
{
|
||||
log.Debug($"Channel {iChannel}: recalculatedDeltaVolume is zero");
|
||||
continue;
|
||||
}
|
||||
|
||||
q3CalibCh[iChannel] = (refVolume / recalculatedDeltaVolume) * initCalibFactor[iChannel];
|
||||
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}");
|
||||
|
||||
}
|
||||
|
||||
log.Debug("=== Q3 CALIBRATION END ===");
|
||||
|
||||
@@ -13,46 +13,29 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
public class CliRunner
|
||||
{
|
||||
//static readonly ILog log = LogManager.GetLogger(typeof(CliRunner));
|
||||
private readonly ILog log;
|
||||
private List<Task> taskPool = new List<Task>();
|
||||
private long startTime;
|
||||
private long incommingTime;
|
||||
|
||||
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 => { }); }
|
||||
private readonly List<CliTaskInfo> taskPool = new List<CliTaskInfo>();
|
||||
private long startTimeMs;
|
||||
private long incommingTimeMs;
|
||||
|
||||
public void StartAll()
|
||||
public List<CliTaskInfo> TaskPool
|
||||
{
|
||||
taskPool.ForEach(t => t.Start());
|
||||
}
|
||||
|
||||
public bool AreTasksDone()
|
||||
{
|
||||
return taskPool.All(task => task.IsCompleted);
|
||||
get { return taskPool; }
|
||||
}
|
||||
|
||||
public long StartTime
|
||||
{
|
||||
get => startTime;
|
||||
get { return startTimeMs; }
|
||||
}
|
||||
|
||||
public long IncommingTime
|
||||
{
|
||||
get => incommingTime;
|
||||
get { return incommingTimeMs; }
|
||||
}
|
||||
|
||||
public bool TimeOutReceived(long timeout)
|
||||
{
|
||||
return (DateTime.Now.Ticks - startTime) > timeout;
|
||||
}
|
||||
|
||||
public CliRunner(bool isCliLogging)
|
||||
{
|
||||
startTime = DateTime.Now.Ticks;
|
||||
ResetStartTime();
|
||||
|
||||
if (isCliLogging)
|
||||
{
|
||||
@@ -60,88 +43,180 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
log = new ScopedLoggerFactory().CreateLogger<CliRunner>(
|
||||
@"C:\TBF\Logs\CliRunner.txt",
|
||||
10, // maxFileSizeMB
|
||||
7, // maxBackups
|
||||
10,
|
||||
7,
|
||||
log4net.Core.Level.Debug,
|
||||
true, // zipRolledFiles
|
||||
true, // singleZipPerDay
|
||||
TimeSpan.FromMinutes(2) // zipScanInterval
|
||||
true,
|
||||
true,
|
||||
TimeSpan.FromMinutes(2)
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fallback to console or handle gracefully
|
||||
Console.WriteLine($"Failed to initialize logger: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public void AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg)
|
||||
public void ResetStartTime()
|
||||
{
|
||||
var task = SendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
|
||||
taskPool.Add(task);
|
||||
startTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
}
|
||||
|
||||
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 };
|
||||
process.Start();
|
||||
|
||||
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
|
||||
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
|
||||
|
||||
try
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var item in taskPool)
|
||||
{
|
||||
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (!process.HasExited)
|
||||
try
|
||||
{
|
||||
item?.Cts?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
|
||||
throw;
|
||||
try
|
||||
{
|
||||
if (item?.Process != null)
|
||||
{
|
||||
item.Process.Dispose();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
//comming answer from serial port - good place for time stamp
|
||||
incommingTime = DateTime.Now.Ticks;
|
||||
string allOutput = (stdOutTask.Result ?? "") + (stdErrTask.Result ?? "");
|
||||
log?.Debug(allOutput);
|
||||
return allOutput;
|
||||
taskPool.Clear();
|
||||
}
|
||||
|
||||
|
||||
public void AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new()
|
||||
public void WaitAll()
|
||||
{
|
||||
var task = RunAndCaptureJsonAsync<T>(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
|
||||
taskPool.Add(task);
|
||||
var tasks = taskPool
|
||||
.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
|
||||
{
|
||||
@@ -153,80 +228,210 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
||||
process.Start();
|
||||
using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
|
||||
{
|
||||
info.Process = process;
|
||||
|
||||
var stdoutTask = process.StandardOutput.ReadToEndAsync();
|
||||
var stderrTask = process.StandardError.ReadToEndAsync();
|
||||
try
|
||||
{
|
||||
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 ?? "");
|
||||
log?.Debug(allOutput);
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (info.State != CliTaskState.TimedOut)
|
||||
info.State = CliTaskState.Canceled;
|
||||
|
||||
string json = ExtractJson(allOutput);
|
||||
if (TryJsonStringDeserialize(json, out T result)) return result;
|
||||
return default;
|
||||
if (!process.HasExited)
|
||||
{
|
||||
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)
|
||||
{
|
||||
try
|
||||
{
|
||||
runAndCaptureJsonAsync = JsonConvert.DeserializeObject<T>(json);
|
||||
value = JsonConvert.DeserializeObject<T>(json);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Debug(ex.Message);
|
||||
runAndCaptureJsonAsync = TryConvert<T>(json);
|
||||
log?.Debug(ex.Message);
|
||||
value = TryConvert<T>(json);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
runAndCaptureJsonAsync = default;
|
||||
value = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
JToken token;
|
||||
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
|
||||
try
|
||||
{
|
||||
object value = token.ToObject(prop.PropertyType);
|
||||
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)
|
||||
{
|
||||
Console.WriteLine($"TryConvert failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"TryConvert failed: {ex.Message}");
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public string ExtractJson(string text)
|
||||
@@ -241,6 +446,20 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// </summary>
|
||||
private long startTimeInMilis = -1, fullTimeInMilis = -1;
|
||||
/// 30 seconds
|
||||
private static long SafetyTimeOut = 30 * 1000;
|
||||
private long incommingTime = -1;
|
||||
private bool _lastOpTimedOut;
|
||||
|
||||
|
||||
/// 30 seconds
|
||||
|
||||
public long DeltaTime { get{return fullTimeInMilis;}}
|
||||
public long IncommingTime { get{return incommingTime;}}
|
||||
@@ -356,108 +358,146 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
|
||||
if (_currentOp == CurrentPoseidonOp.SendStartDataStream)
|
||||
{
|
||||
CliRunner.Clear();
|
||||
_lastOpTimedOut = false;
|
||||
CliRunner.AddSendAsync(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
return Event.Busy;
|
||||
_currentOp = CurrentPoseidonOp.SendStartDataStream_Runing;
|
||||
return Event.Busy;
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Runing)
|
||||
{
|
||||
if (CliRunner.AreTasksDone()
|
||||
|| CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||
if (CliRunner.AreTasksDone())
|
||||
{
|
||||
_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)
|
||||
{
|
||||
var firstTask = CliRunner.TaskPool.FindLast(t => t is Task<string>);
|
||||
var firstTaskInfo = CliRunner.TaskPool
|
||||
.FindLast(t => t.Task is Task<string> && t.UseResult);
|
||||
|
||||
|
||||
if (firstTask != null && firstTask is Task<string>)
|
||||
if (firstTaskInfo != null)
|
||||
{
|
||||
string data = null;
|
||||
data = (firstTask as Task<string>).Result;
|
||||
var task = (Task<string>)firstTaskInfo.Task;
|
||||
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;
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
|
||||
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
|
||||
|| _currentOp == CurrentPoseidonOp.ReadDataStream_End)
|
||||
{
|
||||
startTimeInMilis = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
|
||||
startTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
incommingTime = -1;
|
||||
_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;
|
||||
return Event.Busy;
|
||||
}else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
|
||||
{
|
||||
if (CliRunner.AreTasksDone()
|
||||
|| CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||
if (CliRunner.AreTasksDone())
|
||||
{
|
||||
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
|
||||
//set time stamp - end of reading
|
||||
incommingTime = CliRunner.IncommingTime;
|
||||
}
|
||||
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||
{
|
||||
_lastOpTimedOut = true;
|
||||
CliRunner.CancelUndoneTasksAsTimedOut();
|
||||
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
|
||||
incommingTime = CliRunner.IncommingTime;
|
||||
}
|
||||
|
||||
return Event.Busy;
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Done)
|
||||
{
|
||||
fullTimeInMilis = (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - startTimeInMilis;
|
||||
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;
|
||||
fullTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTimeInMilis;
|
||||
log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis}");
|
||||
|
||||
//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))
|
||||
{
|
||||
try
|
||||
{
|
||||
wmSerialNr = data?.DeviceId ?? wmSerialNr;
|
||||
wmSerialNr = data.DeviceId ?? wmSerialNr;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error("Serial Nr - parse error!");
|
||||
log.Error("Serial Nr - parse error!", e);
|
||||
}
|
||||
}
|
||||
|
||||
//GET volume
|
||||
if (data != null && Double.TryParse(data.Reading, out double Volume))
|
||||
double volume;
|
||||
if (Double.TryParse(data.Reading, out volume))
|
||||
{
|
||||
double VolumeLi = Units.ConvertFrom(Unit.USgal, Volume);
|
||||
//double VolumeM3 = Units.ConvertTo(Unit.m3, VolumeLi);
|
||||
if (_isReadingStart)
|
||||
{
|
||||
//volume
|
||||
beginWMState = VolumeLi;
|
||||
}
|
||||
else
|
||||
{
|
||||
//volume
|
||||
endWMState = VolumeLi;
|
||||
}
|
||||
}
|
||||
double volumeLi = Units.ConvertFrom(Unit.USgal, volume);
|
||||
|
||||
if (_isReadingStart)
|
||||
beginWMState = volumeLi;
|
||||
else
|
||||
endWMState = volumeLi;
|
||||
}
|
||||
}
|
||||
|
||||
//Finish reading and loop
|
||||
_currentOp = CurrentPoseidonOp.Done;
|
||||
|
||||
CliRunner.Clear();
|
||||
_currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
|
||||
return Event.Done;
|
||||
}
|
||||
|
||||
return Event.None;
|
||||
|
||||
return Event.None;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Stop this operation</summary>
|
||||
public void Stop()
|
||||
{
|
||||
|
||||
@@ -1396,18 +1396,18 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
||||
chanelXMeterRslt?.CopyContentFrom(meterRslt);
|
||||
if (iCH == 0)
|
||||
{
|
||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartCh1,
|
||||
genesisSmart.TimestampSecEndCh1, genesisSmart.VolumeLtrStartCh1, genesisSmart.VolumeLtrEndCh1);
|
||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh1,
|
||||
genesisSmart.TimestampSecEndRawCh1, genesisSmart.VolumeLtrStartRawCh1, genesisSmart.VolumeLtrEndRawCh1);
|
||||
}
|
||||
else if (iCH == 1)
|
||||
{
|
||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartCh2,
|
||||
genesisSmart.TimestampSecEndCh2, genesisSmart.VolumeLtrStartCh2, genesisSmart.VolumeLtrEndCh2);
|
||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh2,
|
||||
genesisSmart.TimestampSecEndRawCh2, genesisSmart.VolumeLtrStartRawCh2, genesisSmart.VolumeLtrEndRawCh2);
|
||||
}
|
||||
else if (iCH == 2)
|
||||
{
|
||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartCh3,
|
||||
genesisSmart.TimestampSecEndCh3, genesisSmart.VolumeLtrStartCh3, genesisSmart.VolumeLtrEndCh3);
|
||||
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh3,
|
||||
genesisSmart.TimestampSecEndRawCh3, genesisSmart.VolumeLtrStartRawCh3, genesisSmart.VolumeLtrEndRawCh3);
|
||||
}
|
||||
|
||||
if (!genesisSmart.EnableShowChanels)
|
||||
|
||||
@@ -6,20 +6,21 @@ using TBF.Rig.Generic;
|
||||
|
||||
namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
{
|
||||
public class Factory : IComponentFactory
|
||||
{
|
||||
public class Factory : IComponentFactory
|
||||
{
|
||||
public string ClassName { get { return GetType().Namespace.Substring(12); } }
|
||||
public override string ToString() { return ClassName; }
|
||||
|
||||
public IComponent DummyComponent() { return new FlowMeter(); }
|
||||
public IComponent DummyComponent() { return new FlowMeter(); }
|
||||
|
||||
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new FlowMeter(cfg, components); }
|
||||
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new FlowMeter(cfg, components); }
|
||||
|
||||
public IComponentCfg DefaultConfig() { return new FlowMeterCfg(this, "I11+I12"); }
|
||||
public IComponentCfg DefaultConfig() { return new FlowMeterCfg(this, "I11+I12"); }
|
||||
|
||||
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
|
||||
{
|
||||
return ComponentCfgBase.CreateFromDbEntity(FlowMeterCfg.Serializer, component, this);
|
||||
}
|
||||
}
|
||||
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
|
||||
{
|
||||
return ComponentCfgBase.CreateFromDbEntity(FlowMeterCfg.Serializer, component, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using SchematicDrawing;
|
||||
using SharedComponents;
|
||||
using TBF.Boxes;
|
||||
using TBF.Rig.ControlBoard.Uni;
|
||||
using TBF.Rig.GenericDevices;
|
||||
@@ -12,17 +13,16 @@ using TBF.Rig.GenericDevices;
|
||||
namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
{
|
||||
public class FlowMeter : ComponentBase, IFlowMeter, IDrawingItCmpntWithMeasuredVal
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(FlowMeter));
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(FlowMeter));
|
||||
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
||||
|
||||
readonly FlowMeterCfg myCfg;
|
||||
readonly FlowMeterCfg flowMeterCfg;
|
||||
|
||||
UniCB uniCB;
|
||||
IFlowMeterSingle flowMtr1;
|
||||
IFlowMeterSingle flowMtr2;
|
||||
IFlowMeterSingle flowMtr3;
|
||||
IFlowMeterSingle inactiveFlowMtr;
|
||||
double nominalFlow;
|
||||
double nominalFreq;
|
||||
double ltrPerPulse;
|
||||
@@ -34,22 +34,20 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
public double NominalFreq { get { return nominalFreq; } }
|
||||
public double LtrPerPulse { get { return ltrPerPulse; } }
|
||||
|
||||
public SchematicDrawing.IDrawingItem DrawingItem { get { return myCfg as SchematicDrawing.IDrawingItem; } }
|
||||
public string MsrdFormat { get { return myCfg.MsrdFormat; } }
|
||||
public Common.Unit MsrdUnit { get { return myCfg.MsrdUnit; } }
|
||||
public double MsrdValLimLo { get { return myCfg.MsrdValLimLo; } set { myCfg.MsrdValLimLo = value; } }
|
||||
public double MsrdValLimHi { get { return myCfg.MsrdValLimHi; } set { myCfg.MsrdValLimHi = value; } }
|
||||
|
||||
public SchematicDrawing.IDrawingItem DrawingItem { get { return flowMeterCfg as SchematicDrawing.IDrawingItem; } }
|
||||
public string MsrdFormat { get { return flowMeterCfg.MsrdFormat; } }
|
||||
public Common.Unit MsrdUnit { get { return flowMeterCfg.MsrdUnit; } }
|
||||
public double MsrdValLimLo { get { return flowMeterCfg.MsrdValLimLo; } set { flowMeterCfg.MsrdValLimLo = value; } }
|
||||
public double MsrdValLimHi { get { return flowMeterCfg.MsrdValLimHi; } set { flowMeterCfg.MsrdValLimHi = value; } }
|
||||
|
||||
public bool MsrmntAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
bool result = true;
|
||||
if (flowMtr1 != null) result = (result && flowMtr1.MsrmntAvailable);
|
||||
if (flowMtr2 != null) result = (result && flowMtr2.MsrmntAvailable);
|
||||
if (flowMtr3 != null) result = (result && flowMtr3.MsrmntAvailable);
|
||||
if (inactiveFlowMtr != null) result = (result && !inactiveFlowMtr.MsrmntAvailable);
|
||||
if (flowMtr1 != null) result = result && flowMtr1.MsrmntAvailable;
|
||||
if (flowMtr2 != null) result = result && flowMtr2.MsrmntAvailable;
|
||||
if (flowMtr3 != null) result = result && flowMtr3.MsrmntAvailable;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -61,24 +59,22 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
|
||||
public string AltString { get { return "Invalid format"; } }
|
||||
|
||||
public FlowMeter() { }
|
||||
|
||||
public FlowMeter() { }
|
||||
|
||||
public FlowMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
||||
: base(cfg)
|
||||
{
|
||||
myCfg = cfg as FlowMeterCfg;
|
||||
}
|
||||
public FlowMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
||||
: base(cfg)
|
||||
{
|
||||
flowMeterCfg = cfg as FlowMeterCfg;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
uniCB = TbfComponents.FindComponent(myCfg.ParentName) as UniCB;
|
||||
uniCB = TbfComponents.FindComponent(flowMeterCfg.ParentName) as UniCB;
|
||||
if (uniCB == null) throw new Exception("Cannot find " + Name + " parent");
|
||||
|
||||
if (!string.IsNullOrEmpty(myCfg.Flowmeter1)) flowMtr1 = TbfComponents.FindComponent(myCfg.Flowmeter1) as IFlowMeterSingle;
|
||||
if (!string.IsNullOrEmpty(myCfg.Flowmeter2)) flowMtr2 = TbfComponents.FindComponent(myCfg.Flowmeter2) as IFlowMeterSingle;
|
||||
if (!string.IsNullOrEmpty(myCfg.Flowmeter3)) flowMtr3 = TbfComponents.FindComponent(myCfg.Flowmeter3) as IFlowMeterSingle;
|
||||
if (!string.IsNullOrEmpty(myCfg.InactiveFlowmtr)) inactiveFlowMtr = TbfComponents.FindComponent(myCfg.InactiveFlowmtr) as IFlowMeterSingle;
|
||||
if (!string.IsNullOrEmpty(flowMeterCfg.Flowmeter1)) flowMtr1 = TbfComponents.FindComponent(flowMeterCfg.Flowmeter1) as IFlowMeterSingle;
|
||||
if (!string.IsNullOrEmpty(flowMeterCfg.Flowmeter2)) flowMtr2 = TbfComponents.FindComponent(flowMeterCfg.Flowmeter2) as IFlowMeterSingle;
|
||||
if (!string.IsNullOrEmpty(flowMeterCfg.Flowmeter3)) flowMtr3 = TbfComponents.FindComponent(flowMeterCfg.Flowmeter3) as IFlowMeterSingle;
|
||||
|
||||
nominalFlow = 0;
|
||||
flowMetersCount = 0;
|
||||
@@ -91,8 +87,8 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
flowMetersBitfield |= (4 << flowMtr1.Idx1);
|
||||
flowMetersCount++;
|
||||
}
|
||||
if (flowMtr2 != null)
|
||||
{
|
||||
if (flowMtr2 != null)
|
||||
{
|
||||
nominalFlow += flowMtr2.NominalFlow;
|
||||
nominalFreq += flowMtr2.NominalFreq;
|
||||
flowMetersBitfield |= (4 << flowMtr2.Idx1);
|
||||
@@ -109,15 +105,31 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
|
||||
MsrdValLimHi = nominalFlow;
|
||||
|
||||
nominalFreq = nominalFreq / flowMetersCount;
|
||||
|
||||
ltrPerPulse = nominalFlow / (3.6 * nominalFreq); /// Nominal freq. is sum of particular nominal frquencies
|
||||
|
||||
log.FatalFormat("{0} initialized: {1} nom.flow={2} m3/h nom.freq={3} Hz bitfield=0x{4:X2}",
|
||||
Name, this, nominalFlow, nominalFreq, flowMetersBitfield);
|
||||
Name, true, nominalFlow, nominalFreq, flowMetersBitfield);
|
||||
}
|
||||
|
||||
public double ReadFlow()
|
||||
{
|
||||
return ReadFrequency() * nominalFlow / nominalFreq;
|
||||
double flowByNewFormulaSum = 0;
|
||||
|
||||
if (flowMtr1 != null)
|
||||
{
|
||||
flowByNewFormulaSum += flowMtr1.NominalFlow * uniCB.Data.ReferenceFreq[1] / flowMtr1.NominalFreq;
|
||||
}
|
||||
if (flowMtr2 != null)
|
||||
{
|
||||
flowByNewFormulaSum += flowMtr2.NominalFlow * uniCB.Data.ReferenceFreq[2] / flowMtr2.NominalFreq;
|
||||
}
|
||||
if (flowMtr3 != null)
|
||||
{
|
||||
flowByNewFormulaSum += flowMtr3.NominalFlow * uniCB.Data.ReferenceFreq[3] / flowMtr3.NominalFreq;
|
||||
}
|
||||
return flowByNewFormulaSum/*ReadFrequency() * nominalFlow / nominalFreq*/;
|
||||
}
|
||||
|
||||
public double ReadFrequency()
|
||||
@@ -125,27 +137,26 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
return uniCB.RefFrequency;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Events: FlowDone, Error
|
||||
/// </summary>
|
||||
/// <param name="flow">Reference to a variable for the flow in Bar</param>
|
||||
/// <returns>ReadFlowOp instance reference casted to IOperaton</returns>
|
||||
public IOperation ReadFlowOp(ref DoubleBox flow)
|
||||
{
|
||||
return new ReadFlowOp(this, ref flow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Events: flowDone, Error
|
||||
/// </summary>
|
||||
/// <param name="flow">Reference to a variable for the flow in Bar</param>
|
||||
/// <param name="flowDone">Event returned when measurement done</param>
|
||||
/// <returns>ReadFlowOp instance reference casted to IOperaton</returns>
|
||||
public IOperation ReadFlowOp(ref DoubleBox flow, Event flowDone)
|
||||
{
|
||||
return new ReadFlowOp(this, ref flow, flowDone);
|
||||
}
|
||||
/// <summary>
|
||||
/// Events: FlowDone, Error
|
||||
/// </summary>
|
||||
/// <param name="flow">Reference to a variable for the flow in Bar</param>
|
||||
/// <returns>ReadFlowOp instance reference casted to IOperaton</returns>
|
||||
public IOperation ReadFlowOp(ref DoubleBox flow)
|
||||
{
|
||||
return new ReadFlowOp(this, ref flow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Events: flowDone, Error
|
||||
/// </summary>
|
||||
/// <param name="flow">Reference to a variable for the flow in Bar</param>
|
||||
/// <param name="flowDone">Event returned when measurement done</param>
|
||||
/// <returns>ReadFlowOp instance reference casted to IOperaton</returns>
|
||||
public IOperation ReadFlowOp(ref DoubleBox flow, Event flowDone)
|
||||
{
|
||||
return new ReadFlowOp(this, ref flow, flowDone);
|
||||
}
|
||||
|
||||
double flowRawSum;
|
||||
double flowSum;
|
||||
@@ -197,7 +208,7 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
public double LtrPerPulseCorrected(double flow, float temperature)
|
||||
{
|
||||
double ltrPerPulseCorrected = (flowRawSum == 0) ? LtrPerPulse : LtrPerPulse * flowSum / flowRawSum;
|
||||
log.WarnFormat("LtrPerPulseCorrected() flow = {0:F3} m3/h flowRawSum = {1:F3} m3/h flowSum = {2:F3} m3/h temperature = {3:F1} C ltrPerPulseCorrected = {4}", flow, flowRawSum, flowSum, temperature, ltrPerPulseCorrected);
|
||||
log.WarnFormat("LtrPerPulseCorrected() flow = {0} m3/h flowRawSum = {1} m3/h flowSum = {2} m3/h temperature = {3} C ltrPerPulseCorrected = {4}", flow, flowRawSum, flowSum, temperature, ltrPerPulseCorrected);
|
||||
return ltrPerPulseCorrected;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ using TBF.Rig.Generic;
|
||||
namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
{
|
||||
public class FlowMeterCfg : ComponentCfgBase, IChildComponentCfg, IParamsProvider, IDrawingItemWithMeasuredVal
|
||||
{
|
||||
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(FlowMeterCfg) })[0];
|
||||
public override XmlSerializer GetSerializer() { return Serializer; }
|
||||
{
|
||||
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(FlowMeterCfg) })[0];
|
||||
public override XmlSerializer GetSerializer() { return Serializer; }
|
||||
|
||||
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities)
|
||||
{
|
||||
@@ -24,15 +24,14 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
return new Configs.ParamsProvider.ComponentCfgCtrl(this, parents);
|
||||
}
|
||||
|
||||
///
|
||||
/// Serialized parameters
|
||||
///
|
||||
///
|
||||
/// Serialized parameters
|
||||
///
|
||||
public string Flowmeter1; /// 0 Flowmeter component name or string.Empty
|
||||
public string Flowmeter2; /// 1 - ' ' -
|
||||
public string Flowmeter3; /// 2 - ' ' -
|
||||
public string InactiveFlowmtr; /// 3 Inactive flowmeter component name or string.Empty
|
||||
public string MsrdFormat { get; set; } /// 4
|
||||
public Unit MsrdUnit { get; set; } /// 5
|
||||
public string MsrdFormat { get; set; } /// 3
|
||||
public Unit MsrdUnit { get; set; } /// 4
|
||||
|
||||
/// Schematic drawing info
|
||||
public Shape Shape { get; set; }
|
||||
@@ -58,9 +57,6 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
|
||||
private IEnumerable<Config.Entities.Component> flowMeters;
|
||||
|
||||
[XmlIgnore]
|
||||
public bool IsOffline { get; set; }
|
||||
|
||||
/// Private parameterless constructor invoked by all other (public) constructors
|
||||
FlowMeterCfg()
|
||||
{
|
||||
@@ -82,12 +78,13 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
|
||||
public string ComponentName { get { return Name; } }
|
||||
|
||||
public bool IsOffline { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public void InitializeAll()
|
||||
{
|
||||
Flowmeter1 = "I11";
|
||||
Flowmeter2 = "I12";
|
||||
Flowmeter3 = string.Empty;
|
||||
InactiveFlowmtr = "I13";
|
||||
MsrdFormat = "{0:F3} m3/h";
|
||||
MsrdUnit = Unit.m3ph;
|
||||
}
|
||||
@@ -97,9 +94,8 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
"Flow meter 1", /// 0
|
||||
"Flow meter 2", /// 1
|
||||
"Flow meter 3", /// 2
|
||||
"Inactive flow meter", /// 3
|
||||
"Display format", /// 4
|
||||
"Unit of flow on a display", /// 5
|
||||
"Display format", /// 3
|
||||
"Unit of flow on a display", /// 4
|
||||
};
|
||||
public string ParamName(int i) { return paramNames[i]; }
|
||||
public int ParamsCount() { return paramNames.Length; }
|
||||
@@ -111,48 +107,44 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (flowMeters == null) return null;
|
||||
var fmtrs = new List<string>();
|
||||
foreach (var fmtr in flowMeters) fmtrs.Add(fmtr.Name);
|
||||
fmtrs.Add("---");
|
||||
return fmtrs;
|
||||
case 5:
|
||||
return new string[] { "m3/h", "l/h", "gal/m" };
|
||||
case 4:
|
||||
return new string[] { "m3/h", "l/h" };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public string ToString(int i)
|
||||
{
|
||||
public string ToString(int i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0: return string.IsNullOrEmpty(Flowmeter1) ? "---" : Flowmeter1;
|
||||
case 1: return string.IsNullOrEmpty(Flowmeter2) ? "---" : Flowmeter2;
|
||||
case 2: return string.IsNullOrEmpty(Flowmeter3) ? "---" : Flowmeter3;
|
||||
case 3: return string.IsNullOrEmpty(InactiveFlowmtr) ? "---" : InactiveFlowmtr;
|
||||
case 4: return MsrdFormat;
|
||||
case 5: return MsrdUnit.ToDescription();
|
||||
case 3: return MsrdFormat;
|
||||
case 4: return MsrdUnit.ToDescription();
|
||||
|
||||
default:
|
||||
return string.Format("Name={0} {1} {2} {3} ~{4} fmt={5} unit={6} Lo={7} Hi={8}",
|
||||
Name, Flowmeter1, Flowmeter2, Flowmeter3, InactiveFlowmtr,
|
||||
return string.Format("Name={0} {1} {2} {3} fmt={4} unit={5} Lo={6} Hi={7}",
|
||||
Name, Flowmeter1, Flowmeter2, Flowmeter3,
|
||||
MsrdFormat, MsrdUnit.ToDescription(), MsrdValLimLo, MsrdValLimHi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CfgUpdateFlags UpdateParam(int i, string str)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0: Flowmeter1 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
|
||||
case 1: Flowmeter2 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
|
||||
case 2: Flowmeter3 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
|
||||
case 3: InactiveFlowmtr = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
|
||||
case 4: MsrdFormat = str; return CfgUpdateFlags.RestartRqrd;
|
||||
//case 5: MsrdUnit = (str == "l/h") ? Unit.lph : Unit.m3ph; return CfgUpdateFlags.RestartRqrd;
|
||||
case 5: MsrdUnit = ParseFlowUnit(str); return CfgUpdateFlags.RestartRqrd;
|
||||
case 0: Flowmeter1 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
|
||||
case 1: Flowmeter2 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
|
||||
case 2: Flowmeter3 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
|
||||
case 3: MsrdFormat = str; return CfgUpdateFlags.RestartRqrd;
|
||||
case 4: MsrdUnit = (str == "l/h") ? Unit.lph : Unit.m3ph; return CfgUpdateFlags.RestartRqrd;
|
||||
default:
|
||||
return CfgUpdateFlags.None;
|
||||
}
|
||||
@@ -167,11 +159,10 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 5:
|
||||
case 4:
|
||||
if (ParamValues(i).Contains(str)) return true;
|
||||
break;
|
||||
case 4:
|
||||
case 3:
|
||||
return true;
|
||||
default:
|
||||
message = "Invalid index";
|
||||
@@ -187,7 +178,6 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
prms.Flowmeter1 = this.Flowmeter1;
|
||||
prms.Flowmeter2 = this.Flowmeter2;
|
||||
prms.Flowmeter3 = this.Flowmeter3;
|
||||
prms.InactiveFlowmtr = this.InactiveFlowmtr;
|
||||
prms.MsrdFormat = this.MsrdFormat;
|
||||
prms.MsrdUnit = this.MsrdUnit;
|
||||
}
|
||||
@@ -203,19 +193,5 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
{
|
||||
return true; /// =OK, do nothing
|
||||
}
|
||||
public static Unit ParseFlowUnit(string str)
|
||||
{
|
||||
//Check if the string is a valid unit description
|
||||
Unit fromDescription = Units.FromDescription(str);
|
||||
if (fromDescription != Unit.None) return fromDescription;
|
||||
//check if the string is a valid unit name
|
||||
switch (str)
|
||||
{
|
||||
case "l/h": return Unit.lph;
|
||||
case "gal/m": return Unit.USgalpm;
|
||||
case "m3/h":
|
||||
default: return Unit.m3ph;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,57 +8,57 @@ using TBF.Rig.GenericDevices;
|
||||
|
||||
namespace TBF.Rig.Uni.FlowMetersInParallel
|
||||
{
|
||||
public class ReadFlowOp : IOperation
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(ReadFlowOp));
|
||||
public class ReadFlowOp : IOperation
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(ReadFlowOp));
|
||||
public override string ToString() { return string.Format("ReadFlowOp({0},.,.,{1})", flowMeter.Idx1, eventDone); }
|
||||
|
||||
/// Set by the constructor
|
||||
/// Set by the constructor
|
||||
readonly IFlowMeter flowMeter;
|
||||
readonly DoubleBox flowBox;
|
||||
readonly Event eventDone;
|
||||
readonly DoubleBox flowBox;
|
||||
readonly Event eventDone;
|
||||
|
||||
/// <summary>
|
||||
/// Events: FlowInDone, FlowOutDone or Error
|
||||
/// </summary>
|
||||
/// <param name="flowMeter">Flow meter reference</param>
|
||||
/// <param name="flowBox">Reference to the measured flow variable, value is in bar</param>
|
||||
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
|
||||
/// <summary>
|
||||
/// Events: FlowInDone, FlowOutDone or Error
|
||||
/// </summary>
|
||||
/// <param name="flowMeter">Flow meter reference</param>
|
||||
/// <param name="flowBox">Reference to the measured flow variable, value is in bar</param>
|
||||
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
|
||||
public ReadFlowOp(IFlowMeter flowMeter, ref DoubleBox flowBox, Event eventDone)
|
||||
{
|
||||
if (flowMeter == null) throw new ArgumentNullException();
|
||||
|
||||
this.flowMeter = flowMeter;
|
||||
this.eventDone = eventDone;
|
||||
this.flowBox = flowBox;
|
||||
this.eventDone = eventDone;
|
||||
this.flowBox = flowBox;
|
||||
|
||||
log.Debug(this.ToString());
|
||||
log.Debug(this.ToString());
|
||||
}
|
||||
|
||||
public ReadFlowOp(IFlowMeter flowMeter, ref DoubleBox flowBox)
|
||||
: this(flowMeter, ref flowBox, Event.FlowMeasurementDone)
|
||||
{
|
||||
}
|
||||
: this(flowMeter, ref flowBox, Event.FlowMeasurementDone)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Start this operation</summary>
|
||||
public void Start() { }
|
||||
/// <summary>Start this operation</summary>
|
||||
public void Start() { }
|
||||
|
||||
/// <summary>Run this operation</summary>
|
||||
/// <returns>
|
||||
/// Event.FlowInDone or Event.FlowOutDone
|
||||
/// </returns>
|
||||
public Event Run()
|
||||
{
|
||||
/// <summary>Run this operation</summary>
|
||||
/// <returns>
|
||||
/// Event.FlowInDone or Event.FlowOutDone
|
||||
/// </returns>
|
||||
public Event Run()
|
||||
{
|
||||
if (flowMeter.MsrmntAvailable)
|
||||
{
|
||||
{
|
||||
if (flowBox != null) flowBox.Val = flowMeter.ReadFlow();
|
||||
return eventDone;
|
||||
}
|
||||
return eventDone;
|
||||
}
|
||||
|
||||
return Event.Error;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Stop this operation</summary>
|
||||
public void Stop() { }
|
||||
}
|
||||
/// <summary>Stop this operation</summary>
|
||||
public void Stop() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
///
|
||||
/// Copyright (c) 2021-2023 Sensus Slovensko a.s.
|
||||
/// Copyright (c) 2021 Sensus Metering Systems
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using TBF.Rig.ControlBoard.Uni;
|
||||
using TBF.Rig.GenericDevices;
|
||||
|
||||
namespace TBF.Rig.Uni.RegValve
|
||||
@@ -14,12 +13,12 @@ namespace TBF.Rig.Uni.RegValve
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(ChangeRegValvePositionOp));
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("ChangeRegValvePositionOp({0},{1}s)", regV.Name, timePulseSec.ToString("F2"));
|
||||
return string.Format("ChangeRegValvePositionOp({0},{1}s)", regulValve.Name, timePulseSec.ToString("F2"));
|
||||
}
|
||||
|
||||
/// Set by the constructor
|
||||
readonly UniCB uniCB;
|
||||
readonly RegValve regV;
|
||||
readonly TBF.Rig.ControlBoard.Uni.UniCB controlBoard;
|
||||
readonly RegValve regulValve;
|
||||
readonly int regulValveNr;
|
||||
readonly double timePulseSec;
|
||||
|
||||
@@ -33,14 +32,14 @@ namespace TBF.Rig.Uni.RegValve
|
||||
/// <param name="posHiPct">Upper limit of the position to be achieved</param>
|
||||
/// <param name="timeout">Timeout in sec. for setting the flow</param>
|
||||
/// <remarks>Only Elde.Valve flow are used, other flow on the lists are ignored</remarks>
|
||||
public ChangeRegValvePositionOp(UniCB uniCB, RegValve regV, double timePulseSec)
|
||||
public ChangeRegValvePositionOp(TBF.Rig.ControlBoard.IControlBoard cb, RegValve rv, double timePulseSec)
|
||||
{
|
||||
this.uniCB = uniCB;
|
||||
if (this.uniCB == null) throw new ArgumentNullException("ctrlBoard");
|
||||
controlBoard = cb as TBF.Rig.ControlBoard.Uni.UniCB;
|
||||
if (controlBoard == null) throw new ArgumentNullException("ctrlBoard");
|
||||
|
||||
this.regV = regV as RegValve;
|
||||
if (this.regV == null) throw new ArgumentNullException("regValve is null or not Uni");
|
||||
regulValveNr = this.regV.Idx1;
|
||||
regulValve = rv as RegValve;
|
||||
if (regulValve == null) throw new ArgumentNullException("regValve is null or not Elde");
|
||||
regulValveNr = this.regulValve.Idx1;
|
||||
|
||||
this.timePulseSec = timePulseSec;
|
||||
|
||||
@@ -50,7 +49,13 @@ namespace TBF.Rig.Uni.RegValve
|
||||
/// <summary>Start this operation</summary>
|
||||
public void Start()
|
||||
{
|
||||
uniCB.RegVlvIncrMove(false, regulValveNr, timePulseSec);
|
||||
//double positionPct = controlBoard.RValvePosition(regulValveNr);
|
||||
//log.WarnFormat("RV#={0}, actPos={1}%", regulValveNr, positionPct.ToString("F1"));
|
||||
|
||||
//controlBoard.ValveMove(regulValveNr,
|
||||
// TBF.Rig.ControlBoard.Legacy.RegulValveMode.PulseWidth,
|
||||
// new double[2] { timePulseSec, timePulseSec },
|
||||
// regulValve.StableTime);
|
||||
}
|
||||
|
||||
/// <summary>Run this operation</summary>
|
||||
@@ -59,6 +64,9 @@ namespace TBF.Rig.Uni.RegValve
|
||||
/// </returns>
|
||||
public Event Run()
|
||||
{
|
||||
//float positionPct = controlBoard.RValvePosition(regulValveNr);
|
||||
//log.WarnFormat("RV#={0}, actPos={1}%", regulValveNr, positionPct.ToString("F1"));
|
||||
|
||||
return Event.PositionReached;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///
|
||||
/// Copyright (c) 2021 Sensus Slovensko a.s.
|
||||
/// Copyright (c) 2021 Sensus Metering Systems
|
||||
///
|
||||
using System.Collections.Generic;
|
||||
using TBF.Rig.Generic;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///
|
||||
/// Copyright (c) 2021 Sensus Slovensko a.s.
|
||||
/// Copyright (c) 2021 Sensus Metering Systems
|
||||
///
|
||||
using System;
|
||||
using log4net;
|
||||
|
||||
@@ -20,10 +20,11 @@ namespace TBF.Rig.Uni.RegValve
|
||||
return string.Format("SetFlowOp({0}, Qfrom={1}, Qto={2})", regV.Name, shrinkedTgtFlowLo, shrinkedTgtFlowHi);
|
||||
}
|
||||
|
||||
|
||||
public const double RqrdFlowRangeRatio = 0.5;
|
||||
|
||||
///
|
||||
/// Set by the constructor
|
||||
/// Set by the constructor
|
||||
///
|
||||
readonly UniCB uniCB;
|
||||
readonly RegValve regV;
|
||||
@@ -36,8 +37,8 @@ namespace TBF.Rig.Uni.RegValve
|
||||
readonly bool leaveFlowControlRunning;
|
||||
|
||||
readonly double maximalFlow;
|
||||
|
||||
///
|
||||
|
||||
///
|
||||
/// Internal state of this operation
|
||||
///
|
||||
enum OpState
|
||||
@@ -56,12 +57,13 @@ namespace TBF.Rig.Uni.RegValve
|
||||
|
||||
double currentReqFlowLo;
|
||||
double currentReqFlowHi;
|
||||
double targetPositionLo;
|
||||
double targetPositionHi;
|
||||
double targetPositionLo;
|
||||
double targetPositionHi;
|
||||
|
||||
int startTime;
|
||||
int setFlowTime;
|
||||
int expireTime;
|
||||
DoubleBox msrdFlow;
|
||||
DoubleBox flowBox;
|
||||
int isFlowOkDuration;
|
||||
|
||||
|
||||
@@ -70,23 +72,23 @@ namespace TBF.Rig.Uni.RegValve
|
||||
/// Events: FlowSet, FlowTimeOut
|
||||
/// </summary>
|
||||
/// <param name="uniCB">Control board device</param>
|
||||
/// <param name="regValve">Regulation valve component</param>
|
||||
/// <param name="regulValve">Regulation valve component</param>
|
||||
/// <param name="flowMeter">Flowmeter component</param>
|
||||
/// <param name="qFrom">Lower limit of the flow to be achieved in [m3/h]</param>
|
||||
/// <param name="qTo">Upper limit of the flow to be achieved in [m3/h]</param>
|
||||
/// <param name="msrdFlow">DoubleBox for measured flow</param>
|
||||
/// <param name="pidCoef">PID coefficient (float)</param>
|
||||
/// <param name="timeout">Timeout for the flow setting in [s]</param>
|
||||
/// <param name="delay">Flow setting starts after this delay in [s]</param>
|
||||
/// <param name="delay">Flow setting starts after this delay [s]</param>
|
||||
/// <param name="leaveFlowControlRunning">true = Leave the measurement running after op. stop</param>
|
||||
/// <remarks>Only Elde.Valve flow are used, other flow on the lists are ignored</remarks>
|
||||
public SetFlowOp(UniCB uniCB, IRegValve regValve, IFlowMeter flowMeter, double qFrom, double qTo, DoubleBox msrdFlow,
|
||||
public SetFlowOp(UniCB uniCB, IRegValve regValve, IFlowMeter flowMeter, double qFrom, double qTo, DoubleBox flowBox,
|
||||
int timeout, int delay, bool leaveFlowControlRunning)
|
||||
{
|
||||
this.uniCB = uniCB;
|
||||
if (this.uniCB == null) throw new ArgumentNullException("Control board is null or not Uni");
|
||||
this.uniCB = uniCB;
|
||||
if (this.uniCB == null) throw new ArgumentNullException("cBoard is null or not Uni");
|
||||
|
||||
this.regV = regValve as RegValve;
|
||||
if (this.regV == null) throw new ArgumentNullException(string.Format("{0} is not Uni.RegValve", regValve.Name));
|
||||
if (this.regV == null) throw new ArgumentNullException("regValve is null or not Uni");
|
||||
|
||||
this.flowMeter = flowMeter;
|
||||
if (this.flowMeter == null) throw new ArgumentNullException("flowMeter");
|
||||
@@ -96,14 +98,15 @@ namespace TBF.Rig.Uni.RegValve
|
||||
double rqrdFlowLoBeforeCorr = qFrom - MeasurementCorrection.GetCorrection(qFrom, flowMeter.Corrections);
|
||||
double rqrdFlowHiBeforeCorr = qTo - MeasurementCorrection.GetCorrection(qTo, flowMeter.Corrections);
|
||||
targetFlowAve = (rqrdFlowLoBeforeCorr + rqrdFlowHiBeforeCorr) / 2;
|
||||
shrinkedTgtFlowLo = (RqrdFlowRangeRatio * rqrdFlowLoBeforeCorr)
|
||||
+ ((1 - RqrdFlowRangeRatio) * targetFlowAve); /// Move the lower limit 15% of the range up
|
||||
shrinkedTgtFlowHi = (RqrdFlowRangeRatio * rqrdFlowHiBeforeCorr)
|
||||
+ ((1 - RqrdFlowRangeRatio) * targetFlowAve); /// Move the upper limit 15% of the range down
|
||||
shrinkedTgtFlowLo = (RqrdFlowRangeRatio * rqrdFlowLoBeforeCorr) + ((1 - RqrdFlowRangeRatio) * targetFlowAve); /// Move the lower limit 15% of the range up
|
||||
shrinkedTgtFlowHi = (RqrdFlowRangeRatio * rqrdFlowHiBeforeCorr) + ((1 - RqrdFlowRangeRatio) * targetFlowAve); /// Move the upper limit 15% of the range down
|
||||
|
||||
this.flowBox = flowBox;
|
||||
if (this.flowBox == null) throw new ArgumentNullException("flowBox");
|
||||
|
||||
this.msrdFlow = msrdFlow;
|
||||
this.timeout = timeout;
|
||||
this.delay = delay;
|
||||
|
||||
this.leaveFlowControlRunning = leaveFlowControlRunning;
|
||||
|
||||
log.Debug(this.ToString());
|
||||
@@ -169,13 +172,16 @@ namespace TBF.Rig.Uni.RegValve
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>Start this operation</summary>
|
||||
/// <summary>
|
||||
/// Start this operation
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
log.InfoFormat("SetFlowOp:Start() rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3}",
|
||||
regV.Idx1, flowMeter.Idx1, currentReqFlowLo, currentReqFlowHi);
|
||||
|
||||
/// Store the current time, etc.
|
||||
startTime = StateMachine.Time;
|
||||
setFlowTime = StateMachine.Time + delay;
|
||||
expireTime = StateMachine.Time + timeout;
|
||||
if (expireTime < 0) expireTime = int.MaxValue;
|
||||
@@ -273,16 +279,17 @@ namespace TBF.Rig.Uni.RegValve
|
||||
///
|
||||
double frequency = flowMeter.ReadFrequency();
|
||||
double flow = flowMeter.ReadFlow();
|
||||
if (msrdFlow != null && flow != 0) msrdFlow.Val = flow;
|
||||
if (flow != 0) flowBox.Val = flow;
|
||||
|
||||
if (currentReqFlowLo <= flow && flow <= currentReqFlowHi)
|
||||
if ((currentReqFlowLo <= flow) && (flow <= currentReqFlowHi))
|
||||
{
|
||||
isFlowOkDuration++; /// Increment the flow within range duration
|
||||
/// Flow is within range
|
||||
isFlowOkDuration++;
|
||||
|
||||
if (isFlowOkDuration >= regV.FlowStableSec)
|
||||
{
|
||||
/// Flow is within range for sufficiently long time
|
||||
if (regV.StoredPositionReuse)
|
||||
if (regV.regValveCfg.StoredPositionReuse)
|
||||
{
|
||||
double rvPosition = regV.Position; /// Read the current position
|
||||
double storedPosition;
|
||||
@@ -329,12 +336,13 @@ namespace TBF.Rig.Uni.RegValve
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Stop this operation</summary>
|
||||
/// <summary>Start this operation</summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (!leaveFlowControlRunning)
|
||||
{
|
||||
uniCB.StopFlowControl(false, regV.Idx1); /// Stop the flow measurement
|
||||
/// Stop flow measurement
|
||||
uniCB.StopFlowControl(false, regV.Idx1);
|
||||
}
|
||||
|
||||
opState = OpState.Idle;
|
||||
|
||||
+24
-8
@@ -71,7 +71,7 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;CAMERA;LANG_PL;IPERL;</DefineConstants>
|
||||
<DefineConstants>TRACE;CAMERA;LANG_PL;IPERL;LIVELOGDIAG_FlowMeter_cs;LIVELOGDIAG_RegValve_cs</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -1049,7 +1049,9 @@
|
||||
<Compile Include="Rig\Operations\LargeMessageBoxOp.cs" />
|
||||
<Compile Include="Rig\Operations\ReturnGivenEventOp.cs" />
|
||||
<Compile Include="Rig\Output\DB\DatabaseWriter\Factory.cs" />
|
||||
<Compile Include="Rig\Output\DB\DatabaseWriter\WriteDBCfgCtrl.cs" />
|
||||
<Compile Include="Rig\Output\DB\DatabaseWriter\WriteDBCfgCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\Output\DB\DatabaseWriter\WriteDBCfgCtrl.designer.cs" />
|
||||
<Compile Include="Rig\Output\DB\DatabaseWriter\WriterCfg.cs" />
|
||||
<Compile Include="Rig\Output\DB\DatabaseWriter\WritingToDB.cs" />
|
||||
@@ -1386,12 +1388,16 @@
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Utils\SerialDriver.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Utils\SerialDriverBuilder.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\Factory.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisHeadTestCtrl.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisHeadTestCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisHeadTestCtrl.Designer.cs">
|
||||
<DependentUpon>GenesisHeadTestCtrl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisCfg.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisCfgCtrl.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisCfgCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisCfgCtrl.designer.cs">
|
||||
<DependentUpon>GenesisCfgCtrl.cs</DependentUpon>
|
||||
</Compile>
|
||||
@@ -1410,9 +1416,13 @@
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\Factory.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\IPerlASICImplHeadTestCtrl.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\SmartReader.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\IperlASICUniHeadTestCtrl.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\IperlASICUniHeadTestCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\IperlASICUniHeadTestCtrl.Designer.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\IPerlUniCfgCtrl.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\IPerlUniCfgCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\IPerlUniCfgCtrl.designer.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlReaderUNI\common\IUniHeadTestCtrl.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlReaderUNI\Factory.cs" />
|
||||
@@ -1484,6 +1494,8 @@
|
||||
<DependentUpon>RRCfgCtrl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<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\JsonDataFromPoseidon.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonCfg.cs" />
|
||||
@@ -1715,7 +1727,9 @@
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\Factory.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHead.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfg.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfgCtrl.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfgCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfgCtrl.designer.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\OptoReceivedEventArgs.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\OptoTelegramRaw.cs" />
|
||||
@@ -1771,7 +1785,9 @@
|
||||
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\Utils\ISerialDriver.cs" />
|
||||
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\Utils\SerialDriver.cs" />
|
||||
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\Utils\SerialDriverBuilder.cs" />
|
||||
<Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationForm.cs" />
|
||||
<Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationForm.designer.cs" />
|
||||
<Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationParams.cs" />
|
||||
<Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationSeq.cs" />
|
||||
|
||||
@@ -189,8 +189,16 @@ namespace TBF.UI.Bench.Components
|
||||
int extraWidth = splitContainer.Panel2.Width + 40;
|
||||
int extraHeight = 80;
|
||||
|
||||
this.Width = desired.Width + extraWidth;
|
||||
this.Height = desired.Height + extraHeight;
|
||||
if (!desired.IsEmpty)
|
||||
{
|
||||
this.Width = desired.Width + extraWidth;
|
||||
this.Height = desired.Height + extraHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
//this.Width += extraWidth;
|
||||
//this.Height += extraHeight;
|
||||
}
|
||||
|
||||
// Do not exceed screen working area
|
||||
var screen = Screen.FromControl(this).WorkingArea;
|
||||
|
||||
@@ -43,7 +43,10 @@ namespace TBF.UI.Bench.Components
|
||||
{
|
||||
availCmpntsLstBox.SelectedIndex = selectedIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// SharedDlgSearchForListBox configuration
|
||||
sharedSearchForListBox1.ListBoxEx = availCmpntsLstBox;
|
||||
}
|
||||
|
||||
void Localize()
|
||||
{
|
||||
|
||||
+53
-100
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
||||
@@ -14,10 +15,16 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
public class GenesisReaderTests
|
||||
{
|
||||
|
||||
private static GenesisCfg CreateCfg()
|
||||
{
|
||||
Factory factory = new Factory();
|
||||
return new GenesisCfg(factory);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Debug_ProcessOptoLine_FormatVariants()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
var reader = new GenesisSmartReader(CreateCfg());
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
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"
|
||||
};
|
||||
|
||||
int[] counts = new int[variants.Length];
|
||||
|
||||
for (int i = 0; i < variants.Length; i++)
|
||||
{
|
||||
bool blockCompleted = false;
|
||||
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($" blockCompleted={blockCompleted}");
|
||||
Console.WriteLine($" optoDataCount={optoDataCount}");
|
||||
Console.WriteLine($" optoDataCount={counts[i]}");
|
||||
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]
|
||||
public void RealInput_ShouldCalculate_StartEndVolumes_AndTimes()
|
||||
public void RealInput_ShouldParseCalibrationTelegrams()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
var reader = new GenesisSmartReader(CreateCfg());
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
string[] realInputLines = LoadRealInputLines();
|
||||
@@ -110,87 +119,33 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
Console.WriteLine($"Final firstValidIx = {firstValidIx}");
|
||||
Console.WriteLine($"Final lastValidIx = {lastValidIx}");
|
||||
|
||||
if (processedCount <= 0)
|
||||
{
|
||||
Assert.Fail(
|
||||
"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.");
|
||||
}
|
||||
Assert.AreEqual(3, processedCount, "Expected all 3 calibration telegrams to be parsed.");
|
||||
Assert.AreEqual(0, firstValidIx);
|
||||
Assert.AreEqual(2, lastValidIx);
|
||||
|
||||
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.TestEndTelegramIx = lastValidIx;
|
||||
|
||||
Console.WriteLine("=== BEFORE POST PROCESSING ===");
|
||||
Console.WriteLine($"TestStartTelegramIx = {reader.TestStartTelegramIx}");
|
||||
Console.WriteLine($"TestEndTelegramIx = {reader.TestEndTelegramIx}");
|
||||
InvokePrivate(reader, "AddTestStartEndMarksToData", new object[] { 0, 0 });
|
||||
InvokePrivate(reader, "DataStreamPostProcessing");
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelData");
|
||||
|
||||
object[] markArgs = { 0, 0 };
|
||||
|
||||
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");
|
||||
Assert.IsTrue(reader.NoSamples,
|
||||
"With only one telegram per channel, recalculated start/end samples should still be unavailable.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RealInput_ShouldSupport_RolloverNormalization()
|
||||
public void RealInput_ShouldParseRolloverInputLines()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
var reader = new GenesisSmartReader(CreateCfg());
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
string[] realInputLines = LoadRealInputLinesWithRollover();
|
||||
@@ -211,29 +166,25 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
int finalOptoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||
|
||||
Assert.IsTrue(
|
||||
finalOptoDataCount > 1,
|
||||
"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");
|
||||
Assert.AreEqual(3, finalOptoDataCount,
|
||||
"Expected all provided rollover input lines to be parsed as telegrams.");
|
||||
}
|
||||
|
||||
[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)
|
||||
{
|
||||
const int channelCount = 3;
|
||||
@@ -264,6 +215,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
SetPrivateField(reader, "partOfTelegram", string.Empty);
|
||||
SetPrivateField(reader, "startDataProcessing", true);
|
||||
|
||||
reader.StopQueueData = false;
|
||||
|
||||
reader.TestStartTelegramIx = 0;
|
||||
reader.TestEndTelegramIx = 0;
|
||||
}
|
||||
|
||||
+10
-26
@@ -133,13 +133,13 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
InvokePrepareCalculatedChannelData(reader);
|
||||
|
||||
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(20.0, reader.TimestampSecEnd, 1e-9);
|
||||
|
||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartRaw, 1e-9);
|
||||
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
|
||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartAverage, 1e-9);
|
||||
Assert.AreEqual(219.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -162,11 +162,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
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[28].Flags = OptoTelegramFlags.InvalidTelegram;
|
||||
|
||||
@@ -177,14 +172,14 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
InvokePrepareCalculatedChannelData(reader);
|
||||
|
||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
|
||||
Assert.AreEqual(208.33333333333334, reader.VolumeLtrEnd, 1e-9);
|
||||
Assert.AreEqual(208.33333333333334, reader.VolumeLtrEndRaw, 1e-9);
|
||||
Assert.AreEqual(209.666666666667, reader.VolumeLtrEnd, 1e-9);
|
||||
Assert.AreEqual(209.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
|
||||
|
||||
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
|
||||
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 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(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.TimestampSecEndCh3, 1e-9);
|
||||
|
||||
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 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(219.666666666667, reader.VolumeLtrEnd, 1e-9);
|
||||
|
||||
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
||||
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
|
||||
|
||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartRaw, 1e-9);
|
||||
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 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);
|
||||
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartAverage, 1e-9);
|
||||
Assert.AreEqual(219.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -313,7 +297,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
InvokePrepareCalculatedChannelData(reader);
|
||||
|
||||
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(20.0, reader.VolumeLtrStartCh2, 1e-9);
|
||||
|
||||
+18
-8
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
||||
@@ -36,6 +38,15 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
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)
|
||||
{
|
||||
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.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.VolumeLtrStartRaw, 1e-9,
|
||||
"Expected post-processing to prepare raw start values.");
|
||||
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRaw, 1e-9,
|
||||
"Expected post-processing to prepare raw end values.");
|
||||
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartAverage, 1e-9,
|
||||
"Expected post-processing to prepare raw/recalculated start values.");
|
||||
Assert.AreEqual((15.0 + 25.0 + 36.0) / 3.0, reader.VolumeLtrEndAwerage, 1e-9,
|
||||
"Expected post-processing to prepare recalculated end values.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -111,6 +121,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.optoSerialPort = fake;
|
||||
|
||||
reader.Stop();
|
||||
WaitForStopToFinish(reader);
|
||||
|
||||
var finalState = GetPrivateField<DataStreamState>(reader, "dataStreamState");
|
||||
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.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
|
||||
|
||||
// Should still be default because DataStreamPostProcessing() must NOT run.
|
||||
Assert.AreEqual(0.0, reader.VolumeLtrStartRaw, 1e-9,
|
||||
Assert.AreEqual(0.0, reader.VolumeLtrStartAverage, 1e-9,
|
||||
"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.");
|
||||
}
|
||||
|
||||
|
||||
+107
-76
@@ -79,6 +79,15 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
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]
|
||||
public void Start_ShouldOpenPort_AndEnableProcessing()
|
||||
{
|
||||
@@ -129,9 +138,10 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.TestEndTelegramIx = 1;
|
||||
|
||||
reader.Stop();
|
||||
WaitForStopToFinish(reader);
|
||||
|
||||
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.optoSerialPort = fake;
|
||||
|
||||
InitializeThreeChannelState(reader);
|
||||
EnableProcessingLoopForTests(reader);
|
||||
|
||||
bool 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;
|
||||
InitializeThreeChannelState(reader);
|
||||
EnableProcessingLoopForTests(reader);
|
||||
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
||||
|
||||
@@ -254,6 +268,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
reader.optoSerialPort = fake;
|
||||
InitializeThreeChannelState(reader);
|
||||
EnableProcessingLoopForTests(reader);
|
||||
|
||||
var line =
|
||||
"@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]
|
||||
[DataRow("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", 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)]
|
||||
[DataRow(
|
||||
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
||||
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)
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
@@ -277,6 +298,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.Initialize();
|
||||
reader.optoSerialPort = fake;
|
||||
|
||||
InitializeThreeChannelState(reader);
|
||||
EnableProcessingLoopForTests(reader);
|
||||
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
||||
|
||||
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
|
||||
@@ -284,27 +308,25 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
||||
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||
|
||||
Assert.AreEqual(1, optoDataCount, "Exactly one telegram should be inserted.");
|
||||
Assert.AreEqual(1, optoDataCount);
|
||||
|
||||
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(0, inserted.Counter, "First inserted telegram should have counter 0.");
|
||||
Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags, "Inserted telegram should be marked OK.");
|
||||
Assert.AreNotEqual(default(DateTime), inserted.DateTime, "Inserted telegram DateTime should be initialized.");
|
||||
Assert.AreEqual(expectedChannelIndex, inserted.IChannel());
|
||||
Assert.AreEqual(0, inserted.Counter);
|
||||
Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags);
|
||||
|
||||
Assert.AreNotEqual(0.0, inserted.VolumeRaw, "Inserted telegram VolumeRaw should be set.");
|
||||
Assert.AreNotEqual(0.0, inserted.VolumeRawExt, "Inserted telegram VolumeRawExt should be set.");
|
||||
Assert.AreNotEqual(0.0, inserted.Timestamp, "Inserted telegram Timestamp should be set.");
|
||||
Assert.AreNotEqual(0.0, inserted.TimestampExt, "Inserted telegram TimestampExt should be set.");
|
||||
Assert.AreNotEqual(0.0, inserted.VolumeRawExt);
|
||||
Assert.AreNotEqual(0.0, inserted.TimestampExt);
|
||||
|
||||
for (int i = 0; i < ChannelCount; i++)
|
||||
{
|
||||
if (i == expectedChannelIndex)
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -313,11 +335,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9,
|
||||
"Inserted telegram VolumeRawExt should match updated channel cache.");
|
||||
|
||||
Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9,
|
||||
"Inserted telegram TimestampExt should match updated channel cache.");
|
||||
Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9);
|
||||
Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -344,13 +363,16 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
InvokePrepareCalculatedChannelData(reader);
|
||||
|
||||
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(105.0, reader.TimestampSecEnd, 1e-9);
|
||||
|
||||
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRaw, 1e-9);
|
||||
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRaw, 1e-9);
|
||||
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartAverage, 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)
|
||||
@@ -388,7 +410,21 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
InvokePrepareCalculatedChannelData(reader);
|
||||
|
||||
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]
|
||||
@@ -425,39 +461,37 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadOptoData_FullBlock_ShouldDiscardBuffersAfterClosingF()
|
||||
public void ProcessingLoop_FullBlock_ShouldDiscardBuffersAfterClosingF()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
|
||||
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");
|
||||
fake.Open();
|
||||
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
reader.Initialize();
|
||||
|
||||
fake.Open();
|
||||
reader.optoSerialPort = fake;
|
||||
reader.ResetAfterBlockRepetitions = 1;
|
||||
|
||||
var readMethod = typeof(GenesisSmartReader).GetMethod(
|
||||
"ReadOptoData",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
new[] { typeof(DataStreamState) },
|
||||
null);
|
||||
InitializeThreeChannelState(reader);
|
||||
EnableProcessingLoopForTests(reader);
|
||||
|
||||
Assert.IsNotNull(readMethod);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
reader.StartProcessingLoop();
|
||||
try
|
||||
{
|
||||
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.");
|
||||
Assert.AreEqual(1, fake.DiscardOutCalls, "Output buffer should be discarded once after completed block.");
|
||||
Thread.Sleep(300);
|
||||
|
||||
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]
|
||||
@@ -471,10 +505,13 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.optoSerialPort = fake;
|
||||
reader.ResetAfterBlockRepetitions = 1;
|
||||
|
||||
InitializeThreeChannelState(reader);
|
||||
EnableProcessingLoopForTests(reader);
|
||||
|
||||
bool 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);
|
||||
Assert.IsFalse(reset);
|
||||
@@ -529,24 +566,24 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.optoSerialPort = fake;
|
||||
reader.ResetAfterBlockRepetitions = 3;
|
||||
|
||||
InitializeThreeChannelState(reader);
|
||||
EnableProcessingLoopForTests(reader);
|
||||
|
||||
bool reset;
|
||||
|
||||
for (int repetition = 1; repetition <= 3; repetition++)
|
||||
{
|
||||
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",
|
||||
DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset, "Reset must not happen after @h1, repetition " + repetition);
|
||||
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset);
|
||||
|
||||
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
||||
DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset, "Reset must not happen after @h2, repetition " + repetition);
|
||||
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset);
|
||||
|
||||
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
||||
DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset, "Reset must not happen after @h3, repetition " + repetition);
|
||||
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(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.ResetAfterBlockRepetitions = 5;
|
||||
|
||||
InitializeThreeChannelState(reader);
|
||||
EnableProcessingLoopForTests(reader);
|
||||
|
||||
bool reset;
|
||||
|
||||
for (int repetition = 1; repetition <= 5; repetition++)
|
||||
{
|
||||
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",
|
||||
DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset, "Reset must not happen after @h1, repetition " + repetition);
|
||||
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset);
|
||||
|
||||
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
||||
DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset, "Reset must not happen after @h2, repetition " + repetition);
|
||||
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset);
|
||||
|
||||
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
||||
DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(reset, "Reset must not happen after @h3, repetition " + repetition);
|
||||
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsFalse(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.TimestampSecEndCh3, 0.0001);
|
||||
|
||||
Assert.AreEqual(105.0, sut.VolumeLtrStartRaw, 0.0001);
|
||||
Assert.AreEqual(157.0, sut.VolumeLtrEndRaw, 0.0001);
|
||||
Assert.AreEqual(105.0, sut.VolumeLtrStartAverage, 0.0001);
|
||||
Assert.AreEqual(157.0, sut.VolumeLtrEndAwerage, 0.0001);
|
||||
|
||||
Assert.AreEqual(10.0, sut.TimestampSecStart, 0.0001);
|
||||
Assert.AreEqual(20.0, sut.TimestampSecEnd, 0.0001);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void METHOD()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+260
-28
@@ -10,22 +10,33 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
[TestClass]
|
||||
public class GenesisSmartReader_Q3Test
|
||||
{
|
||||
private static readonly double[] ValidInitFactors = { 15625.0, 15625.0, 15625.0 };
|
||||
|
||||
[TestMethod]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRawDataIsNull()
|
||||
public void GetQ3Calibration_ShouldRemainInvalid_WhenRawDataIsNull()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
InitializeReaderForQ3Test(sut);
|
||||
|
||||
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.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]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenNoSamplesConditionIsTrue()
|
||||
public void GetQ3Calibration_ShouldRemainInvalid_WhenNoSamplesIsTrue()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
InitializeReaderForQ3Test(sut);
|
||||
@@ -34,69 +45,255 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
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.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]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRefVolumeIsZero()
|
||||
public void GetQ3Calibration_ShouldRemainInvalid_WhenRefVolumeIsZero()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
|
||||
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.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]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRefTimeIsZero()
|
||||
public void GetQ3Calibration_ShouldRemainInvalid_WhenRefTimeIsZero()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
|
||||
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.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]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenInitCalibFactorIsZero()
|
||||
public void GetQ3Calibration_ShouldMarkInvalid_WhenInitFactorsAreZero()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
|
||||
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.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]
|
||||
public void GetQ3Calibration_ShouldProduceNonNegativeResult_WithPreparedSimulationData()
|
||||
public void CalculateQ3Calibration_ShouldPopulatePerChannelValues_WithPreparedSimulationData()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
|
||||
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)
|
||||
@@ -115,20 +312,55 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
{
|
||||
new[]
|
||||
{
|
||||
CreateRecord(0.0, 0.0),
|
||||
CreateRecord(15625.0, 120.0)
|
||||
CreateRecord(0.0, 0.0, 1),
|
||||
CreateRecord(15625.0, 120.0, 1)
|
||||
},
|
||||
new OptoTelegramRaw[2],
|
||||
new OptoTelegramRaw[2]
|
||||
new[]
|
||||
{
|
||||
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
|
||||
{
|
||||
VolumeRawExt = volumeRawExt,
|
||||
TimestampExt = timestampExt
|
||||
TimestampExt = timestampExt,
|
||||
iChannel = channel,
|
||||
Flags = OptoTelegramFlags.OK
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,35 +12,36 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
[TestSubject(typeof(CliRunner))]
|
||||
public class CliRunnerTest
|
||||
{
|
||||
|
||||
[TestMethod]
|
||||
public void ExtractJson_Test()
|
||||
{
|
||||
String allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
|
||||
string allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
string json = cliRunner.ExtractJson(allOutput);
|
||||
|
||||
Assert.IsTrue(!string.IsNullOrEmpty(json));
|
||||
|
||||
Assert.IsFalse(string.IsNullOrEmpty(json));
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
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);
|
||||
string json = cliRunner.ExtractJson(allOutput);
|
||||
|
||||
Assert.IsTrue(!string.IsNullOrEmpty(json));
|
||||
|
||||
Assert.IsFalse(string.IsNullOrEmpty(json));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
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);
|
||||
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.IsNotNull(dto);
|
||||
Assert.AreEqual("1000000267", dto.DeviceId);
|
||||
@@ -53,120 +54,165 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
[TestMethod]
|
||||
public void RunMultipleTimesTestProgram_CheckParalelWork()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74);
|
||||
|
||||
// Check if the executable exists in current directory
|
||||
SerialPortData serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
|
||||
|
||||
CliRunner cliRunnerOne = new CliRunner(false);
|
||||
DateTime StartTime = DateTime.Now;
|
||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
DateTime startTime = DateTime.Now;
|
||||
|
||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
cliRunnerOne.WaitAll();
|
||||
TimeSpan OneEndTime = DateTime.Now - StartTime;
|
||||
|
||||
StartTime = DateTime.Now;
|
||||
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
|
||||
|
||||
TimeSpan oneEndTime = DateTime.Now - startTime;
|
||||
|
||||
startTime = DateTime.Now;
|
||||
Console.WriteLine($"Start Time Loop: {startTime:yyyy-MM-dd HH:mm:ss}");
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
}
|
||||
|
||||
cliRunner.WaitAll();
|
||||
|
||||
DateTime EndTime = DateTime.Now;
|
||||
Console.WriteLine($"End Time Loop: {EndTime.ToString("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} " +
|
||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||
DateTime endTime = DateTime.Now;
|
||||
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} " +
|
||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||
Console.WriteLine($"Total tasks: {cliRunner.TaskPool.Count}");
|
||||
|
||||
|
||||
foreach (Task<JsonDataFromPoseidon> task in cliRunner.TaskPool)
|
||||
|
||||
foreach (CliTaskInfo taskInfo 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;
|
||||
Console.WriteLine($"Result: {jsonDataFromPoseidon.DeviceId}");
|
||||
Console.WriteLine($"Result: {jsonDataFromPoseidon?.DeviceId}");
|
||||
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]
|
||||
public async Task RunMultipleTimesTestProgram_CheckParalelWorkCumulative()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74);
|
||||
|
||||
// Check if the executable exists in current directory
|
||||
SerialPortData serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
List<CliRunner> cliRunnerList = new List<CliRunner>();
|
||||
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
cliRunnerList.Add(new CliRunner(false));
|
||||
|
||||
}
|
||||
|
||||
CliRunner cliRunnerOne = new CliRunner(false);
|
||||
DateTime StartTime = DateTime.Now;
|
||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
DateTime startTime = DateTime.Now;
|
||||
|
||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
cliRunnerOne.WaitAll();
|
||||
TimeSpan OneEndTime = DateTime.Now - StartTime;
|
||||
|
||||
StartTime = DateTime.Now;
|
||||
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
|
||||
TimeSpan oneEndTime = DateTime.Now - startTime;
|
||||
|
||||
startTime = DateTime.Now;
|
||||
Console.WriteLine($"Start Time Loop: {startTime:yyyy-MM-dd HH:mm:ss}");
|
||||
|
||||
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
|
||||
{
|
||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
|
||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
|
||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for ALL tasks from ALL runners
|
||||
var allTasks = cliRunnerList.SelectMany(r => r.TaskPool).ToArray();
|
||||
Task[] allTasks = cliRunnerList
|
||||
.SelectMany(r => r.TaskPool)
|
||||
.Where(ti => ti != null && ti.Task != null)
|
||||
.Select(ti => ti.Task)
|
||||
.ToArray();
|
||||
|
||||
await Task.WhenAll(allTasks);
|
||||
|
||||
DateTime EndTime = DateTime.Now;
|
||||
Console.WriteLine($"End Time Loop: {EndTime.ToString("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} " +
|
||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||
DateTime endTime = DateTime.Now;
|
||||
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} " +
|
||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||
Console.WriteLine($"Total tasks: {cliRunnerList.Sum(runner => runner.TaskPool.Count)}");
|
||||
|
||||
|
||||
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; 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;
|
||||
results += ($" ID: {jsonDataFromPoseidon.DeviceId}");
|
||||
results += $" ID: {jsonDataFromPoseidon?.DeviceId}";
|
||||
Assert.IsNotNull(jsonDataFromPoseidon);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail(
|
||||
$"Task did not complete successfully. Runner={iCliRunner}, State={taskInfo.State}, Status={taskInfo.Task.Status}");
|
||||
}
|
||||
}
|
||||
|
||||
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\TouchReadFrameBuilderTest.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\PoseidonReader\UniHeadTestCtrlTest.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />
|
||||
|
||||
Reference in New Issue
Block a user