Compare commits

...
Author SHA1 Message Date
michal 842f009b42 Increment assembly version to 3.9.3058.1 and fix logging format issue in FlowMeter initialization. 2026-04-21 07:43:35 +02:00
michal 7f30ca0cd1 Merge branch 'develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode_morisville
# Conflicts:
#	TBF/Properties/AssemblyInfo.cs
#	TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs
2026-04-20 14:25:13 +02:00
michal 93e207c211 Update GenesisSmartReader for refined start/end marker logic, add _endDeltaSeconds property, and improve telegram index resolution methods. Increment assembly version to 3.9.3031.1. 2026-04-20 13:25:25 +02:00
marekf 64cafc088a Merge branch 'bugfix/Flow-meter-in-parallel-reverse-bug' into develop/SLM-PT50 2026-04-20 12:45:25 +02:00
marekf 8a0da27a07 Fixed RegValve + FlowMetersInParallel - incorrect NominalFreq sum 2026-04-20 12:39:51 +02:00
michal 5a8478e782 Refactor GenesisSmartReader Q3 calibration tests and logic:
- Expand Q3 calibration tests with diverse scenarios in `GenesisSmartReader_Q3Test`.
- Introduce channel-specific validation for calibration tests.
- Adjust methods for Q3 calibration in `GenesisSmartReader` to support per-channel factors.
- Add utilities for preparing simulation data and refinements for test consistency.
- Update `CliRunner` with improved task management, timeout handling, and logging enhancements.
2026-04-17 16:05:16 +02:00
marekf 368cd95114 Bug (fixing) RegValve OK, FlowMeterInParallel NOK 2026-04-15 10:50:27 +02:00
michal 33ef914984 Update GenesisReaderTests: add real input parsing, format variation handling, and rollover cases; remove outdated Q3 calibration tests; increment version to 3.9.3056.1 2026-04-15 09:56:54 +02:00
michal 3eb0b87f0d Add Q3 calibration tests for GenesisSmartReader and refactor test cases:
- Introduce new `GenesisSmartReader_Q3Test` class with comprehensive Q3 calibration scenarios.
- Refactor existing tests to utilize simulation data for better consistency and validation.
- Adjust Q3 calibration logic in `GenesisSmartReader` to enhance calculation accuracy and validation criteria.
2026-04-15 09:22:09 +02:00
michal 711dfc03ef Fix Q3 calibration logic in GenesisSmartReader: replace _rawStartEndByChannel with recalculatedVariablesByChannel for accurate data computation. 2026-04-14 15:54:28 +02:00
michal 93459ccfab Refactor water meter handling in SequenceBase and FlyingStartMassCollectionSeq:
- Enhance logic for enabling/disabling channels based on water meter data.
- Introduce Q3 calibration validation in `GenesisSmartReader` with configurable thresholds.
- Update `GenesisCfgCtrl` UI to disable certain inputs.
2026-04-14 15:41:13 +02:00
michal 009aefb7c9 Refactor GenesisSmartReader and SequenceBase logic, enhance channel handling in water meters, and update GUI components:
- Adjust `Disabled` logic for water meters in `SequenceBase` allowing conditional enabling of channels based on serial number.
- Introduce `EnableShowChanels` property in `GenesisSmartReader` and integrate into `FlyingStartMassCollectionSeq`.
- Modify `StartupFlushMs` to derive configuration dynamically.
- Update `GenesisCfgCtrl` with a checkbox for enabling channels and input for flush timing.
- Increment assembly version to `3.9.3054.1`.
2026-04-14 08:03:02 +02:00
michal d2e18d615d Bump assembly version to 3.9.3053.1. 2026-04-13 14:17:56 +02:00
michal 69b475eb77 Refactor GenesisSmartReader for improved simulation and testing:
- Add `PrepareCalculatedChannelDataSimulationForTest` and supporting utility methods for generating and cloning test data.
- Update `StartupFlushMs` and sampling intervals for increased flexibility.
- Revise water meter handling logic in `FlyingStartMassCollectionSeq` to support multiple channels.
2026-04-13 14:17:34 +02:00
michal 2210bb8240 Enhance FlyingStartMassCollectionSeq with GenesisSmartReader support; add water meter handling logic and bump assembly version to 3.9.3050.1. 2026-04-11 21:55:34 +02:00
marekf 3376ac41b4 Merge branch 'develop/SLM-PT50_genesisDirectDecode' of http://87.197.120.129:33300/michal/tbf into develop/SLM-PT50 2026-04-11 11:54:37 +02:00
marekf f9181fee74 Fixed SelectComponentDlg > missing assignment sharedSearchForListBox1.ListBoxEx = availCmpntsLstBox, because of this the application crashes 2026-04-11 11:04:00 +02:00
michal 65ab19290f simulate mode - flow reached 2026-04-09 12:49:00 +02:00
michal 94c7279da1 Add localized string resources for "Test in calculation" and integrate it into FlyingStartMassCollectionSeq
- Add `Test_in_calculation` localized strings to `.resx` files for English, Slovak, and Czech.
- Update `FlyingStartMassCollectionSeq` to display the new activity message during the calculation phase.
2026-04-09 10:08:50 +02:00
michal 2136695b80 Fix Stop 2026-04-08 16:37:27 +02:00
michal c0f9b0ffa0 Refactor GenesisSmartReader stop logic for enhanced thread safety and post-processing; bump assembly version to 3.9.3047.1. 2026-04-08 15:44:59 +02:00
michal c4ef22436f Update GenesisSmartReader Stop logic and bump assembly version to 3.9.3045.1:
- Refactor `Stop` for improved thread safety using `lock` on `_stateSync` and `_stopSync`.
- Enhance `Stop` behavior by introducing state updates during immediate stop requests.
- Fix redundant operations and reorganize log and processing statements for clarity.
- Update `RequestImmediateStopInternal` to accept a new state parameter.
2026-04-08 15:06:45 +02:00
michal 0b73871518 Refactor GenesisSmartReader and add advanced test utilities:
- Introduce `ForceProcessingEnabled`, `InstallSuccessfulProcessingOverride`, and `EnqueueIncomingLine` helpers to streamline test setup.
- Add new test suite `GenesisSmartReaderStopTests` to validate `Stop` behavior for different processing states.
- Enhance `GenesisSmartReader` state management with a dedicated `Reset` method and improved `Stop` logic.
- Improve buffer initialization in `ProcessOptoLine` to address null-value edge cases.
- Update assembly version to `3.9.3043.1`.
2026-04-08 13:51:55 +02:00
michal f7dc6fee5e immediate Stop improve 2026-04-07 16:37:19 +02:00
michal ba848a167d improve 2026-04-07 15:44:32 +02:00
michal dcb0bf9a88 Update GenesisSmartReader logging and bump assembly version to 3.9.3040.1:
- Enhance `Stop` logs with `registerReader` and `optoSerialPort` details for improved traceability.
2026-04-07 15:27:20 +02:00
michal f6433134c1 Add WaitForStartupFlushToFinish and EnableProcessingLoopForTests utilities for improved test reliability:
- Incorporate `WaitForStartupFlushToFinish` in `GenesisSmartReaderThreadedReadTests` to ensure startup flush completion before processing.
- Introduce `EnableProcessingLoopForTests` in `GenesisSmartReaderTest` to streamline test initialization.
- Fix thread-safety issues in startup flush handling and reorder operations in `GenesisSmartReader.Stop`.
- Bump assembly version to `3.9.3039.1`.
2026-04-07 14:45:37 +02:00
michal 7e3df77b5b Refactor GenesisSmartReader processing logic and tests for robust handling:
- Replace property getters with concise expressions in `GenesisSmartReader`.
- Implement FIFO and concurrent producer-consumer unit tests for `ProcessingLoop`.
- Add startup flush mechanism for improved input handling on initialization.
- Refactor processing loop with structured start/stop tasks and cancellation tokens.
- Fix thread-safety issues and enhance logic in `Stop` and `StopDataStreamProcessing`.
- Add internal visibility for tests in `TBFTests` via `InternalsVisibleTo`.
- Update assembly version to `3.9.3038.1`.
2026-04-07 10:42:27 +02:00
marekf ff5954d85b Bugfix FlowMeterInParallel and RegValve OUT OF RANGE regulation part1 2026-04-02 12:02:26 +02:00
michal 08dff1272b Add overflow volume/timestamp handling and related tests for GenesisSmartReader:
- Extend `CalibrationRecord` constructor to include overflow fields (`OverflowVolumeCm`, `OverflowTimeS`).
- Implement additional unit tests in `OptoTelegramRawTest` to validate multi-rollover scenarios and extended calculations.
- Add `GenesisSmartReaderThreadedReadTests` to cover threaded read-loop functionality and serial processing.
- Enhance logging in `Pump` classes (`TurnOff` methods).
- Update project file to include new test files.
2026-04-02 09:25:17 +02:00
marekf c09e266b2c Bugfix ComponentPropertiesDlg frm size 140x80 2026-04-01 12:12:04 +02:00
michal d5c8fd1c8f Refactor GenesisSmartReader with enhanced channel-specific volume and timestamp processing:
- Replace `AverageNullable` with `AverageCachedTime` and introduce `GetCachedVolume`/`GetCachedTime` helpers.
- Add recalibration logic with `_recalculatedStartEndByChannel` and `_rawStartEndByChannel`.
- Implement `PrepareCalculatedChannelData` for refined multi-channel synchronization.
- Add unit tests (`GenesisSmartReaderChannelTests`) for improved coverage and validation of new methods.
- Update `OptoTelegramRaw.Copy` to fix field assignment issues.
2026-03-27 15:12:43 +01:00
michal a34740e838 Update GenesisSmartReaderTest to handle DiscardOutCalls and configure block repetition resets:
- Modify tests to validate `DiscardOutCalls` alongside `DiscardInCalls`.
- Add support for configuring `ResetAfterBlockRepetitions` in test initialization.
2026-03-27 13:19:10 +01:00
michal 836ba5a41f Refactor GenesisSmartReader processing logic and add advanced multi-channel volume and timestamp handling:
- Introduce `TimeDeltaPerChannel` and `GroupRecordsPerChannel` for calculating time and volume deltas across channels.
- Add recalibration logic to synchronize volume and timestamp data with channel-specific maxima.
- Fix `ResetDataBuffer` implementation and replace redundant `ReasetDataBuffer` calls.
- Update handling of flow markers and block completion logic for improved buffer management.
- Add `Copy` method to `OptoTelegramRaw` for efficient cloning and recalculation.
- Bump `AssemblyVersion` and `AssemblyFileVersion` to `3.9.3019.1`.
2026-03-27 13:18:37 +01:00
michal 23c83ab452 Enhance GenesisSmartReader multi-channel processing and add tests for telegram handling:
- Refactor `ProcessOptoLine` logic to support output buffer resets at configurable repetition intervals.
- Add tests:
  - `ProcessOptoLine_Block_f_h1_h2_h3_f_ShouldResetBuffersOnlyAfterLastF`
  - `ReadOptoData_FullBlock_ShouldDiscardBuffersAfterClosingF`
  - `ProcessOptoLine_LastF_AfterH3_ShouldRequestBufferReset`
  - `ProcessOptoLine_ShouldReset
2026-03-26 14:45:36 +01:00
michal bc9e583c29 FIX Add ChannelAverages to project and remove unused imports in GenesisHead. 2026-03-26 07:29:13 +01:00
51 changed files with 12261 additions and 1589 deletions
+5 -2
View File
@@ -19,6 +19,9 @@ using System.Runtime.InteropServices;
// COM, set the ComVisible attribute to true on that type. // COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
//Visibility to TBFTests internal classes
[assembly: InternalsVisibleTo("TBFTests")]
// The following GUID is for the ID of the typelib if this project is exposed to COM // The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2c13538e-15ee-48b2-af0f-2c95afb67186")] [assembly: Guid("2c13538e-15ee-48b2-af0f-2c95afb67186")]
@@ -29,5 +32,5 @@ using System.Runtime.InteropServices;
// Build Number // Build Number
// Revision // Revision
// //
[assembly: AssemblyVersion("3.9.3013.1")] [assembly: AssemblyVersion("3.9.3058.1")]
[assembly: AssemblyFileVersion("3.9.3013.1")] [assembly: AssemblyFileVersion("3.9.3058.1")]
+9 -1
View File
@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // This code was generated by a tool.
// Runtime Version:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@@ -6270,6 +6269,15 @@ namespace TBF.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Test in calculation.
/// </summary>
internal static string Test_in_calculation {
get {
return ResourceManager.GetString("Test_in_calculation", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Test in progress. /// Looks up a localized string similar to Test in progress.
/// </summary> /// </summary>
File diff suppressed because it is too large Load Diff
+3
View File
@@ -1764,4 +1764,7 @@
<data name="Units" xml:space="preserve"> <data name="Units" xml:space="preserve">
<value /> <value />
</data> </data>
<data name="Test_in_calculation" xml:space="preserve">
<value>Probíhá výpočet</value>
</data>
</root> </root>
+3
View File
@@ -2482,4 +2482,7 @@
<data name="Units" xml:space="preserve"> <data name="Units" xml:space="preserve">
<value>Units</value> <value>Units</value>
</data> </data>
<data name="Test_in_calculation" xml:space="preserve">
<value>Test in calculation</value>
</data>
</root> </root>
+3
View File
@@ -259,4 +259,7 @@
<data name="Units" xml:space="preserve"> <data name="Units" xml:space="preserve">
<value /> <value />
</data> </data>
<data name="Test_in_calculation" xml:space="preserve">
<value>Prebieha výpočet</value>
</data>
</root> </root>
+2
View File
@@ -111,6 +111,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
public void TurnOff() public void TurnOff()
{ {
log.DebugFormat("{0}.TurnOff()", Name);
if ((pumpFm1 != null) && (pumpFm2 != null)) if ((pumpFm1 != null) && (pumpFm2 != null))
{ {
pumpFm1.TurnOff(); pumpFm1.TurnOff();
@@ -120,6 +121,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
{ {
// TODO // TODO
} }
log.DebugFormat("DONE {0}.TurnOff()", Name);
} }
@@ -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 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 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, uniCB.SetFlow(false, regV.Idx1, flowMeter.NominalFreq * reqFlowLo / flowMeter.NominalFlow,
2000.0 * reqFlowHi / flowMeter.NominalFlow); flowMeter.NominalFreq * reqFlowHi / flowMeter.NominalFlow);
if (readDivTransition) if (readDivTransition)
{ {
/// Schedule reading diverter transition data /// Schedule reading diverter transition data
+65 -4
View File
@@ -803,12 +803,60 @@ namespace TBF.Rig.ControlBoard.Uni
/// </summary> /// </summary>
public void SetFlow(bool isFromUI, int regVId, double freqLo, double freqHi, int regulationMinStep = 0) 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)); if (IsUIBlocked && isFromUI)
log.InfoFormat("Enqueue( SetFlow(rv={0}, fLo={1}, fHi={2}, pid={3}) )", regVId, freqLo, freqHi, Convert.ToInt32(Math.Round(Devices.PidCoef))); {
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, public void StartTest(bool isFromUI, int flowMId, int divId, int divThreshold,
@@ -981,5 +1029,18 @@ namespace TBF.Rig.ControlBoard.Uni
{ {
/// TODO /// 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));
}
}
} }
} }
+2
View File
@@ -332,6 +332,8 @@ namespace TBF.Rig.Danfoss.VLT2800
Telegram.UpdateTelegramChecksum(msg); Telegram.UpdateTelegramChecksum(msg);
} }
SendData(msg); SendData(msg);
log.DebugFormat("DONE {0}.TurnOff()", Name);
} }
+2
View File
@@ -264,6 +264,8 @@ namespace TBF.Rig.Modbus.PumpFM.DanfossVLT
modbus.SendMessage(msg, Name); modbus.SendMessage(msg, Name);
} }
log.DebugFormat("DONE {0}.TurnOff()", Name);
} }
+2
View File
@@ -416,6 +416,8 @@ namespace TBF.Rig.Modbus.PumpFM.Grundfoss
// --- DIAGNOSTIKA --- // --- DIAGNOSTIKA ---
LiveLogDiag.Log1("GF TurnOff msg = " + BitConverter.ToString(msg)); LiveLogDiag.Log1("GF TurnOff msg = " + BitConverter.ToString(msg));
} }
log.DebugFormat("DONE {0}.TurnOff()", Name);
} }
/// <summary> /// <summary>
@@ -17,8 +17,6 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
return new GenesisCfgCtrl(); return new GenesisCfgCtrl();
} }
/// ///
/// Serialized parameters /// Serialized parameters
/// ///
@@ -31,7 +29,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
public int MuxBoardNr; /// 0 = use RfidComPort(Nr), otherwise mux. board nr. 1 .. 4 public int MuxBoardNr; /// 0 = use RfidComPort(Nr), otherwise mux. board nr. 1 .. 4
public int Group; /// Number written to QuidoRS to connct the watermeter to RfidComPort, 1 .. 10 public int Group; /// Number written to QuidoRS to connct the watermeter to RfidComPort, 1 .. 10
public CommunicationInterface CommunicationInterface; /// Communication Interface: RFID or NFC public CommunicationInterface CommunicationInterface; /// Communication Interface: RFID or NFC
/// public bool EnableShowChanels;
public int IBeginDataFlush;
/// <summary> Procedure parameters </summary> /// <summary> Procedure parameters </summary>
@@ -56,6 +56,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
ProcParams = CreateProcParamsProvider() as ProcParams; ProcParams = CreateProcParamsProvider() as ProcParams;
CommunicationInterface = CommunicationInterface.RFID; CommunicationInterface = CommunicationInterface.RFID;
HeadCommunicationComPortNr = 0; HeadCommunicationComPortNr = 0;
EnableShowChanels = false;
IBeginDataFlush = 2000;
} }
public GenesisCfg(IComponentFactory factory) public GenesisCfg(IComponentFactory factory)
@@ -67,7 +69,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
public string ToString(int i) public string ToString(int i)
{ {
return $"{Name} Group1 (mux#)={MuxBoardNr}, Group2={Group}, Opto=Com{OptoComPortNr}, {CommunicationInterface}=Com{RfidComPortNr}"; return $"{Name} Group1 (mux#)={MuxBoardNr}, Group2={Group}, Opto=Com{OptoComPortNr}, {CommunicationInterface}=Com{RfidComPortNr}, EnableShowChanels={EnableShowChanels}, IBeginDataFlush={IBeginDataFlush}";
} }
} }
} }
@@ -58,6 +58,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
muxBoardNrTextBox.Text = config.MuxBoardNr.ToString(); muxBoardNrTextBox.Text = config.MuxBoardNr.ToString();
groupTextBox.Text = config.Group.ToString(); groupTextBox.Text = config.Group.ToString();
comboBoxCommunicationInterface.SelectedItem = config.CommunicationInterface.ToString(); comboBoxCommunicationInterface.SelectedItem = config.CommunicationInterface.ToString();
tBBeginDataFlush.Text = config.IBeginDataFlush.ToString();
checkBox_EnableShowChanels.Checked = config.EnableShowChanels;
tabPage2.Controls.Add(new GenesisHeadTestCtrl(config)); tabPage2.Controls.Add(new GenesisHeadTestCtrl(config));
} }
@@ -74,6 +77,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
muxBoardNrTextBox.Enabled = true; muxBoardNrTextBox.Enabled = true;
groupTextBox.Enabled = true; groupTextBox.Enabled = true;
comboBoxCommunicationInterface.Enabled = true; comboBoxCommunicationInterface.Enabled = true;
tBBeginDataFlush.Enabled = true;
checkBox_EnableShowChanels.Enabled = true;
} }
public CfgUpdateFlags VerifyCfg(ref string message) public CfgUpdateFlags VerifyCfg(ref string message)
@@ -130,6 +135,12 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
message += Environment.NewLine + string.Format(Strings.Invalid_0, groupLabel.Text); message += Environment.NewLine + string.Format(Strings.Invalid_0, groupLabel.Text);
} }
if (!int.TryParse(tBBeginDataFlush.Text, out dummy) || dummy < 0 || dummy > 10000)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + string.Format(Strings.Invalid_0, tBBeginDataFlush.Text);
}
return flags; return flags;
} }
@@ -158,6 +169,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
config.Group = int.Parse(groupTextBox.Text); config.Group = int.Parse(groupTextBox.Text);
config.CommunicationInterface = (CommunicationInterface)comboBoxCommunicationInterface.SelectedIndex; config.CommunicationInterface = (CommunicationInterface)comboBoxCommunicationInterface.SelectedIndex;
config.HeadCommunicationComPortNr = int.Parse(headPortNrTextBox.Text); config.HeadCommunicationComPortNr = int.Parse(headPortNrTextBox.Text);
config.IBeginDataFlush = int.Parse(tBBeginDataFlush.Text);
config.EnableShowChanels = checkBox_EnableShowChanels.Checked;
return flags; return flags;
} }
@@ -31,382 +31,427 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage(); this.tabPage1 = new System.Windows.Forms.TabPage();
this.label4 = new System.Windows.Forms.Label(); this.groupBox2 = new System.Windows.Forms.GroupBox();
this.label3 = new System.Windows.Forms.Label(); this.headPortNrTextBox = new System.Windows.Forms.TextBox();
this.groupBox1 = new System.Windows.Forms.GroupBox(); this.label2 = new System.Windows.Forms.Label();
this.comboBoxCommunicationInterface = new System.Windows.Forms.ComboBox(); this.label4 = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label();
this.rfidPortNrTextBox = new System.Windows.Forms.TextBox(); this.groupBox1 = new System.Windows.Forms.GroupBox();
this.rfidSerialPortNrLabel = new System.Windows.Forms.Label(); this.comboBoxCommunicationInterface = new System.Windows.Forms.ComboBox();
this.optoDataGroupBox = new System.Windows.Forms.GroupBox(); this.label1 = new System.Windows.Forms.Label();
this.tcpipPortLabel = new System.Windows.Forms.Label(); this.rfidPortNrTextBox = new System.Windows.Forms.TextBox();
this.tcpipPortTextBox = new System.Windows.Forms.TextBox(); this.rfidSerialPortNrLabel = new System.Windows.Forms.Label();
this.ipAddressLabel = new System.Windows.Forms.Label(); this.optoDataGroupBox = new System.Windows.Forms.GroupBox();
this.ipAddressTextBox = new System.Windows.Forms.TextBox(); this.checkBox_EnableShowChanels = new System.Windows.Forms.CheckBox();
this.radioButton1 = new System.Windows.Forms.RadioButton(); this.tBBeginDataFlush = new System.Windows.Forms.TextBox();
this.radioButton2 = new System.Windows.Forms.RadioButton(); this.labelFlush = new System.Windows.Forms.Label();
this.optoSerialPortLabel = new System.Windows.Forms.Label(); this.tcpipPortLabel = new System.Windows.Forms.Label();
this.optoSerialPortTextBox = new System.Windows.Forms.TextBox(); this.tcpipPortTextBox = new System.Windows.Forms.TextBox();
this.groupTextBox = new System.Windows.Forms.TextBox(); this.ipAddressLabel = new System.Windows.Forms.Label();
this.groupLabel = new System.Windows.Forms.Label(); this.ipAddressTextBox = new System.Windows.Forms.TextBox();
this.muxBoardNrTextBox = new System.Windows.Forms.TextBox(); this.radioButton1 = new System.Windows.Forms.RadioButton();
this.muxBoardNrLabel = new System.Windows.Forms.Label(); this.radioButton2 = new System.Windows.Forms.RadioButton();
this.nameTextBox = new System.Windows.Forms.TextBox(); this.optoSerialPortLabel = new System.Windows.Forms.Label();
this.nameLabel = new System.Windows.Forms.Label(); this.optoSerialPortTextBox = new System.Windows.Forms.TextBox();
this.classNameLabel = new System.Windows.Forms.Label(); this.groupTextBox = new System.Windows.Forms.TextBox();
this.tabPage2 = new System.Windows.Forms.TabPage(); this.groupLabel = new System.Windows.Forms.Label();
this.groupBox2 = new System.Windows.Forms.GroupBox(); this.muxBoardNrTextBox = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label(); this.muxBoardNrLabel = new System.Windows.Forms.Label();
this.headPortNrTextBox = new System.Windows.Forms.TextBox(); this.nameTextBox = new System.Windows.Forms.TextBox();
this.tabControl1.SuspendLayout(); this.nameLabel = new System.Windows.Forms.Label();
this.tabPage1.SuspendLayout(); this.classNameLabel = new System.Windows.Forms.Label();
this.groupBox1.SuspendLayout(); this.tabPage2 = new System.Windows.Forms.TabPage();
this.optoDataGroupBox.SuspendLayout(); this.tabControl1.SuspendLayout();
this.groupBox2.SuspendLayout(); this.tabPage1.SuspendLayout();
this.SuspendLayout(); this.groupBox2.SuspendLayout();
// this.groupBox1.SuspendLayout();
// tabControl1 this.optoDataGroupBox.SuspendLayout();
// this.SuspendLayout();
this.tabControl1.Controls.Add(this.tabPage1); //
this.tabControl1.Controls.Add(this.tabPage2); // tabControl1
this.tabControl1.Location = new System.Drawing.Point(3, 3); //
this.tabControl1.Name = "tabControl1"; this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.SelectedIndex = 0; this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Size = new System.Drawing.Size(611, 432); this.tabControl1.Location = new System.Drawing.Point(3, 4);
this.tabControl1.TabIndex = 0; this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
// this.tabControl1.Name = "tabControl1";
// tabPage1 this.tabControl1.SelectedIndex = 0;
// this.tabControl1.Size = new System.Drawing.Size(687, 540);
this.tabPage1.Controls.Add(this.groupBox2); this.tabControl1.TabIndex = 0;
this.tabPage1.Controls.Add(this.label4); //
this.tabPage1.Controls.Add(this.label3); // tabPage1
this.tabPage1.Controls.Add(this.groupBox1); //
this.tabPage1.Controls.Add(this.optoDataGroupBox); this.tabPage1.Controls.Add(this.groupBox2);
this.tabPage1.Controls.Add(this.groupTextBox); this.tabPage1.Controls.Add(this.label4);
this.tabPage1.Controls.Add(this.groupLabel); this.tabPage1.Controls.Add(this.label3);
this.tabPage1.Controls.Add(this.muxBoardNrTextBox); this.tabPage1.Controls.Add(this.groupBox1);
this.tabPage1.Controls.Add(this.muxBoardNrLabel); this.tabPage1.Controls.Add(this.optoDataGroupBox);
this.tabPage1.Controls.Add(this.nameTextBox); this.tabPage1.Controls.Add(this.groupTextBox);
this.tabPage1.Controls.Add(this.nameLabel); this.tabPage1.Controls.Add(this.groupLabel);
this.tabPage1.Controls.Add(this.classNameLabel); this.tabPage1.Controls.Add(this.muxBoardNrTextBox);
this.tabPage1.Location = new System.Drawing.Point(4, 25); this.tabPage1.Controls.Add(this.muxBoardNrLabel);
this.tabPage1.Name = "tabPage1"; this.tabPage1.Controls.Add(this.nameTextBox);
this.tabPage1.Padding = new System.Windows.Forms.Padding(3); this.tabPage1.Controls.Add(this.nameLabel);
this.tabPage1.Size = new System.Drawing.Size(603, 403); this.tabPage1.Controls.Add(this.classNameLabel);
this.tabPage1.TabIndex = 0; this.tabPage1.Location = new System.Drawing.Point(4, 29);
this.tabPage1.Text = "Config"; this.tabPage1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.tabPage1.UseVisualStyleBackColor = true; this.tabPage1.Name = "tabPage1";
// this.tabPage1.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
// label4 this.tabPage1.Size = new System.Drawing.Size(679, 507);
// this.tabPage1.TabIndex = 0;
this.label4.AutoSize = true; this.tabPage1.Text = "Config";
this.label4.Location = new System.Drawing.Point(208, 101); this.tabPage1.UseVisualStyleBackColor = true;
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); //
this.label4.Name = "label4"; // groupBox2
this.label4.Size = new System.Drawing.Size(40, 16); //
this.label4.TabIndex = 25; this.groupBox2.Controls.Add(this.headPortNrTextBox);
this.label4.Text = "1 .. 10"; this.groupBox2.Controls.Add(this.label2);
// this.groupBox2.Location = new System.Drawing.Point(11, 450);
// label3 this.groupBox2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
// this.groupBox2.Name = "groupBox2";
this.label3.AutoSize = true; this.groupBox2.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.label3.Location = new System.Drawing.Point(208, 72); this.groupBox2.Size = new System.Drawing.Size(621, 52);
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.groupBox2.TabIndex = 26;
this.label3.Name = "label3"; this.groupBox2.TabStop = false;
this.label3.Size = new System.Drawing.Size(33, 16); this.groupBox2.Text = "Head Communication";
this.label3.TabIndex = 24; //
this.label3.Text = "1 .. 4"; // headPortNrTextBox
// //
// groupBox1 this.headPortNrTextBox.Enabled = false;
// this.headPortNrTextBox.Location = new System.Drawing.Point(494, 19);
this.groupBox1.Controls.Add(this.comboBoxCommunicationInterface); this.headPortNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupBox1.Controls.Add(this.label1); this.headPortNrTextBox.Name = "headPortNrTextBox";
this.groupBox1.Controls.Add(this.rfidPortNrTextBox); this.headPortNrTextBox.Size = new System.Drawing.Size(49, 26);
this.groupBox1.Controls.Add(this.rfidSerialPortNrLabel); this.headPortNrTextBox.TabIndex = 8;
this.groupBox1.Location = new System.Drawing.Point(10, 259); //
this.groupBox1.Margin = new System.Windows.Forms.Padding(4); // label2
this.groupBox1.Name = "groupBox1"; //
this.groupBox1.Padding = new System.Windows.Forms.Padding(4); this.label2.AutoSize = true;
this.groupBox1.Size = new System.Drawing.Size(552, 68); this.label2.Location = new System.Drawing.Point(361, 22);
this.groupBox1.TabIndex = 23; this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.groupBox1.TabStop = false; this.label2.Name = "label2";
this.groupBox1.Text = "RFID / NFC communication (in case mux. board is not used)"; this.label2.Size = new System.Drawing.Size(107, 20);
// this.label2.TabIndex = 7;
// comboBoxCommunicationInterface this.label2.Text = "Serial port nr.:";
// //
this.comboBoxCommunicationInterface.Enabled = false; // label4
this.comboBoxCommunicationInterface.FormattingEnabled = true; //
this.comboBoxCommunicationInterface.Items.AddRange(new object[] { this.label4.AutoSize = true;
"RFID", this.label4.Location = new System.Drawing.Point(234, 126);
"NFC"}); this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.comboBoxCommunicationInterface.Location = new System.Drawing.Point(201, 27); this.label4.Name = "label4";
this.comboBoxCommunicationInterface.Name = "comboBoxCommunicationInterface"; this.label4.Size = new System.Drawing.Size(52, 20);
this.comboBoxCommunicationInterface.Size = new System.Drawing.Size(71, 24); this.label4.TabIndex = 25;
this.comboBoxCommunicationInterface.TabIndex = 9; this.label4.Text = "1 .. 10";
// //
// label1 // label3
// //
this.label1.AutoSize = true; this.label3.AutoSize = true;
this.label1.Location = new System.Drawing.Point(41, 30); this.label3.Location = new System.Drawing.Point(234, 90);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1"; this.label3.Name = "label3";
this.label1.Size = new System.Drawing.Size(153, 16); this.label3.Size = new System.Drawing.Size(43, 20);
this.label1.TabIndex = 8; this.label3.TabIndex = 24;
this.label1.Text = "Communication Interface"; this.label3.Text = "1 .. 4";
// //
// rfidPortNrTextBox // groupBox1
// //
this.rfidPortNrTextBox.Enabled = false; this.groupBox1.Controls.Add(this.comboBoxCommunicationInterface);
this.rfidPortNrTextBox.Location = new System.Drawing.Point(439, 26); this.groupBox1.Controls.Add(this.label1);
this.rfidPortNrTextBox.Margin = new System.Windows.Forms.Padding(4); this.groupBox1.Controls.Add(this.rfidPortNrTextBox);
this.rfidPortNrTextBox.Name = "rfidPortNrTextBox"; this.groupBox1.Controls.Add(this.rfidSerialPortNrLabel);
this.rfidPortNrTextBox.Size = new System.Drawing.Size(44, 22); this.groupBox1.Location = new System.Drawing.Point(11, 363);
this.rfidPortNrTextBox.TabIndex = 7; this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
// this.groupBox1.Name = "groupBox1";
// rfidSerialPortNrLabel this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
// this.groupBox1.Size = new System.Drawing.Size(621, 85);
this.rfidSerialPortNrLabel.AutoSize = true; this.groupBox1.TabIndex = 23;
this.rfidSerialPortNrLabel.Location = new System.Drawing.Point(321, 30); this.groupBox1.TabStop = false;
this.rfidSerialPortNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.groupBox1.Text = "RFID / NFC communication (in case mux. board is not used)";
this.rfidSerialPortNrLabel.Name = "rfidSerialPortNrLabel"; //
this.rfidSerialPortNrLabel.Size = new System.Drawing.Size(88, 16); // comboBoxCommunicationInterface
this.rfidSerialPortNrLabel.TabIndex = 6; //
this.rfidSerialPortNrLabel.Text = "Serial port nr.:"; this.comboBoxCommunicationInterface.Enabled = false;
// this.comboBoxCommunicationInterface.FormattingEnabled = true;
// optoDataGroupBox this.comboBoxCommunicationInterface.Items.AddRange(new object[] { "RFID", "NFC" });
// this.comboBoxCommunicationInterface.Location = new System.Drawing.Point(226, 34);
this.optoDataGroupBox.Controls.Add(this.tcpipPortLabel); this.comboBoxCommunicationInterface.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.optoDataGroupBox.Controls.Add(this.tcpipPortTextBox); this.comboBoxCommunicationInterface.Name = "comboBoxCommunicationInterface";
this.optoDataGroupBox.Controls.Add(this.ipAddressLabel); this.comboBoxCommunicationInterface.Size = new System.Drawing.Size(79, 28);
this.optoDataGroupBox.Controls.Add(this.ipAddressTextBox); this.comboBoxCommunicationInterface.TabIndex = 9;
this.optoDataGroupBox.Controls.Add(this.radioButton1); //
this.optoDataGroupBox.Controls.Add(this.radioButton2); // label1
this.optoDataGroupBox.Controls.Add(this.optoSerialPortLabel); //
this.optoDataGroupBox.Controls.Add(this.optoSerialPortTextBox); this.label1.AutoSize = true;
this.optoDataGroupBox.Location = new System.Drawing.Point(10, 131); this.label1.Location = new System.Drawing.Point(46, 38);
this.optoDataGroupBox.Margin = new System.Windows.Forms.Padding(4); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.optoDataGroupBox.Name = "optoDataGroupBox"; this.label1.Name = "label1";
this.optoDataGroupBox.Padding = new System.Windows.Forms.Padding(4); this.label1.Size = new System.Drawing.Size(187, 20);
this.optoDataGroupBox.Size = new System.Drawing.Size(552, 119); this.label1.TabIndex = 8;
this.optoDataGroupBox.TabIndex = 18; this.label1.Text = "Communication Interface";
this.optoDataGroupBox.TabStop = false; //
this.optoDataGroupBox.Text = "Opto-data"; // rfidPortNrTextBox
// //
// tcpipPortLabel this.rfidPortNrTextBox.Enabled = false;
// this.rfidPortNrTextBox.Location = new System.Drawing.Point(494, 32);
this.tcpipPortLabel.AutoSize = true; this.rfidPortNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.tcpipPortLabel.Location = new System.Drawing.Point(41, 87); this.rfidPortNrTextBox.Name = "rfidPortNrTextBox";
this.tcpipPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.rfidPortNrTextBox.Size = new System.Drawing.Size(49, 26);
this.tcpipPortLabel.Name = "tcpipPortLabel"; this.rfidPortNrTextBox.TabIndex = 7;
this.tcpipPortLabel.Size = new System.Drawing.Size(54, 16); //
this.tcpipPortLabel.TabIndex = 4; // rfidSerialPortNrLabel
this.tcpipPortLabel.Text = "Port nr..:"; //
// this.rfidSerialPortNrLabel.AutoSize = true;
// tcpipPortTextBox this.rfidSerialPortNrLabel.Location = new System.Drawing.Point(361, 38);
// this.rfidSerialPortNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.tcpipPortTextBox.Enabled = false; this.rfidSerialPortNrLabel.Name = "rfidSerialPortNrLabel";
this.tcpipPortTextBox.Location = new System.Drawing.Point(143, 84); this.rfidSerialPortNrLabel.Size = new System.Drawing.Size(107, 20);
this.tcpipPortTextBox.Margin = new System.Windows.Forms.Padding(4); this.rfidSerialPortNrLabel.TabIndex = 6;
this.tcpipPortTextBox.Name = "tcpipPortTextBox"; this.rfidSerialPortNrLabel.Text = "Serial port nr.:";
this.tcpipPortTextBox.Size = new System.Drawing.Size(51, 22); //
this.tcpipPortTextBox.TabIndex = 5; // optoDataGroupBox
// //
// ipAddressLabel this.optoDataGroupBox.Controls.Add(this.checkBox_EnableShowChanels);
// this.optoDataGroupBox.Controls.Add(this.tBBeginDataFlush);
this.ipAddressLabel.AutoSize = true; this.optoDataGroupBox.Controls.Add(this.labelFlush);
this.ipAddressLabel.Location = new System.Drawing.Point(41, 59); this.optoDataGroupBox.Controls.Add(this.tcpipPortLabel);
this.ipAddressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.optoDataGroupBox.Controls.Add(this.tcpipPortTextBox);
this.ipAddressLabel.Name = "ipAddressLabel"; this.optoDataGroupBox.Controls.Add(this.ipAddressLabel);
this.ipAddressLabel.Size = new System.Drawing.Size(78, 16); this.optoDataGroupBox.Controls.Add(this.ipAddressTextBox);
this.ipAddressLabel.TabIndex = 2; this.optoDataGroupBox.Controls.Add(this.radioButton1);
this.ipAddressLabel.Text = "IP address.:"; this.optoDataGroupBox.Controls.Add(this.radioButton2);
// this.optoDataGroupBox.Controls.Add(this.optoSerialPortLabel);
// ipAddressTextBox this.optoDataGroupBox.Controls.Add(this.optoSerialPortTextBox);
// this.optoDataGroupBox.Location = new System.Drawing.Point(11, 164);
this.ipAddressTextBox.Enabled = false; this.optoDataGroupBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.ipAddressTextBox.Location = new System.Drawing.Point(143, 55); this.optoDataGroupBox.Name = "optoDataGroupBox";
this.ipAddressTextBox.Margin = new System.Windows.Forms.Padding(4); this.optoDataGroupBox.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.ipAddressTextBox.Name = "ipAddressTextBox"; this.optoDataGroupBox.Size = new System.Drawing.Size(621, 189);
this.ipAddressTextBox.Size = new System.Drawing.Size(129, 22); this.optoDataGroupBox.TabIndex = 18;
this.ipAddressTextBox.TabIndex = 3; this.optoDataGroupBox.TabStop = false;
// this.optoDataGroupBox.Text = "Opto-data";
// radioButton1 //
// // checkBox_EnableShowChanels
this.radioButton1.AutoSize = true; //
this.radioButton1.Checked = true; this.checkBox_EnableShowChanels.Checked = true;
this.radioButton1.Enabled = false; this.checkBox_EnableShowChanels.CheckState = System.Windows.Forms.CheckState.Checked;
this.radioButton1.Location = new System.Drawing.Point(29, 23); this.checkBox_EnableShowChanels.Enabled = false;
this.radioButton1.Margin = new System.Windows.Forms.Padding(4); this.checkBox_EnableShowChanels.Location = new System.Drawing.Point(351, 147);
this.radioButton1.Name = "radioButton1"; this.checkBox_EnableShowChanels.Name = "checkBox_EnableShowChanels";
this.radioButton1.Size = new System.Drawing.Size(99, 20); this.checkBox_EnableShowChanels.Size = new System.Drawing.Size(238, 24);
this.radioButton1.TabIndex = 0; this.checkBox_EnableShowChanels.TabIndex = 10;
this.radioButton1.TabStop = true; this.checkBox_EnableShowChanels.Text = "Enable Show Channels";
this.radioButton1.Text = "Use TCP/IP"; this.checkBox_EnableShowChanels.UseVisualStyleBackColor = true;
this.radioButton1.UseVisualStyleBackColor = true; //
// // tBBeginDataFlush
// radioButton2 //
// this.tBBeginDataFlush.Enabled = false;
this.radioButton2.AutoSize = true; this.tBBeginDataFlush.Location = new System.Drawing.Point(474, 105);
this.radioButton2.Enabled = false; this.tBBeginDataFlush.MaxLength = 8;
this.radioButton2.Location = new System.Drawing.Point(312, 23); this.tBBeginDataFlush.Name = "tBBeginDataFlush";
this.radioButton2.Margin = new System.Windows.Forms.Padding(4); this.tBBeginDataFlush.Size = new System.Drawing.Size(69, 26);
this.radioButton2.Name = "radioButton2"; this.tBBeginDataFlush.TabIndex = 9;
this.radioButton2.Size = new System.Drawing.Size(115, 20); this.tBBeginDataFlush.Text = "2000";
this.radioButton2.TabIndex = 1; this.tBBeginDataFlush.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
this.radioButton2.Text = "Use serial port"; //
this.radioButton2.UseVisualStyleBackColor = true; // labelFlush
// //
// optoSerialPortLabel this.labelFlush.Location = new System.Drawing.Point(328, 109);
// this.labelFlush.Name = "labelFlush";
this.optoSerialPortLabel.AutoSize = true; this.labelFlush.Size = new System.Drawing.Size(140, 22);
this.optoSerialPortLabel.Location = new System.Drawing.Point(321, 55); this.labelFlush.TabIndex = 8;
this.optoSerialPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.labelFlush.Text = "Begin Data Flush:";
this.optoSerialPortLabel.Name = "optoSerialPortLabel"; //
this.optoSerialPortLabel.Size = new System.Drawing.Size(88, 16); // tcpipPortLabel
this.optoSerialPortLabel.TabIndex = 6; //
this.optoSerialPortLabel.Text = "Serial port nr.:"; this.tcpipPortLabel.AutoSize = true;
// this.tcpipPortLabel.Location = new System.Drawing.Point(46, 109);
// optoSerialPortTextBox this.tcpipPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
// this.tcpipPortLabel.Name = "tcpipPortLabel";
this.optoSerialPortTextBox.Enabled = false; this.tcpipPortLabel.Size = new System.Drawing.Size(68, 20);
this.optoSerialPortTextBox.Location = new System.Drawing.Point(439, 52); this.tcpipPortLabel.TabIndex = 4;
this.optoSerialPortTextBox.Margin = new System.Windows.Forms.Padding(4); this.tcpipPortLabel.Text = "Port nr..:";
this.optoSerialPortTextBox.Name = "optoSerialPortTextBox"; //
this.optoSerialPortTextBox.Size = new System.Drawing.Size(44, 22); // tcpipPortTextBox
this.optoSerialPortTextBox.TabIndex = 7; //
// this.tcpipPortTextBox.Enabled = false;
// groupTextBox this.tcpipPortTextBox.Location = new System.Drawing.Point(161, 105);
// this.tcpipPortTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupTextBox.Enabled = false; this.tcpipPortTextBox.Name = "tcpipPortTextBox";
this.groupTextBox.Location = new System.Drawing.Point(153, 97); this.tcpipPortTextBox.Size = new System.Drawing.Size(57, 26);
this.groupTextBox.Margin = new System.Windows.Forms.Padding(4); this.tcpipPortTextBox.TabIndex = 5;
this.groupTextBox.Name = "groupTextBox"; //
this.groupTextBox.Size = new System.Drawing.Size(44, 22); // ipAddressLabel
this.groupTextBox.TabIndex = 22; //
// this.ipAddressLabel.AutoSize = true;
// groupLabel this.ipAddressLabel.Location = new System.Drawing.Point(46, 74);
// this.ipAddressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.groupLabel.AutoSize = true; this.ipAddressLabel.Name = "ipAddressLabel";
this.groupLabel.Location = new System.Drawing.Point(6, 101); this.ipAddressLabel.Size = new System.Drawing.Size(93, 20);
this.groupLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.ipAddressLabel.TabIndex = 2;
this.groupLabel.Name = "groupLabel"; this.ipAddressLabel.Text = "IP address.:";
this.groupLabel.Size = new System.Drawing.Size(54, 16); //
this.groupLabel.TabIndex = 21; // ipAddressTextBox
this.groupLabel.Text = "Group 2"; //
// this.ipAddressTextBox.Enabled = false;
// muxBoardNrTextBox this.ipAddressTextBox.Location = new System.Drawing.Point(161, 69);
// this.ipAddressTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.muxBoardNrTextBox.Enabled = false; this.ipAddressTextBox.Name = "ipAddressTextBox";
this.muxBoardNrTextBox.Location = new System.Drawing.Point(153, 69); this.ipAddressTextBox.Size = new System.Drawing.Size(145, 26);
this.muxBoardNrTextBox.Margin = new System.Windows.Forms.Padding(4); this.ipAddressTextBox.TabIndex = 3;
this.muxBoardNrTextBox.Name = "muxBoardNrTextBox"; //
this.muxBoardNrTextBox.Size = new System.Drawing.Size(44, 22); // radioButton1
this.muxBoardNrTextBox.TabIndex = 20; //
// this.radioButton1.AutoSize = true;
// muxBoardNrLabel this.radioButton1.Checked = true;
// this.radioButton1.Enabled = false;
this.muxBoardNrLabel.AutoSize = true; this.radioButton1.Location = new System.Drawing.Point(33, 29);
this.muxBoardNrLabel.Location = new System.Drawing.Point(6, 72); this.radioButton1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.muxBoardNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.radioButton1.Name = "radioButton1";
this.muxBoardNrLabel.Name = "muxBoardNrLabel"; this.radioButton1.Size = new System.Drawing.Size(109, 24);
this.muxBoardNrLabel.Size = new System.Drawing.Size(131, 16); this.radioButton1.TabIndex = 0;
this.muxBoardNrLabel.TabIndex = 19; this.radioButton1.TabStop = true;
this.muxBoardNrLabel.Text = "Group 1 (mux. board)"; this.radioButton1.Text = "Use TCP/IP";
// this.radioButton1.UseVisualStyleBackColor = true;
// nameTextBox //
// // radioButton2
this.nameTextBox.Enabled = false; //
this.nameTextBox.Location = new System.Drawing.Point(153, 40); this.radioButton2.AutoSize = true;
this.nameTextBox.Margin = new System.Windows.Forms.Padding(4); this.radioButton2.Enabled = false;
this.nameTextBox.Name = "nameTextBox"; this.radioButton2.Location = new System.Drawing.Point(351, 29);
this.nameTextBox.Size = new System.Drawing.Size(160, 22); this.radioButton2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.nameTextBox.TabIndex = 17; this.radioButton2.Name = "radioButton2";
// this.radioButton2.Size = new System.Drawing.Size(129, 24);
// nameLabel this.radioButton2.TabIndex = 1;
// this.radioButton2.Text = "Use serial port";
this.nameLabel.AutoSize = true; this.radioButton2.UseVisualStyleBackColor = true;
this.nameLabel.Location = new System.Drawing.Point(6, 44); //
this.nameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); // optoSerialPortLabel
this.nameLabel.Name = "nameLabel"; //
this.nameLabel.Size = new System.Drawing.Size(44, 16); this.optoSerialPortLabel.AutoSize = true;
this.nameLabel.TabIndex = 16; this.optoSerialPortLabel.Location = new System.Drawing.Point(361, 69);
this.nameLabel.Text = "Name"; this.optoSerialPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
// this.optoSerialPortLabel.Name = "optoSerialPortLabel";
// classNameLabel this.optoSerialPortLabel.Size = new System.Drawing.Size(107, 20);
// this.optoSerialPortLabel.TabIndex = 6;
this.classNameLabel.AutoSize = true; this.optoSerialPortLabel.Text = "Serial port nr.:";
this.classNameLabel.Location = new System.Drawing.Point(149, 11); //
this.classNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); // optoSerialPortTextBox
this.classNameLabel.Name = "classNameLabel"; //
this.classNameLabel.Size = new System.Drawing.Size(78, 16); this.optoSerialPortTextBox.Enabled = false;
this.classNameLabel.TabIndex = 15; this.optoSerialPortTextBox.Location = new System.Drawing.Point(494, 65);
this.classNameLabel.Text = "ClassName"; this.optoSerialPortTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
// this.optoSerialPortTextBox.Name = "optoSerialPortTextBox";
// tabPage2 this.optoSerialPortTextBox.Size = new System.Drawing.Size(49, 26);
// this.optoSerialPortTextBox.TabIndex = 7;
this.tabPage2.Location = new System.Drawing.Point(4, 25); //
this.tabPage2.Name = "tabPage2"; // groupTextBox
this.tabPage2.Padding = new System.Windows.Forms.Padding(3); //
this.tabPage2.Size = new System.Drawing.Size(603, 403); this.groupTextBox.Enabled = false;
this.tabPage2.TabIndex = 1; this.groupTextBox.Location = new System.Drawing.Point(172, 121);
this.tabPage2.Text = "Test"; this.groupTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.tabPage2.UseVisualStyleBackColor = true; this.groupTextBox.Name = "groupTextBox";
// this.groupTextBox.Size = new System.Drawing.Size(49, 26);
// groupBox2 this.groupTextBox.TabIndex = 22;
// //
this.groupBox2.Controls.Add(this.headPortNrTextBox); // groupLabel
this.groupBox2.Controls.Add(this.label2); //
this.groupBox2.Location = new System.Drawing.Point(10, 335); this.groupLabel.AutoSize = true;
this.groupBox2.Name = "groupBox2"; this.groupLabel.Location = new System.Drawing.Point(7, 126);
this.groupBox2.Size = new System.Drawing.Size(552, 50); this.groupLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.groupBox2.TabIndex = 26; this.groupLabel.Name = "groupLabel";
this.groupBox2.TabStop = false; this.groupLabel.Size = new System.Drawing.Size(67, 20);
this.groupBox2.Text = "Head Communication"; this.groupLabel.TabIndex = 21;
// this.groupLabel.Text = "Group 2";
// label2 //
// // muxBoardNrTextBox
this.label2.AutoSize = true; //
this.label2.Location = new System.Drawing.Point(321, 18); this.muxBoardNrTextBox.Enabled = false;
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.muxBoardNrTextBox.Location = new System.Drawing.Point(172, 86);
this.label2.Name = "label2"; this.muxBoardNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.label2.Size = new System.Drawing.Size(88, 16); this.muxBoardNrTextBox.Name = "muxBoardNrTextBox";
this.label2.TabIndex = 7; this.muxBoardNrTextBox.Size = new System.Drawing.Size(49, 26);
this.label2.Text = "Serial port nr.:"; this.muxBoardNrTextBox.TabIndex = 20;
// //
// headPortNrTextBox // muxBoardNrLabel
// //
this.headPortNrTextBox.Enabled = false; this.muxBoardNrLabel.AutoSize = true;
this.headPortNrTextBox.Location = new System.Drawing.Point(439, 15); this.muxBoardNrLabel.Location = new System.Drawing.Point(7, 90);
this.headPortNrTextBox.Margin = new System.Windows.Forms.Padding(4); this.muxBoardNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.headPortNrTextBox.Name = "headPortNrTextBox"; this.muxBoardNrLabel.Name = "muxBoardNrLabel";
this.headPortNrTextBox.Size = new System.Drawing.Size(44, 22); this.muxBoardNrLabel.Size = new System.Drawing.Size(159, 20);
this.headPortNrTextBox.TabIndex = 8; this.muxBoardNrLabel.TabIndex = 19;
// this.muxBoardNrLabel.Text = "Group 1 (mux. board)";
// IperlHeadCfgCtrl //
// // nameTextBox
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); //
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.nameTextBox.Enabled = false;
this.Controls.Add(this.tabControl1); this.nameTextBox.Location = new System.Drawing.Point(172, 50);
this.Margin = new System.Windows.Forms.Padding(4); this.nameTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.Name = "GenesisCfgCtrl"; this.nameTextBox.Name = "nameTextBox";
this.Size = new System.Drawing.Size(617, 438); this.nameTextBox.Size = new System.Drawing.Size(180, 26);
this.Load += new System.EventHandler(this.WaterMeterCfgCtrl_Load); this.nameTextBox.TabIndex = 17;
this.tabControl1.ResumeLayout(false); //
this.tabPage1.ResumeLayout(false); // nameLabel
this.tabPage1.PerformLayout(); //
this.groupBox1.ResumeLayout(false); this.nameLabel.AutoSize = true;
this.groupBox1.PerformLayout(); this.nameLabel.Location = new System.Drawing.Point(7, 55);
this.optoDataGroupBox.ResumeLayout(false); this.nameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.optoDataGroupBox.PerformLayout(); this.nameLabel.Name = "nameLabel";
this.groupBox2.ResumeLayout(false); this.nameLabel.Size = new System.Drawing.Size(51, 20);
this.groupBox2.PerformLayout(); this.nameLabel.TabIndex = 16;
this.ResumeLayout(false); this.nameLabel.Text = "Name";
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(168, 14);
this.classNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(90, 20);
this.classNameLabel.TabIndex = 15;
this.classNameLabel.Text = "ClassName";
//
// tabPage2
//
this.tabPage2.Location = new System.Drawing.Point(4, 29);
this.tabPage2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.tabPage2.Size = new System.Drawing.Size(679, 507);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Test";
this.tabPage2.UseVisualStyleBackColor = true;
//
// GenesisCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.tabControl1);
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.Name = "GenesisCfgCtrl";
this.Size = new System.Drawing.Size(694, 548);
this.Load += new System.EventHandler(this.WaterMeterCfgCtrl_Load);
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.optoDataGroupBox.ResumeLayout(false);
this.optoDataGroupBox.PerformLayout();
this.ResumeLayout(false);
} }
#endregion private System.Windows.Forms.CheckBox checkBox_EnableShowChanels;
private System.Windows.Forms.Label labelFlush;
private System.Windows.Forms.TextBox tBBeginDataFlush;
#endregion
private System.Windows.Forms.TabControl tabControl1; private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1; private System.Windows.Forms.TabPage tabPage1;
@@ -6,7 +6,7 @@ using System;
using System.Globalization; using System.Globalization;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol;
using Xylem.Common.Metrology.Measurements;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.common namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
@@ -25,6 +25,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
public static readonly int Length = 42; public static readonly int Length = 42;
private static CultureInfo culture; private static CultureInfo culture;
public MeasurementRecord data;
/// ///
/// Strobed value /// Strobed value
@@ -124,11 +125,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
} }
// -------- TIMESTAMP (seconds) --------
private const double TS_RANGE = StreamingDecoder.CpuTimeOverflowS;
// -------- VOLUME (liters) --------
private const double VOL_RANGE = StreamingDecoder.DefaultAccuDutOverflowVolumeCm * 1000;
/// <summary> /// <summary>
/// update data by CalibrationRecord /// update data by CalibrationRecord
@@ -149,7 +146,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
if (data == null) if (data == null)
throw new ArgumentNullException(nameof(data)); throw new ArgumentNullException(nameof(data));
UpdateData( data.Channel, data.VolumeCm, data.TimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast); this.data = data;
UpdateData( data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
} }
/// <summary> /// <summary>
@@ -170,8 +168,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
{ {
if (data == null) if (data == null)
throw new ArgumentNullException(nameof(data)); throw new ArgumentNullException(nameof(data));
this.data = data;
UpdateData( data.Channel, data.VolumeCm, data.TimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast); UpdateData( data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
} }
/// <summary> /// <summary>
@@ -187,7 +185,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
private void UpdateData( private void UpdateData(
int channel, int channel,
double volumeCm, double volumeCm,
double OverflowVolumeCm,
double timeS, double timeS,
double OverflowTimeS,
int counter, int counter,
float refFlow, float refFlow,
ref double volumeRawExtLast, ref double volumeRawExtLast,
@@ -200,29 +200,30 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
Flags = OptoTelegramFlags.OK; Flags = OptoTelegramFlags.OK;
FlowRaw = 0; FlowRaw = 0;
VolumeRaw = volumeCm * 1000.0; // liters double nV = NormalizeByOverflow(volumeCm, OverflowVolumeCm);
VolumeRaw = nV * 1000.0; // liters
CheckSum = 0; CheckSum = 0;
Impedance = 0; Impedance = 0;
EmfRaw = 0; EmfRaw = 0;
MagneticFieldRaw = 0; MagneticFieldRaw = 0;
double ts = NormalizeTimestamp(timeS); double ts = NormalizeByOverflow(timeS, OverflowTimeS);
Timestamp = ts; Timestamp = ts;
VolumeRawExt = UnwrapVolume(VolumeRaw, ref volumeRawExtLast); VolumeRawExt = UnwrapVolume(VolumeRaw,OverflowVolumeCm * 1000, ref volumeRawExtLast);
TimestampExt = UnwrapTimestamp(ts, ref timestampExtLast); TimestampExt = UnwrapTimestamp(ts,OverflowTimeS, ref timestampExtLast);
} }
private static double NormalizeTimestamp(double timeS) private static double NormalizeByOverflow(double value, double overfValue = 0)
{ {
double ts = timeS % TS_RANGE; double nValue = value % overfValue;
if (ts < 0) if (nValue < 0)
ts += TS_RANGE; nValue += overfValue;
return ts; return nValue;
} }
private static double UnwrapVolume(double currentVolume, ref double volumeRawExtLast) private static double UnwrapVolume(double currentVolume, double overfValue, ref double volumeRawExtLast)
{ {
double result; double result;
@@ -232,16 +233,25 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
} }
else else
{ {
result = currentVolume < volumeRawExtLast double lastMod = volumeRawExtLast % overfValue;
? currentVolume + VOL_RANGE if (lastMod < 0)
: currentVolume; lastMod += overfValue;
double delta = currentVolume - lastMod;
if (delta < -overfValue / 2.0)
delta += overfValue;
else if (delta > overfValue / 2.0)
delta -= overfValue;
result = volumeRawExtLast + delta;
} }
volumeRawExtLast = result; volumeRawExtLast = result;
return result; return result;
} }
private static double UnwrapTimestamp(double currentTimestamp, ref double timestampExtLast) private static double UnwrapTimestamp(double currentTimestamp,double overfValue, ref double timestampExtLast)
{ {
double result; double result;
@@ -251,16 +261,16 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
} }
else else
{ {
double lastMod = timestampExtLast % TS_RANGE; double lastMod = timestampExtLast % overfValue;
if (lastMod < 0) if (lastMod < 0)
lastMod += TS_RANGE; lastMod += overfValue;
double delta = currentTimestamp - lastMod; double delta = currentTimestamp - lastMod;
if (delta < -TS_RANGE / 2.0) if (delta < -overfValue / 2.0)
delta += TS_RANGE; delta += overfValue;
else if (delta > TS_RANGE / 2.0) else if (delta > overfValue / 2.0)
delta -= TS_RANGE; delta -= overfValue;
result = timestampExtLast + delta; result = timestampExtLast + delta;
} }
@@ -349,5 +359,33 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
Label()); Label());
} }
} }
public void Copy(OptoTelegramRaw optoTelegramRaw)
{
this.Flags = optoTelegramRaw.Flags;
this.DateTime = optoTelegramRaw.DateTime;
this.RefFlow = optoTelegramRaw.RefFlow;
this.Counter = optoTelegramRaw.Counter;
this.EmfRaw = optoTelegramRaw.EmfRaw;
this.MagneticFieldRaw = optoTelegramRaw.MagneticFieldRaw;
this.FlowRaw = optoTelegramRaw.FlowRaw;
this.VolumeRaw = optoTelegramRaw.VolumeRaw;
this.VolumeRawExt = optoTelegramRaw.VolumeRawExt;
this.Impedance = optoTelegramRaw.Impedance;
this.Timestamp = optoTelegramRaw.Timestamp;
this.TimestampExt = optoTelegramRaw.TimestampExt;
this.CheckSum = optoTelegramRaw.CheckSum;
this.iChannel = optoTelegramRaw.iChannel;
}
public string rawDataToString()
{
if (data != null)
{
return string.Format("Channel: {0}, VolumeCm:{1}, OverflowVolumeCm:{2}, TimeS: {3}, OverflowTimeS:{4} ",
data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS);
}
return string.Empty;
}
} }
} }
@@ -227,6 +227,20 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils
return _serialPort.ReadExisting(); return _serialPort.ReadExisting();
} }
public void ResetInputBuffer()
{
if (!IsOpen)
throw new InvalidOperationException("Serial port not open");
_serialPort.DiscardInBuffer();
}
public void ResetOutputBuffer()
{
if (!IsOpen)
throw new InvalidOperationException("Serial port not open");
_serialPort.DiscardOutBuffer();
}
public Encoding Encoding => _serialPort?.Encoding ?? _encoding; public Encoding Encoding => _serialPort?.Encoding ?? _encoding;
public int BytesToRead => (_serialPort != null && _serialPort.IsOpen) ? _serialPort.BytesToRead : 0; public int BytesToRead => (_serialPort != null && _serialPort.IsOpen) ? _serialPort.BytesToRead : 0;
@@ -110,6 +110,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
{ {
Flush = 0, Flush = 0,
ProcessAndSave, ProcessAndSave,
CalculateStoredData,
} }
public enum CommunicationInterface public enum CommunicationInterface
File diff suppressed because it is too large Load Diff
@@ -13,46 +13,29 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
{ {
public class CliRunner public class CliRunner
{ {
//static readonly ILog log = LogManager.GetLogger(typeof(CliRunner));
private readonly ILog log; private readonly ILog log;
private List<Task> taskPool = new List<Task>(); private readonly List<CliTaskInfo> taskPool = new List<CliTaskInfo>();
private long startTime; private long startTimeMs;
private long incommingTime; private long incommingTimeMs;
public List<Task> TaskPool { get { return taskPool; } } public List<CliTaskInfo> TaskPool
public void AddTask(Task task) { taskPool.Add(task); }
public void WaitAll() { Task.WaitAll(taskPool.ToArray()); }
public void Clear() { taskPool.Clear(); }
public void CancelAll() { Task.WhenAll(taskPool).ContinueWith(t => { }); }
public void StartAll()
{ {
taskPool.ForEach(t => t.Start()); get { return taskPool; }
}
public bool AreTasksDone()
{
return taskPool.All(task => task.IsCompleted);
} }
public long StartTime public long StartTime
{ {
get => startTime; get { return startTimeMs; }
} }
public long IncommingTime public long IncommingTime
{ {
get => incommingTime; get { return incommingTimeMs; }
}
public bool TimeOutReceived(long timeout)
{
return (DateTime.Now.Ticks - startTime) > timeout;
} }
public CliRunner(bool isCliLogging) public CliRunner(bool isCliLogging)
{ {
startTime = DateTime.Now.Ticks; ResetStartTime();
if (isCliLogging) if (isCliLogging)
{ {
@@ -60,42 +43,180 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
{ {
log = new ScopedLoggerFactory().CreateLogger<CliRunner>( log = new ScopedLoggerFactory().CreateLogger<CliRunner>(
@"C:\TBF\Logs\CliRunner.txt", @"C:\TBF\Logs\CliRunner.txt",
10, // maxFileSizeMB 10,
7, // maxBackups 7,
log4net.Core.Level.Debug, log4net.Core.Level.Debug,
true, // zipRolledFiles true,
true, // singleZipPerDay true,
TimeSpan.FromMinutes(2) // zipScanInterval TimeSpan.FromMinutes(2)
); );
} }
catch (Exception ex) catch (Exception ex)
{ {
// Fallback to console or handle gracefully
Console.WriteLine($"Failed to initialize logger: {ex.Message}"); Console.WriteLine($"Failed to initialize logger: {ex.Message}");
} }
} }
} }
/// <summary> public void ResetStartTime()
/// {
/// </summary> startTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
/// <param name="fileName"></param> }
/// <param name="args"></param>
/// <typeparam name="T"></typeparam> public void Clear()
{
foreach (var item in taskPool)
{
try
{
item?.Cts?.Dispose();
}
catch
{
}
try
{
if (item?.Process != null)
{
item.Process.Dispose();
}
}
catch
{
}
}
taskPool.Clear();
}
public void WaitAll()
{
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 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) public void AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg)
{ {
var task = SendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg)); AddSendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
taskPool.Add(task);
} }
public void AddSendAsync(string fileName, string args) public void AddSendAsync(string fileName, string args)
{ {
var task = SendAsync(fileName, args); ResetStartTime();
taskPool.Add(task);
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)
public async Task<string> SendAsync(string fileName, string args, CancellationToken ct = default)
{ {
var psi = new ProcessStartInfo var psi = new ProcessStartInfo
{ {
@@ -107,41 +228,84 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
CreateNoWindow = true CreateNoWindow = true
}; };
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true }; using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
process.Start();
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
try
{ {
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct)); info.Process = process;
}
catch (OperationCanceledException) try
{
if (!process.HasExited)
{ {
process.Kill(); process.Start();
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
Task<string> 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);
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;
} }
throw;
} }
//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;
} }
public void AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new() public void AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new()
{ {
var task = RunAndCaptureJsonAsync<T>(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg)); AddRunAndCaptureJsonAsync<T>(
taskPool.Add(task); data.SerialPortCmdClientPath,
data.DefaultArgSettings(eMeterArg));
} }
public async Task<T> RunAndCaptureJsonAsync<T>(string fileName, string args, CancellationToken ct = default) where T : new() 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 var psi = new ProcessStartInfo
{ {
@@ -153,80 +317,121 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
CreateNoWindow = true CreateNoWindow = true
}; };
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true }; using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
process.Start(); {
info.Process = process;
var stdoutTask = process.StandardOutput.ReadToEndAsync(); try
var stderrTask = process.StandardError.ReadToEndAsync(); {
process.Start();
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct)); var stdoutTask = process.StandardOutput.ReadToEndAsync();
var stderrTask = process.StandardError.ReadToEndAsync();
string allOutput = (stdoutTask.Result ?? "") + (stderrTask.Result ?? ""); try
log?.Debug(allOutput); {
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct));
}
catch (OperationCanceledException)
{
if (info.State != CliTaskState.TimedOut)
info.State = CliTaskState.Canceled;
string json = ExtractJson(allOutput); if (!process.HasExited)
if (TryJsonStringDeserialize(json, out T result)) return result; {
return default; process.Kill();
}
throw;
}
incommingTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string allOutput = (stdoutTask.Result ?? "") + (stderrTask.Result ?? "");
log?.Debug(allOutput);
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 runAndCaptureJsonAsync) where T : new() public bool TryJsonStringDeserialize<T>(string json, out T value) where T : new()
{ {
if (json != null) if (json != null)
{ {
try try
{ {
runAndCaptureJsonAsync = JsonConvert.DeserializeObject<T>(json); value = JsonConvert.DeserializeObject<T>(json);
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
log.Debug(ex.Message); log?.Debug(ex.Message);
runAndCaptureJsonAsync = TryConvert<T>(json); value = TryConvert<T>(json);
return true; return true;
} }
} }
runAndCaptureJsonAsync = default; value = default(T);
return false; return false;
} }
private static T TryConvert<T>(string json) where T : new() private static T TryConvert<T>(string json) where T : new()
{ {
T obj = new T();
T obj = new T(); try
{
JObject jObject = JObject.Parse(json);
try foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{ {
JObject jObject = JObject.Parse(json); if (!prop.CanWrite)
continue;
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)) JToken token;
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
{ {
if (!prop.CanWrite) continue; try
{
JToken token; object value = token.ToObject(prop.PropertyType);
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token)) prop.SetValue(obj, value);
}
catch
{ {
try
{
object value = token.ToObject(prop.PropertyType);
prop.SetValue(obj, value);
}
catch
{
// leave default if conversion fails
}
} }
// else → keep default value
} }
} }
catch (Exception ex) }
{ catch (Exception ex)
Console.WriteLine($"TryConvert failed: {ex.Message}"); {
} Console.WriteLine($"TryConvert failed: {ex.Message}");
}
return obj;
return obj;
} }
public string ExtractJson(string text) public string ExtractJson(string text)
@@ -241,6 +446,20 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
return null; return null;
} }
}
internal void AddTaskForTest(Task task, string name = "TestTask", CancellationTokenSource cts = null)
{
taskPool.Add(new CliTaskInfo
{
Task = task,
Cts = cts,
Name = name,
State = task.IsCompleted
? (task.IsCanceled ? CliTaskState.Canceled :
task.IsFaulted ? CliTaskState.Faulted :
CliTaskState.Completed)
: CliTaskState.Running
});
}
}
} }
@@ -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 /// for debug purposes what time will consume answer
/// </summary> /// </summary>
private long startTimeInMilis = -1, fullTimeInMilis = -1; private long startTimeInMilis = -1, fullTimeInMilis = -1;
/// 30 seconds
private static long SafetyTimeOut = 30 * 1000; private static long SafetyTimeOut = 30 * 1000;
private long incommingTime = -1; private long incommingTime = -1;
private bool _lastOpTimedOut;
/// 30 seconds
public long DeltaTime { get{return fullTimeInMilis;}} public long DeltaTime { get{return fullTimeInMilis;}}
public long IncommingTime { get{return incommingTime;}} public long IncommingTime { get{return incommingTime;}}
@@ -356,108 +358,146 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
if (_currentOp == CurrentPoseidonOp.SendStartDataStream) if (_currentOp == CurrentPoseidonOp.SendStartDataStream)
{ {
CliRunner.Clear();
_lastOpTimedOut = false;
CliRunner.AddSendAsync(serialPort, SerialPortData.EMeterArg.AllParams); CliRunner.AddSendAsync(serialPort, SerialPortData.EMeterArg.AllParams);
_currentOp = CurrentPoseidonOp.SendStartDataStream_Runing;
return Event.Busy; return Event.Busy;
} }
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Runing) else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Runing)
{ {
if (CliRunner.AreTasksDone() if (CliRunner.AreTasksDone())
|| CliRunner.TimeOutReceived(SafetyTimeOut))
{ {
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done; _currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
} }
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
{
_lastOpTimedOut = true;
CliRunner.CancelUndoneTasksAsTimedOut();
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
}
return Event.Busy; return Event.Busy;
} }
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Done) else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Done)
{ {
var firstTask = CliRunner.TaskPool.FindLast(t => t is Task<string>); var firstTaskInfo = CliRunner.TaskPool
.FindLast(t => t.Task is Task<string> && t.UseResult);
if (firstTaskInfo != null)
if (firstTask != null && firstTask is Task<string>)
{ {
string data = null; var task = (Task<string>)firstTaskInfo.Task;
data = (firstTask as Task<string>).Result; string data = task.Result;
if (data != null && TryGetDeviceId(data, out wmSerialNr)) if (!string.IsNullOrEmpty(data))
{ {
TryGetDeviceId(data, out wmSerialNr);
} }
} }
else if (_lastOpTimedOut)
{
log.Warn(
$"PoseidonReader {Name}: SendStartDataStream timed out, no completed result will be used.");
}
_currentOp = CurrentPoseidonOp.Done; CliRunner.Clear();
_currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
return Event.Done; return Event.Done;
} }
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
|| _currentOp == CurrentPoseidonOp.ReadDataStream_End) || _currentOp == CurrentPoseidonOp.ReadDataStream_End)
{ {
startTimeInMilis = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; startTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
incommingTime = -1; incommingTime = -1;
_isReadingStart = (_currentOp == CurrentPoseidonOp.ReadDataStream_Start); _isReadingStart = (_currentOp == CurrentPoseidonOp.ReadDataStream_Start);
CliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
CliRunner.Clear();
_lastOpTimedOut = false;
CliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
SerialPortData.EMeterArg.AllParams);
_currentOp = CurrentPoseidonOp.ReadDatastream_Running; _currentOp = CurrentPoseidonOp.ReadDatastream_Running;
return Event.Busy; return Event.Busy;
}else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running) }
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
{ {
if (CliRunner.AreTasksDone() if (CliRunner.AreTasksDone())
|| CliRunner.TimeOutReceived(SafetyTimeOut))
{ {
_currentOp = CurrentPoseidonOp.ReadDatastream_Done; _currentOp = CurrentPoseidonOp.ReadDatastream_Done;
//set time stamp - end of reading
incommingTime = CliRunner.IncommingTime; incommingTime = CliRunner.IncommingTime;
} }
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
{
_lastOpTimedOut = true;
CliRunner.CancelUndoneTasksAsTimedOut();
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
incommingTime = CliRunner.IncommingTime;
}
return Event.Busy; return Event.Busy;
} }
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Done) else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Done)
{ {
fullTimeInMilis = (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - startTimeInMilis; fullTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTimeInMilis;
log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis} "); log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis}");
JsonDataFromPoseidon data = null;
var first = CliRunner.TaskPool.FindLast(t => t is Task<JsonDataFromPoseidon>);
if (first != null && first is Task<JsonDataFromPoseidon>)
{
data = (first as Task<JsonDataFromPoseidon>).Result;
//GET serial number JsonDataFromPoseidon data = null;
var firstTaskInfo = CliRunner.TaskPool
.FindLast(t => t.Task is Task<JsonDataFromPoseidon> && t.UseResult);
if (firstTaskInfo != null)
{
var task = (Task<JsonDataFromPoseidon>)firstTaskInfo.Task;
data = task.Result;
}
else
{
if (_lastOpTimedOut)
{
log.Warn($"PoseidonReader {Name}: ReadDatastream timed out, no completed result available.");
}
else
{
log.Warn("No completed JsonDataFromPoseidon task available.");
}
}
if (data != null)
{
if (string.IsNullOrEmpty(wmSerialNr)) if (string.IsNullOrEmpty(wmSerialNr))
{ {
try try
{ {
wmSerialNr = data?.DeviceId ?? wmSerialNr; wmSerialNr = data.DeviceId ?? wmSerialNr;
} }
catch (Exception e) catch (Exception e)
{ {
log.Error("Serial Nr - parse error!"); log.Error("Serial Nr - parse error!", e);
} }
} }
//GET volume double volume;
if (data != null && Double.TryParse(data.Reading, out double Volume)) if (Double.TryParse(data.Reading, out volume))
{ {
double VolumeLi = Units.ConvertFrom(Unit.USgal, Volume); double volumeLi = Units.ConvertFrom(Unit.USgal, volume);
//double VolumeM3 = Units.ConvertTo(Unit.m3, VolumeLi);
if (_isReadingStart)
{
//volume
beginWMState = VolumeLi;
}
else
{
//volume
endWMState = VolumeLi;
}
}
if (_isReadingStart)
beginWMState = volumeLi;
else
endWMState = volumeLi;
}
} }
//Finish reading and loop CliRunner.Clear();
_currentOp = CurrentPoseidonOp.Done; _currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
return Event.Done; return Event.Done;
} }
return Event.None; return Event.None;
} }
/// <summary>Stop this operation</summary> /// <summary>Stop this operation</summary>
public void Stop() public void Stop()
{ {
+35 -7
View File
@@ -678,6 +678,7 @@ namespace TBF.Rig.Sequences
/// On error or when STOP pressed /// On error or when STOP pressed
/// ///
foreach (var fmPump in PumpsWithFM) fmPump.TurnOff(); foreach (var fmPump in PumpsWithFM) fmPump.TurnOff();
log.Debug("STOP or ERROR: Pumps with FM stopped!");
if (inPath != null) if (inPath != null)
{ {
@@ -1275,22 +1276,49 @@ namespace TBF.Rig.Sequences
var smryItems = DEItem.GetSummaryColumns(); var smryItems = DEItem.GetSummaryColumns();
for (int i = 0; i < BatchRslts.WMPositionsCount; i++) for (int i = 0; i < BatchRslts.Batch.WaterMeters.Count; i++)
{ {
if (BatchRslts.Batch.WaterMeters != null && if (BatchRslts.Batch.WaterMeters != null &&
BatchRslts.Batch.WaterMeters.Count > i && BatchRslts.Batch.WaterMeters.Count > i &&
BatchRslts.Batch.WaterMeters[i] != null && BatchRslts.Batch.WaterMeters[i] != null)
!BatchRslts.Batch.WaterMeters[i].Disabled)
{ {
bool Disabled = BatchRslts.Batch.WaterMeters[i].Disabled;
bool bChanel = false;
if (Disabled &&
BatchRslts.Batch.WaterMeters[i].SerialNr != null &&
BatchRslts.Batch.WaterMeters[i].SerialNr.Contains("_CH")) //write channels
{
Disabled = false;
bChanel = true;
}
if (Disabled)
continue;
var wm = BatchRslts.Batch.WaterMeters[i]; var wm = BatchRslts.Batch.WaterMeters[i];
if (!wm.Compound() && !wm.HeatMeter()) if (!wm.Compound() && !wm.HeatMeter())
{ {
/// If this is a single meter /// If this is a single meter
Results.Entities.MeterTestRslt mtrRslt = null;
bool wmDisabled = wm.Disabled;
try
{
wm.Disabled = false;
if (!bChanel)
mtrRslt = ProcessData.BatchRslts.GetMeterTestRslt(tstRslt.Name(), i, CompoundMeterId.Single);
else
{
mtrRslt = wm.GetMeterTestRslt(tstRslt.Name(), CompoundMeterId.Single);
}
}
catch (Exception ex) { }
finally
{
wm.Disabled = wmDisabled;
}
Results.Entities.MeterTestRslt mtrRslt = ProcessData.BatchRslts.GetMeterTestRslt(tstRslt.Name(), i, CompoundMeterId.Single); if (mtrRslt != null)
if (mtrRslt != null)
{ {
bool isCamera = (mtrRslt.RegReaderType == (int)RegisterReaderType.Camera); bool isCamera = (mtrRslt.RegReaderType == (int)RegisterReaderType.Camera);
@@ -13,6 +13,7 @@ using TBF.Rig.GenericDevices;
using TBF.Boxes; using TBF.Boxes;
using TBF.Resources; using TBF.Resources;
using TBF.Rig.TestMethods.GenesisCommunication.GenesisHead; using TBF.Rig.TestMethods.GenesisCommunication.GenesisHead;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
using TBF.UiBridge; using TBF.UiBridge;
namespace TBF.Rig.TestMethods.FlyingStart namespace TBF.Rig.TestMethods.FlyingStart
@@ -329,6 +330,9 @@ namespace TBF.Rig.TestMethods.FlyingStart
temperature_set: temperature_set:
//TODO genesis - check with genesis switch //TODO genesis - check with genesis switch
if (atleastOneGenesis) if (atleastOneGenesis)
{ {
@@ -379,18 +383,26 @@ namespace TBF.Rig.TestMethods.FlyingStart
IList<IOperation> cameraMeasurementOps = new List<IOperation>(); IList<IOperation> cameraMeasurementOps = new List<IOperation>();
foreach (var camera in cameras) cameraMeasurementOps.Add(camera.MeasurementOp()); foreach (var camera in cameras) cameraMeasurementOps.Add(camera.MeasurementOp());
foreach (var rr in sensPath.RegisterReaders) /// Prepare datastream read operations
{ IList<IOperation> readDatastreamOps = new List<IOperation>();
if (rr is IRegReaderDatastream) if (sensPath != null && sensPath.RegisterReaders != null)
(rr as IRegReaderDatastream).TestIsGoingToStartSoon(test, repetitionNr); {
foreach (var rr in sensPath.RegisterReaders)
{
if (rr is IRegReaderDatastream datastreamRR)
{
(datastreamRR).TestIsGoingToStartSoon(test, repetitionNr);
readDatastreamOps.Add(datastreamRR.ReadDatastreamOp());
}
if (rr is GenesisHead) // Genesis - CORDONEL head if (rr is GenesisHead) // Genesis - CORDONEL head
(rr as GenesisHead).StartRead(test.Name, BatchRslts.Batch.BatchNr, repetitionNr); (rr as GenesisHead).StartRead(test.Name, BatchRslts.Batch.BatchNr, repetitionNr);
} }
}
//------------------------------------------------ //------------------------------------------------
Bridge.OnActivity(this, Strings.Test_in_progress); Bridge.OnActivity(this, Strings.Test_in_progress);
//------------------------------------------------ //------------------------------------------------
@@ -403,6 +415,7 @@ namespace TBF.Rig.TestMethods.FlyingStart
State.Create(string.Format("{0}({1}) : FlyingStartStopTestOp is running", test.Method, test.Name)) State.Create(string.Format("{0}({1}) : FlyingStartStopTestOp is running", test.Method, test.Name))
.AddOperation(checkUiOp) .AddOperation(checkUiOp)
.AddOperations(readTempPressOps) .AddOperations(readTempPressOps)
.AddOperations(readDatastreamOps)
.AddOperations(cameraMeasurementOps) .AddOperations(cameraMeasurementOps)
.AddOperation(cBrd.FlyingStartStopTestOp(test, test.QfromM3ph(), test.QtoM3ph(), totalPulses)) .AddOperation(cBrd.FlyingStartStopTestOp(test, test.QfromM3ph(), test.QtoM3ph(), totalPulses))
.AddOperation(processDataLoggingOp) .AddOperation(processDataLoggingOp)
@@ -455,6 +468,21 @@ namespace TBF.Rig.TestMethods.FlyingStart
/// Measurement loop end /// Measurement loop end
StopRecordingStatistics(); StopRecordingStatistics();
/// call StopDataStreamProcessing
if (sensPath != null && sensPath.RegisterReaders != null)
{
foreach (var rr in sensPath.RegisterReaders)
{
var datastreamRR = rr as ISmartReader;
if (datastreamRR != null)
{
datastreamRR.StopDataStreamProcessing();
}
}
}
//TODO genesis - check with genesis switch //TODO genesis - check with genesis switch
if (atleastOneGenesis) if (atleastOneGenesis)
{ {
@@ -8,11 +8,14 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization; using System.Xml.Serialization;
using Results.Entities;
using TBF.Boxes; using TBF.Boxes;
using TBF.Resources; using TBF.Resources;
using TBF.Rig; using TBF.Rig;
using TBF.Rig.GenericDevices; using TBF.Rig.GenericDevices;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
using TBF.Rig.RegisterReaders.PoseidonReader; using TBF.Rig.RegisterReaders.PoseidonReader;
using TBF.Rig.RegisterReaders.PoseidonReader.implementations; using TBF.Rig.RegisterReaders.PoseidonReader.implementations;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
@@ -170,8 +173,8 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
string testName = Results.Utils.GetTestName(test.Name, test.Repeats, repetitionNr); string testName = Results.Utils.GetTestName(test.Name, test.Repeats, repetitionNr);
TestStartTime = DateTime.Now; TestStartTime = DateTime.Now;
bool atleastOneGenesis = false; // bool atleastOneGenesis = false;
atleastOneGenesis = GenesisHeadBatch.Start(sensPath.RegisterReaders, Program.LocalSettings.LastSNTexts); // atleastOneGenesis = GenesisHeadBatch.Start(sensPath.RegisterReaders, Program.LocalSettings.LastSNTexts);
//------------------------------------------------ //------------------------------------------------
Bridge.OnActivity(this, Strings.Checking_tank_capacity); Bridge.OnActivity(this, Strings.Checking_tank_capacity);
@@ -452,23 +455,23 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
} }
} }
if (atleastOneGenesis) // if (atleastOneGenesis)
{ // {
log.Info($"Genesis - Starting... Test Name:{test.Name.ToLower()}."); // log.Info($"Genesis - Starting... Test Name:{test.Name.ToLower()}.");
if (test.Name.ToLower().Contains("calib")) // if (test.Name.ToLower().Contains("calib"))
{ // {
log.Info("Genesis - Calibration starting."); // log.Info("Genesis - Calibration starting.");
GenesisHeadBatch.BatchHolder.Value.MetersLogin(); // GenesisHeadBatch.BatchHolder.Value.MetersLogin();
GenesisHeadBatch.BatchHolder.Value.MetersInitCalibration(); // GenesisHeadBatch.BatchHolder.Value.MetersInitCalibration();
//
} // }
if (test.Name.ToLower().Contains("init")) // if (test.Name.ToLower().Contains("init"))
{ // {
log.Info("Genesis - init starting."); // log.Info("Genesis - init starting.");
GenesisHeadBatch.BatchHolder.Value.MetersLogin(); // GenesisHeadBatch.BatchHolder.Value.MetersLogin();
GenesisHeadBatch.BatchHolder.Value.MetersInitMeasurement(); // GenesisHeadBatch.BatchHolder.Value.MetersInitMeasurement();
} // }
} // }
if (drainTheTank) if (drainTheTank)
{ {
@@ -607,22 +610,40 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
} }
while (!e.Contains(Event.TestCompleted) && !e.Contains(Event.Next)); /// 'Next' button is enabled in Debug version only while (!e.Contains(Event.TestCompleted) && !e.Contains(Event.Next)); /// 'Next' button is enabled in Debug version only
//------------------------------------------------
Bridge.OnActivity(this, Strings.Test_in_calculation);
//------------------------------------------------
/// Measurement loop end /// Measurement loop end
StopRecordingStatistics(); StopRecordingStatistics();
if (atleastOneGenesis)
{
if (test.Name.ToLower().Contains("calib")) if (sensPath != null && sensPath.RegisterReaders != null)
{ {
log.Info("Genesis - Calibration stopped."); foreach (var rr in sensPath.RegisterReaders)
GenesisHeadBatch.BatchHolder.Value.MetersStopCalibration(); {
} var datastreamRR = rr as ISmartReader;
else if (datastreamRR != null)
{ {
log.Info("Genesis - Init measurement stopped."); datastreamRR.StopDataStreamProcessing();
GenesisHeadBatch.BatchHolder.Value.MetersStopMeasurement(); }
} }
} }
// if (atleastOneGenesis)
// {
// if (test.Name.ToLower().Contains("calib"))
// {
// log.Info("Genesis - Calibration stopped.");
// GenesisHeadBatch.BatchHolder.Value.MetersStopCalibration();
// }
// else
// {
// log.Info("Genesis - Init measurement stopped.");
// GenesisHeadBatch.BatchHolder.Value.MetersStopMeasurement();
// }
// }
/// ///
/// (Berlin:) Water is stopped immediately after the test and before the 2nd mass measurement in case: /// (Berlin:) Water is stopped immediately after the test and before the 2nd mass measurement in case:
@@ -955,6 +976,7 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
} }
else else
{ {
bool bUpgradeCountOfMeters = false;
/// ///
/// Single meters /// Single meters
/// ///
@@ -969,6 +991,7 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
TestMethods.iPerlCommunication.iPerlHead.IperlHead iPerl = regReader as TestMethods.iPerlCommunication.iPerlHead.IperlHead; TestMethods.iPerlCommunication.iPerlHead.IperlHead iPerl = regReader as TestMethods.iPerlCommunication.iPerlHead.IperlHead;
GenericDevices.IRegReaderLiveCamera cameraRoi = regReader as GenericDevices.IRegReaderLiveCamera; GenericDevices.IRegReaderLiveCamera cameraRoi = regReader as GenericDevices.IRegReaderLiveCamera;
TestMethods.GenesisCommunication.GenesisHead.GenesisHead Genesis = regReader as TestMethods.GenesisCommunication.GenesisHead.GenesisHead; TestMethods.GenesisCommunication.GenesisHead.GenesisHead Genesis = regReader as TestMethods.GenesisCommunication.GenesisHead.GenesisHead;
TBF.Rig.RegisterReaders.GenesisRegReader.implementations.GenesisSmartReader GenesisSmart = regReader as TBF.Rig.RegisterReaders.GenesisRegReader.implementations.GenesisSmartReader;
if (meterRslt != null && regReader != null) if (meterRslt != null && regReader != null)
{ {
@@ -1057,6 +1080,23 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
var calError = Formulas.ErrorFromVolumes(meterRslt.VolumeMeter, meterRslt.VolumeRef); var calError = Formulas.ErrorFromVolumes(meterRslt.VolumeMeter, meterRslt.VolumeRef);
Genesis.Log(" MeterError = " + calError.ToString() + " %"); Genesis.Log(" MeterError = " + calError.ToString() + " %");
}
else if (GenesisSmart != null)
{
log.Debug("GenesisSmart - store data on end!");
bUpgradeCountOfMeters = true;
int CH1=0, CH2=1, CH3=2;
//BatchRslts.Batch.WaterMeters.Add(ch1WaterMeter); //[i + BatchRslts.WMPositionsCount + CH1 + 1]
//BatchRslts.Batch.WaterMeters.Add(ch1WaterMeter); //[i + BatchRslts.WMPositionsCount + CH2 + 1]
//BatchRslts.Batch.WaterMeters.Add(ch1WaterMeter); //[i + BatchRslts.WMPositionsCount + CH3 + 1]
CalculateMeterResults(meterRslt, GenesisSmart, tstRslt,GenesisSmart.TimestampSecStart, GenesisSmart.TimestampSecEnd, GenesisSmart.VolumeLtrStart, GenesisSmart.VolumeLtrEnd);
WaterMeterParentCopy(i, CH1,testName,meterRslt,GenesisSmart,tstRslt);
WaterMeterParentCopy(i, CH2,testName,meterRslt,GenesisSmart,tstRslt);
WaterMeterParentCopy(i, CH3,testName,meterRslt,GenesisSmart,tstRslt);
} }
else if (dstrReader != null) else if (dstrReader != null)
{ {
@@ -1241,8 +1281,16 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
#endif #endif
} }
} }
if (bUpgradeCountOfMeters)
{
//TODO remove test!!
//BatchRslts.WMPositionsCount = BatchRslts.WMPositionsCount + 1;
}
} }
tstRslt.Components = Results.Entities.Components.UpdateList(BatchRslts.ComponentsList, tstRslt.Components = Results.Entities.Components.UpdateList(BatchRslts.ComponentsList,
new Results.Entities.Components((BenchInfo != null) ? BenchInfo.TestBenchId : 1, new Results.Entities.Components((BenchInfo != null) ? BenchInfo.TestBenchId : 1,
(BenchInfo != null) ? BenchInfo.TestBenchName : "testbench", (BenchInfo != null) ? BenchInfo.TestBenchName : "testbench",
@@ -1300,6 +1348,98 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
return new List<Event> { retVal }; return new List<Event> { retVal };
} }
private const int CountCh = 3;
private static void WaterMeterParentCopy(int i, int iCH, string testName, MeterTestRslt meterRslt,
GenesisSmartReader genesisSmart,
TestRslt tstRslt)
{
WaterMeter waterMeterParent = BatchRslts.Batch.WaterMeters[i];
int wmNrChX = (i*CountCh) + BatchRslts.WMPositionsCount + iCH + 1;
String sSerialNr = waterMeterParent.SerialNr + "_CH" + (iCH + 1);
WaterMeter chXWaterMeter = null;
//------ add new water meter to batch ------
if (BatchRslts.Batch.WaterMeters.Count < wmNrChX || BatchRslts.Batch.WaterMeters[wmNrChX-1] == null)
{
log.Debug("add new water meter to batch, CH = " + (iCH + 1) + " CH = " + wmNrChX);
chXWaterMeter = new WaterMeter()
{
Batch = BatchRslts.Batch,
WaterMeterData = waterMeterParent.WaterMeterData,
MeterTestRslts = new List<MeterTestRslt>(),
SerialNr = sSerialNr,
WMPosition = wmNrChX,
YearOfProduction = 0,
Disabled = false,
};
chXWaterMeter.CopyContentFrom(waterMeterParent);
chXWaterMeter.SerialNr = sSerialNr;
chXWaterMeter.WMPosition = wmNrChX;
chXWaterMeter.YearOfProduction = waterMeterParent.YearOfProduction;
chXWaterMeter.Disabled = false;
BatchRslts.Batch.WaterMeters.Add(chXWaterMeter);
}
else
{
chXWaterMeter = BatchRslts.Batch.WaterMeters[wmNrChX-1];
}
//~------ add new water meter to batch ------~
//create copy of meterRslt and add to additionalResultsByChannel
MeterTestRslt meterTestRsltChX = new MeterTestRslt(chXWaterMeter, meterRslt.TestRslt, (CompoundMeterId)meterRslt.CompoundMeterId);
chXWaterMeter.MeterTestRslts.Add(meterTestRsltChX);
MeterTestRslt chanelXMeterRslt = BatchRslts.GetMeterTestRslt(testName, wmNrChX-1, Common.CompoundMeterId.Single);
if (chanelXMeterRslt != null)
{
chanelXMeterRslt?.CopyContentFrom(meterRslt);
if (iCH == 0)
{
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh1,
genesisSmart.TimestampSecEndRawCh1, genesisSmart.VolumeLtrStartRawCh1, genesisSmart.VolumeLtrEndRawCh1);
}
else if (iCH == 1)
{
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh2,
genesisSmart.TimestampSecEndRawCh2, genesisSmart.VolumeLtrStartRawCh2, genesisSmart.VolumeLtrEndRawCh2);
}
else if (iCH == 2)
{
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartRawCh3,
genesisSmart.TimestampSecEndRawCh3, genesisSmart.VolumeLtrStartRawCh3, genesisSmart.VolumeLtrEndRawCh3);
}
if (!genesisSmart.EnableShowChanels)
{
chXWaterMeter.Disabled = !genesisSmart.EnableShowChanels;
meterTestRsltChX.TestDone = false;
}
else
{
meterTestRsltChX.TestDone = true;
}
meterTestRsltChX.Passed = meterRslt.Passed;
}
}
private static void CalculateMeterResults(MeterTestRslt meterRslt, IRegReaderDatastream dstrReader, TestRslt tstRslt, double dstrReaderTimestampSecStart, double dstrReaderTimestampSecEnd, double dstrReaderVolumeLtrStart, double dstrReaderVolumeLtrEnd)
{
meterRslt.TimestampStart = dstrReaderTimestampSecStart;
meterRslt.TimestampEnd = !dstrReader.NoSamples
? dstrReaderTimestampSecEnd
: (dstrReaderTimestampSecStart + tstRslt.TestTime);
meterRslt.TestTime = meterRslt.TimestampEnd - meterRslt.TimestampStart;
meterRslt.VolumeStart = dstrReaderVolumeLtrStart; /// liter
meterRslt.VolumeEnd = dstrReaderVolumeLtrEnd; /// liter
meterRslt.VolumeMeter =
Math.Abs(dstrReaderVolumeLtrEnd - dstrReaderVolumeLtrStart);
meterRslt.VolumeRef = tstRslt.TestTime==0? tstRslt.VolumeCTV : tstRslt.VolumeCTV * meterRslt.TestTime / tstRslt.TestTime;
meterRslt.PulsesMaster = tstRslt.TestTime == 0? tstRslt.PulsesMaster :
tstRslt.PulsesMaster * meterRslt.TestTime / tstRslt.TestTime;
}
IList<Event> Simulate1(Config.Entities.Test test, int repetitionNr, bool isLastRepetition, IList<Event> Simulate1(Config.Entities.Test test, int repetitionNr, bool isLastRepetition,
Compound.CombinedTestParams compoundTestParams, Compound.CombinedTestParams compoundTestParams,
@@ -15,8 +15,7 @@ using Xylem.Common.Metrology.Measurements;
using Xylem.Common.Metrology.Measurements.Consts; using Xylem.Common.Metrology.Measurements.Consts;
using Common; using Common;
using System.IO.Ports; using System.IO.Ports;
using GenesisMeter = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter;
using MeterBatch = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.WaterMeterCore.MeterBatch;
namespace TBF.Rig.TestMethods.GenesisCommunication.GenesisHead namespace TBF.Rig.TestMethods.GenesisCommunication.GenesisHead
{ {
+11 -10
View File
@@ -6,20 +6,21 @@ using TBF.Rig.Generic;
namespace TBF.Rig.Uni.FlowMetersInParallel namespace TBF.Rig.Uni.FlowMetersInParallel
{ {
public class Factory : IComponentFactory public class Factory : IComponentFactory
{ {
public string ClassName { get { return GetType().Namespace.Substring(12); } } public string ClassName { get { return GetType().Namespace.Substring(12); } }
public override string ToString() { return ClassName; } 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) public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{ {
return ComponentCfgBase.CreateFromDbEntity(FlowMeterCfg.Serializer, component, this); return ComponentCfgBase.CreateFromDbEntity(FlowMeterCfg.Serializer, component, this);
} }
} }
} }
+60 -49
View File
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using log4net; using log4net;
using SchematicDrawing; using SchematicDrawing;
using SharedComponents;
using TBF.Boxes; using TBF.Boxes;
using TBF.Rig.ControlBoard.Uni; using TBF.Rig.ControlBoard.Uni;
using TBF.Rig.GenericDevices; using TBF.Rig.GenericDevices;
@@ -12,17 +13,16 @@ using TBF.Rig.GenericDevices;
namespace TBF.Rig.Uni.FlowMetersInParallel namespace TBF.Rig.Uni.FlowMetersInParallel
{ {
public class FlowMeter : ComponentBase, IFlowMeter, IDrawingItCmpntWithMeasuredVal 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)); } public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
readonly FlowMeterCfg myCfg; readonly FlowMeterCfg flowMeterCfg;
UniCB uniCB; UniCB uniCB;
IFlowMeterSingle flowMtr1; IFlowMeterSingle flowMtr1;
IFlowMeterSingle flowMtr2; IFlowMeterSingle flowMtr2;
IFlowMeterSingle flowMtr3; IFlowMeterSingle flowMtr3;
IFlowMeterSingle inactiveFlowMtr;
double nominalFlow; double nominalFlow;
double nominalFreq; double nominalFreq;
double ltrPerPulse; double ltrPerPulse;
@@ -34,22 +34,20 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
public double NominalFreq { get { return nominalFreq; } } public double NominalFreq { get { return nominalFreq; } }
public double LtrPerPulse { get { return ltrPerPulse; } } public double LtrPerPulse { get { return ltrPerPulse; } }
public SchematicDrawing.IDrawingItem DrawingItem { get { return myCfg as SchematicDrawing.IDrawingItem; } } public SchematicDrawing.IDrawingItem DrawingItem { get { return flowMeterCfg as SchematicDrawing.IDrawingItem; } }
public string MsrdFormat { get { return myCfg.MsrdFormat; } } public string MsrdFormat { get { return flowMeterCfg.MsrdFormat; } }
public Common.Unit MsrdUnit { get { return myCfg.MsrdUnit; } } public Common.Unit MsrdUnit { get { return flowMeterCfg.MsrdUnit; } }
public double MsrdValLimLo { get { return myCfg.MsrdValLimLo; } set { myCfg.MsrdValLimLo = value; } } public double MsrdValLimLo { get { return flowMeterCfg.MsrdValLimLo; } set { flowMeterCfg.MsrdValLimLo = value; } }
public double MsrdValLimHi { get { return myCfg.MsrdValLimHi; } set { myCfg.MsrdValLimHi = value; } } public double MsrdValLimHi { get { return flowMeterCfg.MsrdValLimHi; } set { flowMeterCfg.MsrdValLimHi = value; } }
public bool MsrmntAvailable public bool MsrmntAvailable
{ {
get get
{ {
bool result = true; bool result = true;
if (flowMtr1 != null) result = (result && flowMtr1.MsrmntAvailable); if (flowMtr1 != null) result = result && flowMtr1.MsrmntAvailable;
if (flowMtr2 != null) result = (result && flowMtr2.MsrmntAvailable); if (flowMtr2 != null) result = result && flowMtr2.MsrmntAvailable;
if (flowMtr3 != null) result = (result && flowMtr3.MsrmntAvailable); if (flowMtr3 != null) result = result && flowMtr3.MsrmntAvailable;
if (inactiveFlowMtr != null) result = (result && !inactiveFlowMtr.MsrmntAvailable);
return result; return result;
} }
} }
@@ -61,24 +59,22 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
public string AltString { get { return "Invalid format"; } } public string AltString { get { return "Invalid format"; } }
public FlowMeter() { }
public FlowMeter() { } public FlowMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
: base(cfg)
public FlowMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components) {
: base(cfg) flowMeterCfg = cfg as FlowMeterCfg;
{ }
myCfg = cfg as FlowMeterCfg;
}
public override void Initialize() 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 (uniCB == null) throw new Exception("Cannot find " + Name + " parent");
if (!string.IsNullOrEmpty(myCfg.Flowmeter1)) flowMtr1 = TbfComponents.FindComponent(myCfg.Flowmeter1) as IFlowMeterSingle; if (!string.IsNullOrEmpty(flowMeterCfg.Flowmeter1)) flowMtr1 = TbfComponents.FindComponent(flowMeterCfg.Flowmeter1) as IFlowMeterSingle;
if (!string.IsNullOrEmpty(myCfg.Flowmeter2)) flowMtr2 = TbfComponents.FindComponent(myCfg.Flowmeter2) as IFlowMeterSingle; if (!string.IsNullOrEmpty(flowMeterCfg.Flowmeter2)) flowMtr2 = TbfComponents.FindComponent(flowMeterCfg.Flowmeter2) as IFlowMeterSingle;
if (!string.IsNullOrEmpty(myCfg.Flowmeter3)) flowMtr3 = TbfComponents.FindComponent(myCfg.Flowmeter3) as IFlowMeterSingle; if (!string.IsNullOrEmpty(flowMeterCfg.Flowmeter3)) flowMtr3 = TbfComponents.FindComponent(flowMeterCfg.Flowmeter3) as IFlowMeterSingle;
if (!string.IsNullOrEmpty(myCfg.InactiveFlowmtr)) inactiveFlowMtr = TbfComponents.FindComponent(myCfg.InactiveFlowmtr) as IFlowMeterSingle;
nominalFlow = 0; nominalFlow = 0;
flowMetersCount = 0; flowMetersCount = 0;
@@ -109,15 +105,31 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
MsrdValLimHi = nominalFlow; MsrdValLimHi = nominalFlow;
nominalFreq = nominalFreq / flowMetersCount;
ltrPerPulse = nominalFlow / (3.6 * nominalFreq); /// Nominal freq. is sum of particular nominal frquencies 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}", 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() 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() public double ReadFrequency()
@@ -125,27 +137,26 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
return uniCB.RefFrequency; return uniCB.RefFrequency;
} }
/// <summary> /// <summary>
/// Events: FlowDone, Error /// Events: FlowDone, Error
/// </summary> /// </summary>
/// <param name="flow">Reference to a variable for the flow in Bar</param> /// <param name="flow">Reference to a variable for the flow in Bar</param>
/// <returns>ReadFlowOp instance reference casted to IOperaton</returns> /// <returns>ReadFlowOp instance reference casted to IOperaton</returns>
public IOperation ReadFlowOp(ref DoubleBox flow) public IOperation ReadFlowOp(ref DoubleBox flow)
{ {
return new ReadFlowOp(this, ref 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>
/// <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 flowRawSum;
double flowSum; double flowSum;
@@ -197,7 +208,7 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
public double LtrPerPulseCorrected(double flow, float temperature) public double LtrPerPulseCorrected(double flow, float temperature)
{ {
double ltrPerPulseCorrected = (flowRawSum == 0) ? LtrPerPulse : LtrPerPulse * flowSum / flowRawSum; 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; return ltrPerPulseCorrected;
} }
} }
@@ -13,9 +13,9 @@ using TBF.Rig.Generic;
namespace TBF.Rig.Uni.FlowMetersInParallel namespace TBF.Rig.Uni.FlowMetersInParallel
{ {
public class FlowMeterCfg : ComponentCfgBase, IChildComponentCfg, IParamsProvider, IDrawingItemWithMeasuredVal public class FlowMeterCfg : ComponentCfgBase, IChildComponentCfg, IParamsProvider, IDrawingItemWithMeasuredVal
{ {
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(FlowMeterCfg) })[0]; public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(FlowMeterCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; } public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities) public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities)
{ {
@@ -24,15 +24,14 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
return new Configs.ParamsProvider.ComponentCfgCtrl(this, parents); return new Configs.ParamsProvider.ComponentCfgCtrl(this, parents);
} }
/// ///
/// Serialized parameters /// Serialized parameters
/// ///
public string Flowmeter1; /// 0 Flowmeter component name or string.Empty public string Flowmeter1; /// 0 Flowmeter component name or string.Empty
public string Flowmeter2; /// 1 - ' ' - public string Flowmeter2; /// 1 - ' ' -
public string Flowmeter3; /// 2 - ' ' - public string Flowmeter3; /// 2 - ' ' -
public string InactiveFlowmtr; /// 3 Inactive flowmeter component name or string.Empty public string MsrdFormat { get; set; } /// 3
public string MsrdFormat { get; set; } /// 4 public Unit MsrdUnit { get; set; } /// 4
public Unit MsrdUnit { get; set; } /// 5
/// Schematic drawing info /// Schematic drawing info
public Shape Shape { get; set; } public Shape Shape { get; set; }
@@ -58,9 +57,6 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
private IEnumerable<Config.Entities.Component> flowMeters; private IEnumerable<Config.Entities.Component> flowMeters;
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors /// Private parameterless constructor invoked by all other (public) constructors
FlowMeterCfg() FlowMeterCfg()
{ {
@@ -82,12 +78,13 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
public string ComponentName { get { return Name; } } public string ComponentName { get { return Name; } }
public bool IsOffline { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public void InitializeAll() public void InitializeAll()
{ {
Flowmeter1 = "I11"; Flowmeter1 = "I11";
Flowmeter2 = "I12"; Flowmeter2 = "I12";
Flowmeter3 = string.Empty; Flowmeter3 = string.Empty;
InactiveFlowmtr = "I13";
MsrdFormat = "{0:F3} m3/h"; MsrdFormat = "{0:F3} m3/h";
MsrdUnit = Unit.m3ph; MsrdUnit = Unit.m3ph;
} }
@@ -97,9 +94,8 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
"Flow meter 1", /// 0 "Flow meter 1", /// 0
"Flow meter 2", /// 1 "Flow meter 2", /// 1
"Flow meter 3", /// 2 "Flow meter 3", /// 2
"Inactive flow meter", /// 3 "Display format", /// 3
"Display format", /// 4 "Unit of flow on a display", /// 4
"Unit of flow on a display", /// 5
}; };
public string ParamName(int i) { return paramNames[i]; } public string ParamName(int i) { return paramNames[i]; }
public int ParamsCount() { return paramNames.Length; } public int ParamsCount() { return paramNames.Length; }
@@ -111,48 +107,44 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
case 0: case 0:
case 1: case 1:
case 2: case 2:
case 3:
if (flowMeters == null) return null; if (flowMeters == null) return null;
var fmtrs = new List<string>(); var fmtrs = new List<string>();
foreach (var fmtr in flowMeters) fmtrs.Add(fmtr.Name); foreach (var fmtr in flowMeters) fmtrs.Add(fmtr.Name);
fmtrs.Add("---"); fmtrs.Add("---");
return fmtrs; return fmtrs;
case 5: case 4:
return new string[] { "m3/h", "l/h", "gal/m" }; return new string[] { "m3/h", "l/h" };
default: default:
return null; return null;
} }
} }
public string ToString(int i) public string ToString(int i)
{ {
switch (i) switch (i)
{ {
case 0: return string.IsNullOrEmpty(Flowmeter1) ? "---" : Flowmeter1; case 0: return string.IsNullOrEmpty(Flowmeter1) ? "---" : Flowmeter1;
case 1: return string.IsNullOrEmpty(Flowmeter2) ? "---" : Flowmeter2; case 1: return string.IsNullOrEmpty(Flowmeter2) ? "---" : Flowmeter2;
case 2: return string.IsNullOrEmpty(Flowmeter3) ? "---" : Flowmeter3; case 2: return string.IsNullOrEmpty(Flowmeter3) ? "---" : Flowmeter3;
case 3: return string.IsNullOrEmpty(InactiveFlowmtr) ? "---" : InactiveFlowmtr; case 3: return MsrdFormat;
case 4: return MsrdFormat; case 4: return MsrdUnit.ToDescription();
case 5: return MsrdUnit.ToDescription();
default: default:
return string.Format("Name={0} {1} {2} {3} ~{4} fmt={5} unit={6} Lo={7} Hi={8}", return string.Format("Name={0} {1} {2} {3} fmt={4} unit={5} Lo={6} Hi={7}",
Name, Flowmeter1, Flowmeter2, Flowmeter3, InactiveFlowmtr, Name, Flowmeter1, Flowmeter2, Flowmeter3,
MsrdFormat, MsrdUnit.ToDescription(), MsrdValLimLo, MsrdValLimHi); MsrdFormat, MsrdUnit.ToDescription(), MsrdValLimLo, MsrdValLimHi);
} }
} }
public CfgUpdateFlags UpdateParam(int i, string str) public CfgUpdateFlags UpdateParam(int i, string str)
{ {
switch (i) switch (i)
{ {
case 0: Flowmeter1 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd; case 0: Flowmeter1 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
case 1: Flowmeter2 = (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 2: Flowmeter3 = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd;
case 3: InactiveFlowmtr = (str != "---") ? str : string.Empty; return CfgUpdateFlags.RestartRqrd; case 3: MsrdFormat = str; return CfgUpdateFlags.RestartRqrd;
case 4: MsrdFormat = str; return CfgUpdateFlags.RestartRqrd; case 4: MsrdUnit = (str == "l/h") ? Unit.lph : Unit.m3ph; return CfgUpdateFlags.RestartRqrd;
//case 5: MsrdUnit = (str == "l/h") ? Unit.lph : Unit.m3ph; return CfgUpdateFlags.RestartRqrd;
case 5: MsrdUnit = ParseFlowUnit(str); return CfgUpdateFlags.RestartRqrd;
default: default:
return CfgUpdateFlags.None; return CfgUpdateFlags.None;
} }
@@ -167,11 +159,10 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
case 0: case 0:
case 1: case 1:
case 2: case 2:
case 3: case 4:
case 5:
if (ParamValues(i).Contains(str)) return true; if (ParamValues(i).Contains(str)) return true;
break; break;
case 4: case 3:
return true; return true;
default: default:
message = "Invalid index"; message = "Invalid index";
@@ -187,7 +178,6 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
prms.Flowmeter1 = this.Flowmeter1; prms.Flowmeter1 = this.Flowmeter1;
prms.Flowmeter2 = this.Flowmeter2; prms.Flowmeter2 = this.Flowmeter2;
prms.Flowmeter3 = this.Flowmeter3; prms.Flowmeter3 = this.Flowmeter3;
prms.InactiveFlowmtr = this.InactiveFlowmtr;
prms.MsrdFormat = this.MsrdFormat; prms.MsrdFormat = this.MsrdFormat;
prms.MsrdUnit = this.MsrdUnit; prms.MsrdUnit = this.MsrdUnit;
} }
@@ -203,19 +193,5 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
{ {
return true; /// =OK, do nothing 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;
}
}
} }
} }
+33 -33
View File
@@ -8,57 +8,57 @@ using TBF.Rig.GenericDevices;
namespace TBF.Rig.Uni.FlowMetersInParallel namespace TBF.Rig.Uni.FlowMetersInParallel
{ {
public class ReadFlowOp : IOperation public class ReadFlowOp : IOperation
{ {
private static readonly ILog log = LogManager.GetLogger(typeof(ReadFlowOp)); private static readonly ILog log = LogManager.GetLogger(typeof(ReadFlowOp));
public override string ToString() { return string.Format("ReadFlowOp({0},.,.,{1})", flowMeter.Idx1, eventDone); } 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 IFlowMeter flowMeter;
readonly DoubleBox flowBox; readonly DoubleBox flowBox;
readonly Event eventDone; readonly Event eventDone;
/// <summary> /// <summary>
/// Events: FlowInDone, FlowOutDone or Error /// Events: FlowInDone, FlowOutDone or Error
/// </summary> /// </summary>
/// <param name="flowMeter">Flow meter reference</param> /// <param name="flowMeter">Flow meter reference</param>
/// <param name="flowBox">Reference to the measured flow variable, value is in bar</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> /// <param name="eventDone">Event to be returned by Run() when completed OK</param>
public ReadFlowOp(IFlowMeter flowMeter, ref DoubleBox flowBox, Event eventDone) public ReadFlowOp(IFlowMeter flowMeter, ref DoubleBox flowBox, Event eventDone)
{ {
if (flowMeter == null) throw new ArgumentNullException(); if (flowMeter == null) throw new ArgumentNullException();
this.flowMeter = flowMeter; this.flowMeter = flowMeter;
this.eventDone = eventDone; this.eventDone = eventDone;
this.flowBox = flowBox; this.flowBox = flowBox;
log.Debug(this.ToString()); log.Debug(this.ToString());
} }
public ReadFlowOp(IFlowMeter flowMeter, ref DoubleBox flowBox) public ReadFlowOp(IFlowMeter flowMeter, ref DoubleBox flowBox)
: this(flowMeter, ref flowBox, Event.FlowMeasurementDone) : this(flowMeter, ref flowBox, Event.FlowMeasurementDone)
{ {
} }
/// <summary>Start this operation</summary> /// <summary>Start this operation</summary>
public void Start() { } public void Start() { }
/// <summary>Run this operation</summary> /// <summary>Run this operation</summary>
/// <returns> /// <returns>
/// Event.FlowInDone or Event.FlowOutDone /// Event.FlowInDone or Event.FlowOutDone
/// </returns> /// </returns>
public Event Run() public Event Run()
{ {
if (flowMeter.MsrmntAvailable) if (flowMeter.MsrmntAvailable)
{ {
if (flowBox != null) flowBox.Val = flowMeter.ReadFlow(); if (flowBox != null) flowBox.Val = flowMeter.ReadFlow();
return eventDone; return eventDone;
} }
return Event.Error; return Event.Error;
} }
/// <summary>Stop this operation</summary> /// <summary>Stop this operation</summary>
public void Stop() { } 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;
using System.Collections.Generic; using System.Collections.Generic;
using log4net; using log4net;
using TBF.Rig.ControlBoard.Uni;
using TBF.Rig.GenericDevices; using TBF.Rig.GenericDevices;
namespace TBF.Rig.Uni.RegValve namespace TBF.Rig.Uni.RegValve
@@ -14,12 +13,12 @@ namespace TBF.Rig.Uni.RegValve
private static readonly ILog log = LogManager.GetLogger(typeof(ChangeRegValvePositionOp)); private static readonly ILog log = LogManager.GetLogger(typeof(ChangeRegValvePositionOp));
public override string ToString() 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 /// Set by the constructor
readonly UniCB uniCB; readonly TBF.Rig.ControlBoard.Uni.UniCB controlBoard;
readonly RegValve regV; readonly RegValve regulValve;
readonly int regulValveNr; readonly int regulValveNr;
readonly double timePulseSec; 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="posHiPct">Upper limit of the position to be achieved</param>
/// <param name="timeout">Timeout in sec. for setting the flow</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> /// <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; controlBoard = cb as TBF.Rig.ControlBoard.Uni.UniCB;
if (this.uniCB == null) throw new ArgumentNullException("ctrlBoard"); if (controlBoard == null) throw new ArgumentNullException("ctrlBoard");
this.regV = regV as RegValve; regulValve = rv as RegValve;
if (this.regV == null) throw new ArgumentNullException("regValve is null or not Uni"); if (regulValve == null) throw new ArgumentNullException("regValve is null or not Elde");
regulValveNr = this.regV.Idx1; regulValveNr = this.regulValve.Idx1;
this.timePulseSec = timePulseSec; this.timePulseSec = timePulseSec;
@@ -50,7 +49,13 @@ namespace TBF.Rig.Uni.RegValve
/// <summary>Start this operation</summary> /// <summary>Start this operation</summary>
public void Start() 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> /// <summary>Run this operation</summary>
@@ -59,6 +64,9 @@ namespace TBF.Rig.Uni.RegValve
/// </returns> /// </returns>
public Event Run() public Event Run()
{ {
//float positionPct = controlBoard.RValvePosition(regulValveNr);
//log.WarnFormat("RV#={0}, actPos={1}%", regulValveNr, positionPct.ToString("F1"));
return Event.PositionReached; return Event.PositionReached;
} }
+1 -1
View File
@@ -1,5 +1,5 @@
/// ///
/// Copyright (c) 2021 Sensus Slovensko a.s. /// Copyright (c) 2021 Sensus Metering Systems
/// ///
using System.Collections.Generic; using System.Collections.Generic;
using TBF.Rig.Generic; using TBF.Rig.Generic;
+1 -1
View File
@@ -1,5 +1,5 @@
/// ///
/// Copyright (c) 2021 Sensus Slovensko a.s. /// Copyright (c) 2021 Sensus Metering Systems
/// ///
using System; using System;
using log4net; using log4net;
+38 -24
View File
@@ -3,6 +3,7 @@
/// ///
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Common;
using log4net; using log4net;
using Config.Entities; using Config.Entities;
using TBF.Rig.ControlBoard.Uni; using TBF.Rig.ControlBoard.Uni;
@@ -19,10 +20,11 @@ namespace TBF.Rig.Uni.RegValve
return string.Format("SetFlowOp({0}, Qfrom={1}, Qto={2})", regV.Name, shrinkedTgtFlowLo, shrinkedTgtFlowHi); return string.Format("SetFlowOp({0}, Qfrom={1}, Qto={2})", regV.Name, shrinkedTgtFlowLo, shrinkedTgtFlowHi);
} }
public const double RqrdFlowRangeRatio = 0.5; public const double RqrdFlowRangeRatio = 0.5;
/// ///
/// Set by the constructor /// Set by the constructor
/// ///
readonly UniCB uniCB; readonly UniCB uniCB;
readonly RegValve regV; readonly RegValve regV;
@@ -36,7 +38,7 @@ namespace TBF.Rig.Uni.RegValve
readonly double maximalFlow; readonly double maximalFlow;
/// ///
/// Internal state of this operation /// Internal state of this operation
/// ///
enum OpState enum OpState
@@ -55,12 +57,13 @@ namespace TBF.Rig.Uni.RegValve
double currentReqFlowLo; double currentReqFlowLo;
double currentReqFlowHi; double currentReqFlowHi;
double targetPositionLo; double targetPositionLo;
double targetPositionHi; double targetPositionHi;
int startTime;
int setFlowTime; int setFlowTime;
int expireTime; int expireTime;
DoubleBox msrdFlow; DoubleBox flowBox;
int isFlowOkDuration; int isFlowOkDuration;
@@ -69,23 +72,23 @@ namespace TBF.Rig.Uni.RegValve
/// Events: FlowSet, FlowTimeOut /// Events: FlowSet, FlowTimeOut
/// </summary> /// </summary>
/// <param name="uniCB">Control board device</param> /// <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="flowMeter">Flowmeter component</param>
/// <param name="qFrom">Lower limit of the flow to be achieved in [m3/h]</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="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="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> /// <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> /// <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) int timeout, int delay, bool leaveFlowControlRunning)
{ {
this.uniCB = uniCB; this.uniCB = uniCB;
if (this.uniCB == null) throw new ArgumentNullException("Control board is null or not Uni"); if (this.uniCB == null) throw new ArgumentNullException("cBoard is null or not Uni");
this.regV = regValve as RegValve; 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; this.flowMeter = flowMeter;
if (this.flowMeter == null) throw new ArgumentNullException("flowMeter"); if (this.flowMeter == null) throw new ArgumentNullException("flowMeter");
@@ -95,14 +98,15 @@ namespace TBF.Rig.Uni.RegValve
double rqrdFlowLoBeforeCorr = qFrom - MeasurementCorrection.GetCorrection(qFrom, flowMeter.Corrections); double rqrdFlowLoBeforeCorr = qFrom - MeasurementCorrection.GetCorrection(qFrom, flowMeter.Corrections);
double rqrdFlowHiBeforeCorr = qTo - MeasurementCorrection.GetCorrection(qTo, flowMeter.Corrections); double rqrdFlowHiBeforeCorr = qTo - MeasurementCorrection.GetCorrection(qTo, flowMeter.Corrections);
targetFlowAve = (rqrdFlowLoBeforeCorr + rqrdFlowHiBeforeCorr) / 2; targetFlowAve = (rqrdFlowLoBeforeCorr + rqrdFlowHiBeforeCorr) / 2;
shrinkedTgtFlowLo = (RqrdFlowRangeRatio * rqrdFlowLoBeforeCorr) shrinkedTgtFlowLo = (RqrdFlowRangeRatio * rqrdFlowLoBeforeCorr) + ((1 - RqrdFlowRangeRatio) * targetFlowAve); /// Move the lower limit 15% of the range up
+ ((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
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.timeout = timeout;
this.delay = delay; this.delay = delay;
this.leaveFlowControlRunning = leaveFlowControlRunning; this.leaveFlowControlRunning = leaveFlowControlRunning;
log.Debug(this.ToString()); log.Debug(this.ToString());
@@ -168,13 +172,16 @@ namespace TBF.Rig.Uni.RegValve
return; return;
} }
/// <summary>Start this operation</summary> /// <summary>
/// Start this operation
/// </summary>
public void Start() public void Start()
{ {
log.InfoFormat("SetFlowOp:Start() rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3}", log.InfoFormat("SetFlowOp:Start() rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3}",
regV.Idx1, flowMeter.Idx1, currentReqFlowLo, currentReqFlowHi); regV.Idx1, flowMeter.Idx1, currentReqFlowLo, currentReqFlowHi);
/// Store the current time, etc. /// Store the current time, etc.
startTime = StateMachine.Time;
setFlowTime = StateMachine.Time + delay; setFlowTime = StateMachine.Time + delay;
expireTime = StateMachine.Time + timeout; expireTime = StateMachine.Time + timeout;
if (expireTime < 0) expireTime = int.MaxValue; if (expireTime < 0) expireTime = int.MaxValue;
@@ -201,6 +208,11 @@ namespace TBF.Rig.Uni.RegValve
{ {
log.DebugFormat("Op.Run() opState={0}", opState); log.DebugFormat("Op.Run() opState={0}", opState);
if (this.uniCB?.DebugLevel == DebugMode.Simulate)
{
return Event.FlowReached;
}
if (StateMachine.Time > expireTime) return Event.RegulValveTimeOut; if (StateMachine.Time > expireTime) return Event.RegulValveTimeOut;
if (opState == OpState.Wait4FlowMsrmntAndSendRVMove) if (opState == OpState.Wait4FlowMsrmntAndSendRVMove)
@@ -267,16 +279,17 @@ namespace TBF.Rig.Uni.RegValve
/// ///
double frequency = flowMeter.ReadFrequency(); double frequency = flowMeter.ReadFrequency();
double flow = flowMeter.ReadFlow(); 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) if (isFlowOkDuration >= regV.FlowStableSec)
{ {
/// Flow is within range for sufficiently long time /// Flow is within range for sufficiently long time
if (regV.StoredPositionReuse) if (regV.regValveCfg.StoredPositionReuse)
{ {
double rvPosition = regV.Position; /// Read the current position double rvPosition = regV.Position; /// Read the current position
double storedPosition; double storedPosition;
@@ -323,12 +336,13 @@ namespace TBF.Rig.Uni.RegValve
} }
} }
/// <summary>Stop this operation</summary> /// <summary>Start this operation</summary>
public void Stop() public void Stop()
{ {
if (!leaveFlowControlRunning) if (!leaveFlowControlRunning)
{ {
uniCB.StopFlowControl(false, regV.Idx1); /// Stop the flow measurement /// Stop flow measurement
uniCB.StopFlowControl(false, regV.Idx1);
} }
opState = OpState.Idle; opState = OpState.Idle;
+25 -8
View File
@@ -71,7 +71,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath> <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> <Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@@ -1049,7 +1049,9 @@
<Compile Include="Rig\Operations\LargeMessageBoxOp.cs" /> <Compile Include="Rig\Operations\LargeMessageBoxOp.cs" />
<Compile Include="Rig\Operations\ReturnGivenEventOp.cs" /> <Compile Include="Rig\Operations\ReturnGivenEventOp.cs" />
<Compile Include="Rig\Output\DB\DatabaseWriter\Factory.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\WriteDBCfgCtrl.designer.cs" />
<Compile Include="Rig\Output\DB\DatabaseWriter\WriterCfg.cs" /> <Compile Include="Rig\Output\DB\DatabaseWriter\WriterCfg.cs" />
<Compile Include="Rig\Output\DB\DatabaseWriter\WritingToDB.cs" /> <Compile Include="Rig\Output\DB\DatabaseWriter\WritingToDB.cs" />
@@ -1293,6 +1295,7 @@
<Compile Include="Rig\RegisterReaders\DataStream\Reader\ReaderCfg.cs" /> <Compile Include="Rig\RegisterReaders\DataStream\Reader\ReaderCfg.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\common\HexFormatter.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\common\HexFormatter.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\common\OptoTelegramRaw.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\common\OptoTelegramRaw.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\ChannelAverages.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\Crc16Ccitt.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\Crc16Ccitt.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\DataPackages\EventArguments\BaseDataEventArgs.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\DataPackages\EventArguments\BaseDataEventArgs.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\DataPackages\EventArguments\BendDetectDataEventArgs.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\DataPackages\EventArguments\BendDetectDataEventArgs.cs" />
@@ -1385,12 +1388,16 @@
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Utils\SerialDriver.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Utils\SerialDriver.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Utils\SerialDriverBuilder.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Utils\SerialDriverBuilder.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\Factory.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"> <Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisHeadTestCtrl.Designer.cs">
<DependentUpon>GenesisHeadTestCtrl.cs</DependentUpon> <DependentUpon>GenesisHeadTestCtrl.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisCfg.cs" /> <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"> <Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisCfgCtrl.designer.cs">
<DependentUpon>GenesisCfgCtrl.cs</DependentUpon> <DependentUpon>GenesisCfgCtrl.cs</DependentUpon>
</Compile> </Compile>
@@ -1409,9 +1416,13 @@
<Compile Include="Rig\RegisterReaders\iPerlASICReader\Factory.cs" /> <Compile Include="Rig\RegisterReaders\iPerlASICReader\Factory.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\IPerlASICImplHeadTestCtrl.cs" /> <Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\IPerlASICImplHeadTestCtrl.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\SmartReader.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\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\iPerlASICReader\IPerlUniCfgCtrl.designer.cs" />
<Compile Include="Rig\RegisterReaders\iPerlReaderUNI\common\IUniHeadTestCtrl.cs" /> <Compile Include="Rig\RegisterReaders\iPerlReaderUNI\common\IUniHeadTestCtrl.cs" />
<Compile Include="Rig\RegisterReaders\iPerlReaderUNI\Factory.cs" /> <Compile Include="Rig\RegisterReaders\iPerlReaderUNI\Factory.cs" />
@@ -1483,6 +1494,8 @@
<DependentUpon>RRCfgCtrl.cs</DependentUpon> <DependentUpon>RRCfgCtrl.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunner.cs" /> <Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunner.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliTaskInfo.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliTaskState.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\Factory.cs" /> <Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\Factory.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\JsonDataFromPoseidon.cs" /> <Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\JsonDataFromPoseidon.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonCfg.cs" /> <Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonCfg.cs" />
@@ -1714,7 +1727,9 @@
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\Factory.cs" /> <Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\Factory.cs" />
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHead.cs" /> <Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHead.cs" />
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfg.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\GenesisHeadCfgCtrl.designer.cs" />
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\OptoReceivedEventArgs.cs" /> <Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\OptoReceivedEventArgs.cs" />
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\OptoTelegramRaw.cs" /> <Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\OptoTelegramRaw.cs" />
@@ -1770,7 +1785,9 @@
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\Utils\ISerialDriver.cs" /> <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\SerialDriver.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\Utils\SerialDriverBuilder.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\iPerlCommunicationForm.designer.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationParams.cs" /> <Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationParams.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationSeq.cs" /> <Compile Include="Rig\TestMethods\iPerlCommunication\iPerlCommunicationSeq.cs" />
@@ -189,8 +189,16 @@ namespace TBF.UI.Bench.Components
int extraWidth = splitContainer.Panel2.Width + 40; int extraWidth = splitContainer.Panel2.Width + 40;
int extraHeight = 80; int extraHeight = 80;
this.Width = desired.Width + extraWidth; if (!desired.IsEmpty)
this.Height = desired.Height + extraHeight; {
this.Width = desired.Width + extraWidth;
this.Height = desired.Height + extraHeight;
}
else
{
//this.Width += extraWidth;
//this.Height += extraHeight;
}
// Do not exceed screen working area // Do not exceed screen working area
var screen = Screen.FromControl(this).WorkingArea; var screen = Screen.FromControl(this).WorkingArea;
@@ -43,7 +43,10 @@ namespace TBF.UI.Bench.Components
{ {
availCmpntsLstBox.SelectedIndex = selectedIndex; availCmpntsLstBox.SelectedIndex = selectedIndex;
} }
}
/// SharedDlgSearchForListBox configuration
sharedSearchForListBox1.ListBoxEx = availCmpntsLstBox;
}
void Localize() void Localize()
{ {
@@ -12,13 +12,17 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
private static CalibrationRecord CreateCalibrationRecord( private static CalibrationRecord CreateCalibrationRecord(
int channel = 1, int channel = 1,
double volumeCm = 1.234, double volumeCm = 1.234,
double timeS = 12.5) double overflowVolumeCm = 8.38860799804687,
double timeS = 12.5,
double overflowTimeS = 65536.0)
{ {
return new CalibrationRecord return new CalibrationRecord
{ {
Channel = channel, Channel = channel,
VolumeCm = volumeCm, VolumeCm = volumeCm,
TimeS = timeS OverflowVolumeCm = overflowVolumeCm,
TimeS = timeS,
OverflowTimeS = overflowTimeS
}; };
} }
@@ -26,7 +30,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
public void UpdateFromSmart_FirstSample_ShouldInitializeFields() public void UpdateFromSmart_FirstSample_ShouldInitializeFields()
{ {
var telegram = new OptoTelegramRaw(); var telegram = new OptoTelegramRaw();
var data = CreateCalibrationRecord(channel: 2, volumeCm: 2.5, timeS: 100.0); var data = CreateCalibrationRecord(
channel: 2,
volumeCm: 2.5,
overflowVolumeCm: 8.38860799804687,
timeS: 100.0,
overflowTimeS: 65536.0);
double lastVolume = double.NaN; double lastVolume = double.NaN;
double lastTimestamp = double.NaN; double lastTimestamp = double.NaN;
@@ -54,32 +63,479 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
Assert.AreEqual(0, telegram.MagneticFieldRaw); Assert.AreEqual(0, telegram.MagneticFieldRaw);
} }
[TestMethod]
public void UpdateFromSmart_RealGenesisDecodedSample_ShouldMapExpectedValues()
{
var telegram = new OptoTelegramRaw();
var data = CreateCalibrationRecord(
channel: 2,
volumeCm: 0.48025591796875,
overflowVolumeCm: 8.38860799804687,
timeS: 62727.8408813477,
overflowTimeS: 65536.0);
double lastVolume = double.NaN;
double lastTimestamp = double.NaN;
telegram.UpdateFromSmart(data, counter: 1, refFlow: 0.0f, ref lastVolume, ref lastTimestamp);
Assert.AreEqual(1, telegram.IChannel());
Assert.AreEqual(1, telegram.Counter);
Assert.AreEqual(0.0f, telegram.RefFlow);
Assert.AreEqual(OptoTelegramFlags.OK, telegram.Flags);
Assert.AreEqual(480.25591796875, telegram.VolumeRaw, 1e-9);
Assert.AreEqual(480.25591796875, telegram.VolumeRawExt, 1e-9);
Assert.AreEqual(480.25591796875, lastVolume, 1e-9);
Assert.AreEqual(62727.8408813477, telegram.Timestamp, 1e-9);
Assert.AreEqual(62727.8408813477, telegram.TimestampExt, 1e-9);
Assert.AreEqual(62727.8408813477, lastTimestamp, 1e-9);
Assert.AreEqual(0, telegram.FlowRaw);
Assert.AreEqual(0, telegram.CheckSum);
Assert.AreEqual(0, telegram.Impedance);
Assert.AreEqual(0, telegram.EmfRaw);
Assert.AreEqual(0, telegram.MagneticFieldRaw);
}
[TestMethod] [TestMethod]
public void UpdateFromSmart_WhenVolumeDecreases_ShouldApplyVolumeRollover() public void UpdateFromSmart_WhenVolumeDecreases_ShouldApplyVolumeRollover()
{ {
var telegram = new OptoTelegramRaw(); var telegram = new OptoTelegramRaw();
var data = CreateCalibrationRecord(channel: 1, volumeCm: 1.0, timeS: 10.0); var overflowVolumeCm = 8.38860799804687;
var overflowRaw = overflowVolumeCm * 1000.0;
double previousExtendedVolume = 5000.0; var data = CreateCalibrationRecord(
channel: 1,
volumeCm: 1.0,
overflowVolumeCm: overflowVolumeCm,
timeS: 10.0,
overflowTimeS: 65536.0);
// Must be close enough to overflow so that current=1000 is interpreted as a wrap
double previousExtendedVolume = 8000.0;
double previousExtendedTimestamp = 10.0; double previousExtendedTimestamp = 10.0;
telegram.UpdateFromSmart(data, counter: 1, refFlow: 0.5f, ref previousExtendedVolume, ref previousExtendedTimestamp); telegram.UpdateFromSmart(
data,
counter: 1,
refFlow: 0.5f,
ref previousExtendedVolume,
ref previousExtendedTimestamp);
double expectedVolumeRaw = 1000.0; double expectedVolumeRaw = 1000.0;
double expectedExtended = expectedVolumeRaw + StreamingDecoder.DefaultAccuDutOverflowVolumeCm * 1000.0; double expectedExtended = expectedVolumeRaw + overflowRaw;
Assert.AreEqual(expectedVolumeRaw, telegram.VolumeRaw, 1e-9); Assert.AreEqual(expectedVolumeRaw, telegram.VolumeRaw, 1e-9);
Assert.AreEqual(expectedExtended, telegram.VolumeRawExt, 1e-6); Assert.AreEqual(expectedExtended, telegram.VolumeRawExt, 1e-6);
Assert.AreEqual(expectedExtended, previousExtendedVolume, 1e-6); Assert.AreEqual(expectedExtended, previousExtendedVolume, 1e-6);
} }
[TestMethod]
public void UpdateFromSmart_MultipleVolumeRollover_ShouldKeepIncreasingExtendedVolume()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
// Safe sequence: small forward steps, natural wrap, small forward steps
double[] samples =
{
7.5,
8.0,
0.2,
1.0,
1.8,
2.6,
3.4,
4.2,
5.0,
5.8,
6.6,
7.4,
8.1,
0.3,
1.1
};
double previousExt = double.NaN;
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
double expectedRaw = samples[i] * 1000.0;
Assert.AreEqual(expectedRaw, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
if (!double.IsNaN(previousExt))
{
Assert.IsTrue(
telegram.VolumeRawExt >= previousExt,
$"Volume should not decrease at index {i}. Prev={previousExt}, Current={telegram.VolumeRawExt}");
}
previousExt = telegram.VolumeRawExt;
}
}
[TestMethod]
public void UpdateFromSmart_MultipleVolumeRollover_FirstToLastDeltaShouldBePositive()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] samples =
{
7.5, 8.0, 0.2, 1.0, 1.8, 2.6, 3.4, 4.2, 5.0, 5.8, 6.6, 7.4, 8.1, 0.3, 1.1
};
double firstExt = double.NaN;
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
if (i == 0)
firstExt = telegram.VolumeRawExt;
}
Assert.IsTrue(lastVolumeExt > firstExt,
$"Volume should increase from first to last. First={firstExt}, Last={lastVolumeExt}");
}
[TestMethod]
public void UpdateFromSmart_MultipleVolumeRollover_ShouldMatchPhysicalDelta()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] samples =
{
7.5,
8.0,
0.2,
1.0,
1.8,
2.6,
3.4,
4.2,
5.0,
5.8,
6.6,
7.4,
8.1,
0.3,
1.1
};
double firstExt = double.NaN;
double lastExt = double.NaN;
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
if (i == 0)
firstExt = telegram.VolumeRawExt;
if (i == samples.Length - 1)
lastExt = telegram.VolumeRawExt;
}
double start = samples[0];
double end = samples[samples.Length - 1];
int wraps = 0;
for (int i = 1; i < samples.Length; i++)
{
if (samples[i] < samples[i - 1])
wraps++;
}
double expectedDeltaCm = wraps * overflowVolumeCm + end - start;
double expectedDeltaRaw = expectedDeltaCm * 1000.0;
double actualDeltaRaw = lastExt - firstExt;
Assert.AreEqual(
expectedDeltaRaw,
actualDeltaRaw,
1e-6,
$"Delta mismatch. Wraps={wraps}, Expected={expectedDeltaRaw}, Actual={actualDeltaRaw}");
}
[TestMethod]
public void UpdateFromSmart_MultipleTimestampWrap_ShouldMatchPhysicalDelta()
{
var telegram = new OptoTelegramRaw();
double overflowTimeS = 65536.0;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
// Safe sequence: small forward steps, natural wrap, small forward steps, second wrap
double[] times =
{
65530.0,
65535.0,
2.0, // wrap 1
10.0,
100.0,
1000.0,
10000.0,
30000.0,
50000.0,
65534.0,
3.0, // wrap 2
20.0
};
double firstExt = double.NaN;
double lastExt = double.NaN;
for (int i = 0; i < times.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: 1.0,
overflowVolumeCm: 8.38860799804687,
timeS: times[i],
overflowTimeS: overflowTimeS);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
if (i == 0)
firstExt = telegram.TimestampExt;
if (i == times.Length - 1)
lastExt = telegram.TimestampExt;
}
double start = times[0];
double end = times[times.Length - 1];
int wraps = 0;
for (int i = 1; i < times.Length; i++)
{
if (times[i] < times[i - 1])
wraps++;
}
double expectedDelta = wraps * overflowTimeS + end - start;
double actualDelta = lastExt - firstExt;
Assert.AreEqual(
expectedDelta,
actualDelta,
1e-6,
$"Timestamp delta mismatch. Wraps={wraps}, Expected={expectedDelta}, Actual={actualDelta}");
}
[TestMethod]
public void UpdateFromSmart_MultipleTimestampWrap_ShouldKeepIncreasingExtendedTimestamp()
{
var telegram = new OptoTelegramRaw();
double overflowTimeS = 65536.0;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] times =
{
65530.0,
65535.0,
2.0,
10.0,
100.0,
1000.0,
10000.0,
30000.0,
50000.0,
65534.0,
3.0,
20.0
};
double previousExt = double.NaN;
for (int i = 0; i < times.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: 1.0,
overflowVolumeCm: 8.38860799804687,
timeS: times[i],
overflowTimeS: overflowTimeS);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
if (!double.IsNaN(previousExt))
{
Assert.IsTrue(
telegram.TimestampExt >= previousExt,
$"Timestamp should not decrease at index {i}. Prev={previousExt}, Current={telegram.TimestampExt}");
}
previousExt = telegram.TimestampExt;
}
}
[TestMethod]
public void UpdateFromSmart_MultipleVolumeRollover_ShouldProduceExpectedExtendedValues()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double overflowRaw = overflowVolumeCm * 1000.0;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] samples =
{
7.5,
8.0,
0.2,
0.8,
1.4
};
double[] expectedExt =
{
7500.0,
8000.0,
200.0 + overflowRaw,
800.0 + overflowRaw,
1400.0 + overflowRaw
};
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
Assert.AreEqual(samples[i] * 1000.0, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
Assert.AreEqual(expectedExt[i], telegram.VolumeRawExt, 1e-6, $"Mismatch at index {i}");
}
}
[TestMethod]
public void UpdateFromSmart_TwoVolumeRollovers_ShouldKeepExtendedVolumeIncreasing()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double overflowRaw = overflowVolumeCm * 1000.0;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] samples =
{
6.8,
8.1,
0.3,
1.2,
2.1,
3.0,
4.0,
7.5,
8.1,
0.4,
1.1
};
double[] expectedExt =
{
6800.0,
8100.0,
overflowRaw + 300.0,
overflowRaw + 1200.0,
overflowRaw + 2100.0,
overflowRaw + 3000.0,
overflowRaw + 4000.0,
overflowRaw + 7500.0,
overflowRaw + 8100.0,
2.0 * overflowRaw + 400.0,
2.0 * overflowRaw + 1100.0
};
double previous = double.NaN;
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
Assert.AreEqual(samples[i] * 1000.0, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
Assert.AreEqual(expectedExt[i], telegram.VolumeRawExt, 1e-6, $"Mismatch at index {i}");
if (!double.IsNaN(previous))
{
Assert.IsTrue(
telegram.VolumeRawExt > previous,
$"Extended volume did not increase at index {i}. Prev={previous}, Current={telegram.VolumeRawExt}");
}
previous = telegram.VolumeRawExt;
}
}
[TestMethod] [TestMethod]
public void UpdateFromSmart_WhenTimestampWraps_ShouldUnwrapForward() public void UpdateFromSmart_WhenTimestampWraps_ShouldUnwrapForward()
{ {
var telegram = new OptoTelegramRaw(); var telegram = new OptoTelegramRaw();
double tsRange = StreamingDecoder.CpuTimeOverflowS; double tsRange = StreamingDecoder.CpuTimeOverflowS;
var data = CreateCalibrationRecord(channel: 1, volumeCm: 1.0, timeS: 1.0); var data = CreateCalibrationRecord(
channel: 1,
volumeCm: 1.0,
overflowVolumeCm: 8.38860799804687,
timeS: 1.0,
overflowTimeS: tsRange);
double previousExtendedVolume = 1000.0; double previousExtendedVolume = 1000.0;
double previousExtendedTimestamp = tsRange - 0.25; double previousExtendedTimestamp = tsRange - 0.25;
@@ -17,9 +17,10 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
public int DiscardInCalls { get; private set; } public int DiscardInCalls { get; private set; }
public int DiscardOutCalls { get; private set; } public int DiscardOutCalls { get; private set; }
public Encoding Encoding { get; set; } = Encoding.ASCII; public Encoding Encoding { get; set; } = Encoding.ASCII;
public int BytesToRead => _lines.Count > 0 ? 1 : 0; public int BytesToRead => _isOpen && _lines.Count > 0 ? 1 : 0;
public int BytesToWrite => 0; public int BytesToWrite => 0;
public void EnqueueLine(string line) => _lines.Enqueue(line); public void EnqueueLine(string line) => _lines.Enqueue(line);
@@ -0,0 +1,318 @@
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;
using TBF.Rig.Sequences;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
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(CreateCfg());
InitializeReaderForTest(reader);
string[] variants =
{
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
"@he 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
"@he\t1\t0\t0A1F59C4\t00017A43\t00115C45\t72E1596B\t00000400\t00001998\t7D91B652\t7D4F37E6\t000191E6\t0C\t062E4A9C\t5331",
"@he \t1\t0\t0A1F59C4\t00017A43\t00115C45\t72E1596B\t00000400\t00001998\t7D91B652\t7D4F37E6\t000191E6\t0C\t062E4A9C\t5331"
};
int[] counts = new int[variants.Length];
for (int i = 0; i < variants.Length; i++)
{
bool blockCompleted = false;
reader.ProcessOptoLine(variants[i], DataStreamState.ProcessAndSave, out blockCompleted);
counts[i] = GetPrivateField<int>(reader, "optoDataCount");
Console.WriteLine($"Variant {i}: {variants[i]}");
Console.WriteLine($" blockCompleted={blockCompleted}");
Console.WriteLine($" optoDataCount={counts[i]}");
Console.WriteLine("--------------------------------");
}
Assert.IsTrue(counts.Any(c => c > 0), "At least one telegram format variant should be accepted.");
}
[TestMethod]
public void RealInput_ShouldParseCalibrationTelegrams()
{
var reader = new GenesisSmartReader(CreateCfg());
InitializeReaderForTest(reader);
string[] realInputLines = LoadRealInputLines();
Assert.IsTrue(realInputLines.Length > 0, "No test input lines were provided.");
int processedCount = 0;
int firstValidIx = -1;
int lastValidIx = -1;
Console.WriteLine("=== BEGIN INPUT PARSING ===");
for (int i = 0; i < realInputLines.Length; i++)
{
string line = realInputLines[i];
bool blockCompleted = false;
Exception parseException = null;
try
{
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out blockCompleted);
}
catch (Exception ex)
{
parseException = ex;
}
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Console.WriteLine($"[{i}] line = {line}");
Console.WriteLine($"[{i}] blockCompleted = {blockCompleted}");
Console.WriteLine($"[{i}] optoDataCount = {optoDataCount}");
if (parseException != null)
{
Console.WriteLine($"[{i}] EXCEPTION = {parseException.GetType().Name}: {parseException.Message}");
}
if (optoDataCount > processedCount)
{
if (firstValidIx < 0)
firstValidIx = processedCount;
lastValidIx = optoDataCount - 1;
processedCount = optoDataCount;
Console.WriteLine(
$"[{i}] ACCEPTED -> processedCount={processedCount}, firstValidIx={firstValidIx}, lastValidIx={lastValidIx}");
}
else
{
Console.WriteLine($"[{i}] NOT ACCEPTED");
}
Console.WriteLine("--------------------------------------------------");
}
Console.WriteLine("=== END INPUT PARSING ===");
Console.WriteLine($"Final processedCount = {processedCount}");
Console.WriteLine($"Final firstValidIx = {firstValidIx}");
Console.WriteLine($"Final lastValidIx = {lastValidIx}");
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;
InvokePrivate(reader, "AddTestStartEndMarksToData", new object[] { 0, 0 });
InvokePrivate(reader, "DataStreamPostProcessing");
InvokePrivate(reader, "PrepareCalculatedChannelData");
Assert.IsTrue(reader.NoSamples,
"With only one telegram per channel, recalculated start/end samples should still be unavailable.");
}
[TestMethod]
public void RealInput_ShouldParseRolloverInputLines()
{
var reader = new GenesisSmartReader(CreateCfg());
InitializeReaderForTest(reader);
string[] realInputLines = LoadRealInputLinesWithRollover();
Assert.IsTrue(realInputLines.Length > 0, "No rollover input lines were provided.");
for (int i = 0; i < realInputLines.Length; i++)
{
string line = realInputLines[i];
bool blockCompleted;
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out blockCompleted);
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Console.WriteLine($"[{i}] line={line}");
Console.WriteLine($"[{i}] optoDataCount={optoDataCount}, blockCompleted={blockCompleted}");
}
int finalOptoDataCount = GetPrivateField<int>(reader, "optoDataCount");
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;
SetPrivateField(reader, "volumeRawExtLast", new double[channelCount]);
SetPrivateField(reader, "timestampExtLast", new double[channelCount]);
SetPrivateField(reader, "lastTimestamp", new double[channelCount]);
SetPrivateField(reader, "timestampSec", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "timestampSec0", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "lastVolumeRaw", new double[channelCount]);
SetPrivateField(reader, "volumeLtr", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "volumeLtr0", Enumerable.Repeat(double.NaN, channelCount).ToArray());
var optoData = new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize];
for (int i = 0; i < optoData.Length; i++)
optoData[i] = new OptoTelegramRaw();
SetPrivateField(reader, "optoData", optoData);
SetPrivateField(reader, "optoDataCount", 0);
SetPrivateField(reader, "toBeFlushed", new OptoTelegramRaw());
SetPrivateField(reader, "flowDirectionDetection", new FlowDirectionDetection());
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
SetPrivateField(reader, "synchronized", false);
SetPrivateField(reader, "synchronized2", false);
SetPrivateField(reader, "partOfTelegram", string.Empty);
SetPrivateField(reader, "startDataProcessing", true);
reader.StopQueueData = false;
reader.TestStartTelegramIx = 0;
reader.TestEndTelegramIx = 0;
}
private static string[] LoadRealInputLines()
{
return new[]
{
// Replace these with REAL, unchanged captured telegram lines from the device.
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
};
}
private static string[] LoadRealInputLinesWithRollover()
{
return new[]
{
// Replace these with REAL, unchanged captured telegram lines spanning the rollover case.
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
};
}
private static void SetPrivateField(object target, string fieldName, object value)
{
Type type = target.GetType();
while (type != null)
{
var field = type.GetField(
fieldName,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
if (field != null)
{
field.SetValue(target, value);
return;
}
type = type.BaseType;
}
Assert.Fail($"Field '{fieldName}' not found.");
}
private static T GetPrivateField<T>(object target, string fieldName)
{
Type type = target.GetType();
while (type != null)
{
var field = type.GetField(
fieldName,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
if (field != null)
return (T)field.GetValue(target);
type = type.BaseType;
}
Assert.Fail($"Field '{fieldName}' not found.");
return default;
}
private static object InvokePrivate(object target, string methodName, object[] args = null)
{
Type type = target.GetType();
while (type != null)
{
var methods = type.GetMethods(
BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.DeclaredOnly)
.Where(m => m.Name == methodName)
.ToList();
foreach (var method in methods)
{
if (args == null && method.GetParameters().Length == 0)
return method.Invoke(target, null);
if (args != null && method.GetParameters().Length == args.Length)
return method.Invoke(target, args);
}
type = type.BaseType;
}
Assert.Fail($"Method '{methodName}' not found.");
return null;
}
}
}
@@ -2,7 +2,6 @@ using System;
using System.Reflection; using System.Reflection;
using Common; using Common;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.GenesisRegReader; using TBF.Rig.RegisterReaders.GenesisRegReader;
using TBF.Rig.RegisterReaders.GenesisRegReader.common; using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations; using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
@@ -14,6 +13,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{ {
private const int ChannelCount = 3; private const int ChannelCount = 3;
[TestInitialize]
public void TestInitialize()
{
SetPrivateStaticField(typeof(GenesisSmartReader), "chenelsSwichCount", ChannelCount);
}
private static void SetPrivateField(object target, string fieldName, object value) private static void SetPrivateField(object target, string fieldName, object value)
{ {
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
@@ -32,13 +37,25 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
return (T)field.GetValue(target); return (T)field.GetValue(target);
} }
private static void InvokePrivateMethod(object target, string methodName) private static void SetPrivateStaticField(Type type, string fieldName, object value)
{ {
var method = target.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); var field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic);
if (method == null) if (field == null)
throw new MissingMethodException(target.GetType().FullName, methodName); throw new MissingFieldException(type.FullName, fieldName);
method.Invoke(target, null); field.SetValue(null, value);
}
private static void InvokePrepareCalculatedChannelData(GenesisSmartReader reader)
{
var method = typeof(GenesisSmartReader).GetMethod(
"PrepareCalculatedChannelData",
BindingFlags.Instance | BindingFlags.NonPublic);
if (method == null)
throw new MissingMethodException(typeof(GenesisSmartReader).FullName, "PrepareCalculatedChannelData");
method.Invoke(reader, null);
} }
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt, int counter) private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt, int counter)
@@ -54,7 +71,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
}; };
} }
private static GenesisCfg CreateCfg() private static GenesisCfg CreateCfg()
{ {
var cfg = new GenesisCfg(null); var cfg = new GenesisCfg(null);
@@ -76,20 +92,26 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
return cfg; return cfg;
} }
[TestMethod] private static GenesisSmartReader CreateReaderWithOptoBuffer()
public void SimulatedInterleaved20x3ChannelStream_ShouldComputeStartAndEndVolumeAndTimeCorrectly()
{ {
var fake = new FakeSerialDriver(); var reader = new GenesisSmartReader(CreateCfg(), null);
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
SetPrivateField(reader, "volumeRawExtLast", new double[3]);
SetPrivateField(reader, "timestampExtLast", new double[3]);
SetPrivateField(reader, "flowDirectionDetection", new FlowDirectionDetection());
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]); SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData"); var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
for (int i = 0; i < optoData.Length; i++) for (int i = 0; i < optoData.Length; i++)
{
optoData[i] = new OptoTelegramRaw(); optoData[i] = new OptoTelegramRaw();
}
return reader;
}
[TestMethod]
public void SimulatedInterleaved20x3ChannelStream_ShouldComputeRecalculatedStartAndEndVolumeAndTimeCorrectly()
{
var reader = CreateReaderWithOptoBuffer();
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
int index = 0; int index = 0;
for (int g = 0; g < 20; g++) for (int g = 0; g < 20; g++)
@@ -108,66 +130,78 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.TestStartTelegramIx = 2; reader.TestStartTelegramIx = 2;
reader.TestEndTelegramIx = index - 1; reader.TestEndTelegramIx = index - 1;
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9); InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(217.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(219.666666666667, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9); Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(18.0, reader.TimestampSecEnd, 1e-9); Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartAverage, 1e-9);
Assert.AreEqual(219.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
} }
[TestMethod] [TestMethod]
public void VolumeLtrEnd_ShouldIgnoreInvalidTrailingRecords_AndUsePreviousValidPerChannelSamples() public void VolumeLtrEnd_ShouldRecalculateShorterChannelToLongestChannelTimeSpan()
{ {
var reader = new GenesisSmartReader(CreateCfg(), null); var reader = CreateReaderWithOptoBuffer();
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData"); var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
for (int i = 0; i < optoData.Length; i++)
optoData[i] = new OptoTelegramRaw();
int index = 0; int index = 0;
// Build 10 groups of valid 3-channel data
for (int g = 0; g < 10; g++) for (int g = 0; g < 10; g++)
{ {
optoData[index++] = CreateOptoRecord(0, 100 + g, 1 + g, index); optoData[index] = CreateOptoRecord(0, 100 + g, 1 + g, index);
optoData[index++] = CreateOptoRecord(1, 200 + g, 1 + g, index); index++;
optoData[index++] = CreateOptoRecord(2, 300 + g, 1 + g, index);
optoData[index] = CreateOptoRecord(1, 200 + g, 1 + g, index);
index++;
optoData[index] = CreateOptoRecord(2, 300 + g, 1 + g, index);
index++;
} }
// Corrupt the last two channel-1 records optoData[25].Flags = OptoTelegramFlags.InvalidTelegram;
optoData[25].Flags = OptoTelegramFlags.InvalidTelegram; // group 8, channel 1 optoData[28].Flags = OptoTelegramFlags.InvalidTelegram;
optoData[28].Flags = OptoTelegramFlags.InvalidTelegram; // group 9, channel 1
SetPrivateField(reader, "optoDataCount", index); SetPrivateField(reader, "optoDataCount", index);
reader.TestStartTelegramIx = 2; reader.TestStartTelegramIx = 2;
reader.TestEndTelegramIx = index - 1; reader.TestEndTelegramIx = index - 1;
// Start still comes from first full valid 3-channel snapshot: InvokePrepareCalculatedChannelData(reader);
// (100 + 200 + 300) / 3 = 200
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
// End with last 5 valid per channel: Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
// ch0 valid last 5: 105,106,107,108,109 => avg 107 Assert.AreEqual(209.666666666667, reader.VolumeLtrEnd, 1e-9);
// ch1 valid last 5: 203,204,205,206,207 => avg 205 Assert.AreEqual(209.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
// because 208 and 209 were invalid
// ch2 valid last 5: 305,306,307,308,309 => avg 307 Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
// total avg = (107 + 205 + 307) / 3 = 206.3333333333... Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(206.33333333333334, reader.VolumeLtrEnd, 1e-9); Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 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);
Assert.AreEqual(309.0, reader.VolumeLtrEndCh3, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(10.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh1, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh2, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh3, 1e-9);
Assert.AreEqual(10.0, reader.TimestampSecEndCh1, 1e-9);
Assert.AreEqual(10.0, reader.TimestampSecEndCh2, 1e-9);
Assert.AreEqual(10.0, reader.TimestampSecEndCh3, 1e-9);
} }
[TestMethod] [TestMethod]
public void VolumeLtrStart_And_End_ShouldBeZero_WhenAllSamplesAreInvalid() public void VolumeLtrStart_And_End_ShouldBeZero_WhenAllSamplesAreInvalid()
{ {
var reader = new GenesisSmartReader(CreateCfg(), null); var reader = CreateReaderWithOptoBuffer();
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData"); var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
for (int i = 0; i < optoData.Length; i++)
optoData[i] = new OptoTelegramRaw();
for (int i = 0; i < 9; i++) for (int i = 0; i < 9; i++)
{ {
@@ -184,8 +218,97 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.TestStartTelegramIx = 2; reader.TestStartTelegramIx = 2;
reader.TestEndTelegramIx = 8; reader.TestEndTelegramIx = 8;
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(0.0, reader.VolumeLtrStart, 1e-9); Assert.AreEqual(0.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(0.0, reader.VolumeLtrEnd, 1e-9); Assert.AreEqual(0.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(0.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(0.0, reader.TimestampSecEnd, 1e-9);
}
[TestMethod]
public void SimulatedInterleaved20x3ChannelStream_ShouldComputePerChannelStartAndEndValues()
{
var reader = CreateReaderWithOptoBuffer();
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
int index = 0;
for (int g = 0; g < 20; g++)
{
optoData[index] = CreateOptoRecord(0, 100 + g, 1 + g, index);
index++;
optoData[index] = CreateOptoRecord(1, 200 + g, 1 + g, index);
index++;
optoData[index] = CreateOptoRecord(2, 300 + g, 1 + g, index);
index++;
}
SetPrivateField(reader, "optoDataCount", index);
reader.TestStartTelegramIx = 2;
reader.TestEndTelegramIx = index - 1;
InvokePrepareCalculatedChannelData(reader);
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(120.0, reader.VolumeLtrEndCh1, 1e-9);
Assert.AreEqual(220.0, reader.VolumeLtrEndCh2, 1e-9);
Assert.AreEqual(319.0, reader.VolumeLtrEndCh3, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh1, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh2, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh3, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEndCh1, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEndCh2, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEndCh3, 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.VolumeLtrStartAverage, 1e-9);
Assert.AreEqual(219.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
}
[TestMethod]
public void VolumeLtrEnd_ShouldAverageOnlyAvailableChannels()
{
var reader = CreateReaderWithOptoBuffer();
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
int idx = 0;
for (int i = 0; i < 5; i++)
{
optoData[idx++] = CreateOptoRecord(0, 10 + i, 100 + i, idx);
optoData[idx++] = CreateOptoRecord(1, 20 + i, 100 + i, idx);
}
SetPrivateField(reader, "optoDataCount", idx);
reader.TestStartTelegramIx = 1;
reader.TestEndTelegramIx = idx - 1;
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(15.5, reader.VolumeLtrStart, 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);
Assert.AreEqual(0.0, reader.VolumeLtrStartCh3, 1e-9);
Assert.AreEqual(15.0, reader.VolumeLtrEndCh1, 1e-9);
Assert.AreEqual(24.0, reader.VolumeLtrEndCh2, 1e-9);
Assert.AreEqual(0.0, reader.VolumeLtrEndCh3, 1e-9);
Assert.AreEqual(100.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(104.0, reader.TimestampSecEnd, 1e-9);
} }
} }
} }
@@ -0,0 +1,511 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class GenesisSmartReaderChannelTests
{
private GenesisSmartReader _reader;
[TestInitialize]
public void Setup()
{
_reader = new GenesisSmartReader();
}
private static OptoTelegramRaw Rec(
int channel,
double time,
double volume,
OptoTelegramFlags flags = OptoTelegramFlags.OK)
{
return new OptoTelegramRaw
{
iChannel = channel,
TimestampExt = time,
VolumeRawExt = volume,
Flags = flags
};
}
private object InvokePrivate(string methodName, params object[] args)
{
var mi = typeof(GenesisSmartReader).GetMethod(
methodName,
BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(mi, $"Method '{methodName}' was not found.");
return mi.Invoke(_reader, args);
}
private static T GetPrivateField<T>(object instance, string fieldName)
{
var fi = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(fi, $"Field '{fieldName}' was not found.");
return (T)fi.GetValue(instance);
}
private static void SetPrivateField(object instance, string fieldName, object value)
{
var fi = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(fi, $"Field '{fieldName}' was not found.");
fi.SetValue(instance, value);
}
[TestMethod]
public void GroupRecordsPerChannel_Groups_Valid_Records_By_Channel()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 110),
Rec(1, 14, 220),
Rec(2, 15, 330),
};
var grouped = (OptoTelegramRaw[][])InvokePrivate(
"GroupRecordsPerChannel",
(object)input, 0, input.Length - 1, input.Length);
Assert.AreEqual(3, grouped.Length);
Assert.AreEqual(2, grouped[0].Length);
Assert.AreEqual(2, grouped[1].Length);
Assert.AreEqual(2, grouped[2].Length);
Assert.AreEqual(100, grouped[0][0].VolumeRawExt);
Assert.AreEqual(110, grouped[0][1].VolumeRawExt);
Assert.AreEqual(200, grouped[1][0].VolumeRawExt);
Assert.AreEqual(220, grouped[1][1].VolumeRawExt);
Assert.AreEqual(300, grouped[2][0].VolumeRawExt);
Assert.AreEqual(330, grouped[2][1].VolumeRawExt);
}
[TestMethod]
public void GroupRecordsPerChannel_Ignores_Invalid_Records()
{
var input = new[]
{
Rec(0, 10, 100, OptoTelegramFlags.OK),
Rec(1, 11, 200, OptoTelegramFlags.InvalidTelegram),
Rec(2, 12, 300, OptoTelegramFlags.OK),
Rec(0, 13, 110, OptoTelegramFlags.SyncError),
Rec(1, 14, 220, OptoTelegramFlags.OK),
};
var grouped = (OptoTelegramRaw[][])InvokePrivate(
"GroupRecordsPerChannel",
(object)input, 0, input.Length - 1, input.Length);
Assert.AreEqual(1, grouped[0].Length);
Assert.AreEqual(1, grouped[1].Length);
Assert.AreEqual(1, grouped[2].Length);
Assert.AreEqual(100, grouped[0][0].VolumeRawExt);
Assert.AreEqual(220, grouped[1][0].VolumeRawExt);
Assert.AreEqual(300, grouped[2][0].VolumeRawExt);
}
[TestMethod]
public void GroupRecordsPerChannel_Swaps_Start_And_End_When_Reversed()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(0, 12, 120),
};
var grouped = (OptoTelegramRaw[][])InvokePrivate(
"GroupRecordsPerChannel",
(object)input, 2, 0, input.Length);
Assert.AreEqual(2, grouped[0].Length);
Assert.AreEqual(1, grouped[1].Length);
Assert.AreEqual(0, grouped[2].Length);
}
[TestMethod]
public void GroupRecordsPerChannel_Returns_Empty_For_Invalid_Range()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
};
var grouped = (OptoTelegramRaw[][])InvokePrivate(
"GroupRecordsPerChannel",
(object)input, -1, 1, input.Length);
Assert.AreEqual(3, grouped.Length);
Assert.IsTrue(grouped.All(x => x.Length == 0));
}
[TestMethod]
public void TimeDeltaPerChannel_Computes_First_Last_Deltas()
{
var grouped = new[]
{
new[]
{
Rec(0, 10, 100),
Rec(0, 18, 170),
Rec(0, 15, 120)
},
new[]
{
Rec(1, 20, 500),
Rec(1, 23, 530)
},
new[]
{
Rec(2, 30, 900)
}
};
var result = (ValueTuple<int, double?, double?>[])InvokePrivate(
"TimeDeltaPerChannel",
(object)grouped);
Assert.AreEqual(0, result[0].Item1);
Assert.AreEqual(5, result[0].Item2);
Assert.AreEqual(20, result[0].Item3);
Assert.AreEqual(1, result[1].Item1);
Assert.AreEqual(3, result[1].Item2);
Assert.AreEqual(30, result[1].Item3);
Assert.AreEqual(2, result[2].Item1);
Assert.IsNull(result[2].Item2);
Assert.IsNull(result[2].Item3);
}
[TestMethod]
public void VolumeStartPerChannel_Returns_Last_Seen_Value_Backward_From_Index()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(0, 12, 120),
Rec(2, 13, 300),
Rec(1, 14, 220),
};
var result = (double?[])InvokePrivate(
"VolumeStartPerChannel",
(object)input, input.Length, 4);
Assert.AreEqual(120, result[0]);
Assert.AreEqual(220, result[1]);
Assert.AreEqual(300, result[2]);
}
[TestMethod]
public void TimeStartPerChannel_Returns_Last_Seen_Time_Backward_From_Index()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(0, 12, 120),
Rec(2, 13, 300),
Rec(1, 14, 220),
};
var result = (double?[])InvokePrivate(
"TimeStartPerChannel",
(object)input, input.Length, 4);
Assert.AreEqual(12, result[0]);
Assert.AreEqual(14, result[1]);
Assert.AreEqual(13, result[2]);
}
[TestMethod]
public void VolumeEndPerChannel_Averages_Last_N_Samples_Per_Channel()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 120),
Rec(1, 14, 220),
Rec(2, 15, 330),
Rec(0, 16, 140),
Rec(1, 17, 240),
Rec(2, 18, 360),
};
var result = (double?[])InvokePrivate(
"VolumeEndPerChannel",
(object)input, input.Length, 8, 2);
Assert.AreEqual((140 + 120) / 2.0, result[0]);
Assert.AreEqual((240 + 220) / 2.0, result[1]);
Assert.AreEqual((360 + 330) / 2.0, result[2]);
}
[TestMethod]
public void TimeEndPerChannel_Averages_Last_N_Samples_Per_Channel()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 120),
Rec(1, 14, 220),
Rec(2, 15, 330),
Rec(0, 16, 140),
Rec(1, 17, 240),
Rec(2, 18, 360),
};
var result = (double?[])InvokePrivate(
"TimeEndPerChannel",
(object)input, input.Length, 8, 2);
Assert.AreEqual((16 + 13) / 2.0, result[0]);
Assert.AreEqual((17 + 14) / 2.0, result[1]);
Assert.AreEqual((18 + 15) / 2.0, result[2]);
}
[TestMethod]
public void VolumeStartPerChannel_Returns_All_Nulls_For_OutOfRange_Index()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
};
var result = (double?[])InvokePrivate(
"VolumeStartPerChannel",
(object)input, input.Length, -1);
Assert.IsNull(result[0]);
Assert.IsNull(result[1]);
Assert.IsNull(result[2]);
}
[TestMethod]
public void TimeEndPerChannel_Uses_One_Sample_When_SamplesPerChannel_Is_Zero()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 120),
Rec(1, 14, 220),
Rec(2, 15, 330),
};
var result = (double?[])InvokePrivate(
"TimeEndPerChannel",
(object)input, input.Length, 5, 0);
Assert.AreEqual(13, result[0]);
Assert.AreEqual(14, result[1]);
Assert.AreEqual(15, result[2]);
}
[TestMethod]
public void Precreated_OptoData_Private_Methods_Read_Correct_Values()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 120),
Rec(1, 14, 220),
Rec(2, 15, 330),
Rec(0, 16, 140),
Rec(1, 17, 240),
Rec(2, 18, 360),
};
SetPrivateField(_reader, "optoData", input);
SetPrivateField(_reader, "optoDataCount", input.Length);
_reader.TestStartTelegramIx = 5;
_reader.TestEndTelegramIx = 8;
Assert.AreEqual(input.Length, GetPrivateField<int>(_reader, "optoDataCount"));
var startVolumes = (double?[])InvokePrivate(
"VolumeStartPerChannel",
(object)input, input.Length, 5);
var endVolumes = (double?[])InvokePrivate(
"VolumeEndPerChannel",
(object)input, input.Length, 8, 2 * GenesisSmartReader.StartEndFilterSamplesCount2 + 1);
var startTimes = (double?[])InvokePrivate(
"TimeStartPerChannel",
(object)input, input.Length, 5);
var endTimes = (double?[])InvokePrivate(
"TimeEndPerChannel",
(object)input, input.Length, 8, 2 * GenesisSmartReader.StartEndFilterSamplesCount2 + 1);
Assert.AreEqual(120, startVolumes[0]);
Assert.AreEqual(220, startVolumes[1]);
Assert.AreEqual(330, startVolumes[2]);
Assert.AreEqual(13, startTimes[0]);
Assert.AreEqual(14, startTimes[1]);
Assert.AreEqual(15, startTimes[2]);
Assert.AreEqual((140 + 120 + 100) / 3.0, endVolumes[0]);
Assert.AreEqual((240 + 220 + 200) / 3.0, endVolumes[1]);
Assert.AreEqual((360 + 330 + 300) / 3.0, endVolumes[2]);
Assert.AreEqual((16 + 13 + 10) / 3.0, endTimes[0]);
Assert.AreEqual((17 + 14 + 11) / 3.0, endTimes[1]);
Assert.AreEqual((18 + 15 + 12) / 3.0, endTimes[2]);
}
[TestMethod]
public void Public_Channel_Properties_Default_To_Zero_When_NoSamples_Is_True()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
};
SetPrivateField(_reader, "optoData", input);
SetPrivateField(_reader, "optoDataCount", input.Length);
_reader.TestStartTelegramIx = 5;
_reader.TestEndTelegramIx = 8;
Assert.IsTrue(_reader.NoSamples);
Assert.AreEqual(0, _reader.VolumeLtrStartCh1);
Assert.AreEqual(0, _reader.VolumeLtrStartCh2);
Assert.AreEqual(0, _reader.VolumeLtrStartCh3);
Assert.AreEqual(0, _reader.VolumeLtrEndCh1);
Assert.AreEqual(0, _reader.VolumeLtrEndCh2);
Assert.AreEqual(0, _reader.VolumeLtrEndCh3);
Assert.AreEqual(0, _reader.TimestampSecStartCh1);
Assert.AreEqual(0, _reader.TimestampSecStartCh2);
Assert.AreEqual(0, _reader.TimestampSecStartCh3);
Assert.AreEqual(0, _reader.TimestampSecEndCh1);
Assert.AreEqual(0, _reader.TimestampSecEndCh2);
Assert.AreEqual(0, _reader.TimestampSecEndCh3);
}
[TestMethod]
public void Copy_Should_Copy_From_Source_Record()
{
var source = Rec(2, 123.5, 456.7, OptoTelegramFlags.OK_TestEnd);
source.Counter = 77;
source.RefFlow = 9.5f;
source.FlowRaw = 111;
source.VolumeRaw = 222;
source.Impedance = 333;
source.CheckSum = 44;
var target = new OptoTelegramRaw();
target.Copy(source);
Assert.AreEqual(source.iChannel, target.iChannel);
Assert.AreEqual(source.TimestampExt, target.TimestampExt);
Assert.AreEqual(source.VolumeRawExt, target.VolumeRawExt);
Assert.AreEqual(source.Flags, target.Flags);
Assert.AreEqual(source.Counter, target.Counter);
Assert.AreEqual(source.RefFlow, target.RefFlow);
Assert.AreEqual(source.FlowRaw, target.FlowRaw);
Assert.AreEqual(source.VolumeRaw, target.VolumeRaw);
Assert.AreEqual(source.Impedance, target.Impedance);
Assert.AreEqual(source.CheckSum, target.CheckSum);
}
[TestMethod]
public void RecalculateVolumeAndTimeDeltaPerChannel_Returns_Recalculated_Output()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(0, 20, 200),
Rec(1, 10, 500),
Rec(1, 30, 560),
Rec(2, 10, 1000),
Rec(2, 15, 1050),
};
var result = (OptoTelegramRaw[][])InvokePrivate(
"RecalculateVolumeAndTimeDeltaPerChannel",
(object)input,
input.Length,
0,
input.Length - 1);
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Length);
for (int ch = 0; ch < 3; ch++)
{
Assert.IsNotNull(result[ch]);
Assert.AreEqual(2, result[ch].Length);
Assert.IsNotNull(result[ch][0]);
Assert.IsNotNull(result[ch][1]);
Assert.AreEqual(10, result[ch][0].TimestampExt, 0.0001);
Assert.AreEqual(30, result[ch][1].TimestampExt, 0.0001);
}
Assert.AreEqual(100, result[0][0].VolumeRawExt, 0.0001);
Assert.AreEqual(300, result[0][1].VolumeRawExt, 0.0001);
Assert.AreEqual(500, result[1][0].VolumeRawExt, 0.0001);
Assert.AreEqual(560, result[1][1].VolumeRawExt, 0.0001);
Assert.AreEqual(1000, result[2][0].VolumeRawExt, 0.0001);
Assert.AreEqual(1200, result[2][1].VolumeRawExt, 0.0001);
}
[TestMethod]
public void RecalculateVolumeAndTimeDeltaPerChannel_Returns_Null_When_No_Channel_Has_Two_Records()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 20, 200),
Rec(2, 30, 300),
};
var result = (OptoTelegramRaw[][])InvokePrivate(
"RecalculateVolumeAndTimeDeltaPerChannel",
(object)input,
input.Length,
0,
input.Length - 1);
Assert.IsNull(result);
}
}
}
@@ -0,0 +1,163 @@
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;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class GenesisSmartReaderStopTests
{
private const int ChannelCount = 3;
private static GenesisCfg CreateCfg()
{
Factory factory = new Factory();
return new GenesisCfg(factory);
}
private static void SetPrivateField(object target, string fieldName, object value)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
field.SetValue(target, value);
}
private static T GetPrivateField<T>(object target, string fieldName)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
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
{
Flags = OptoTelegramFlags.OK,
VolumeRawExt = volumeRawExt,
TimestampExt = timestampExt,
iChannel = channel
};
}
private static void FillValidOptoDataForPostProcessing(GenesisSmartReader reader)
{
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
int idx = 0;
for (int group = 0; group < 6; group++)
{
optoData[idx++] = CreateOptoRecord(0, 10 + group, 100 + group);
optoData[idx++] = CreateOptoRecord(1, 20 + group, 100 + group);
optoData[idx++] = CreateOptoRecord(2, 30 + group, 100 + group);
}
SetPrivateField(reader, "optoDataCount", 18);
reader.TestStartTelegramIx = 1;
reader.TestEndTelegramIx = 17;
}
[TestMethod]
public void Stop_WhenStateIsCalculateStoredData_ShouldRunPostProcessing_AndFlushState()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
FillValidOptoDataForPostProcessing(reader);
SetPrivateField(reader, "dataStreamState", DataStreamState.CalculateStoredData);
SetPrivateField(reader, "startDataProcessing", false);
SetPrivateField(reader, "_stopQueueData", true);
fake.Open();
reader.optoSerialPort = fake;
reader.Stop();
var finalState = GetPrivateField<DataStreamState>(reader, "dataStreamState");
Assert.AreEqual(DataStreamState.Flush, finalState, "Stop() should finish in Flush state.");
Assert.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
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]
public void Stop_WhenStateIsProcessAndSave_ShouldNotRunPostProcessing_AndFlushState()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
FillValidOptoDataForPostProcessing(reader);
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
SetPrivateField(reader, "startDataProcessing", true);
SetPrivateField(reader, "_stopQueueData", false);
fake.Open();
reader.optoSerialPort = fake;
reader.Stop();
WaitForStopToFinish(reader);
var finalState = GetPrivateField<DataStreamState>(reader, "dataStreamState");
var stopQueueData = GetPrivateField<bool>(reader, "_stopQueueData");
Assert.AreEqual(DataStreamState.Flush, finalState, "Stop() should finish in Flush state.");
Assert.IsTrue(stopQueueData, "Immediate stop path should stop queueing.");
Assert.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
Assert.AreEqual(0.0, reader.VolumeLtrStartAverage, 1e-9,
"Post-processing should not run when previous state was ProcessAndSave.");
Assert.AreEqual(0.0, reader.VolumeLtrEndAwerage, 1e-9,
"Post-processing should not run when previous state was ProcessAndSave.");
}
[TestMethod]
public void RequestImmediateStop_ShouldNotChangeCalculateStoredDataState()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
SetPrivateField(reader, "dataStreamState", DataStreamState.CalculateStoredData);
SetPrivateField(reader, "startDataProcessing", true);
SetPrivateField(reader, "_stopQueueData", false);
reader.RequestImmediateStop();
var state = GetPrivateField<DataStreamState>(reader, "dataStreamState");
var stopQueueData = GetPrivateField<bool>(reader, "_stopQueueData");
var startDataProcessing = GetPrivateField<bool>(reader, "startDataProcessing");
Assert.AreEqual(DataStreamState.CalculateStoredData, state,
"RequestImmediateStop() must not destroy CalculateStoredData state.");
Assert.IsTrue(stopQueueData, "Immediate stop should stop queueing.");
Assert.IsFalse(startDataProcessing, "Immediate stop should disable active processing.");
}
}
}
@@ -1,5 +1,11 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Common;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig; using TBF.Rig;
@@ -39,6 +45,18 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
return (T)field.GetValue(target); return (T)field.GetValue(target);
} }
private static void InvokePrepareCalculatedChannelData(GenesisSmartReader reader)
{
var method = typeof(GenesisSmartReader).GetMethod(
"PrepareCalculatedChannelData",
BindingFlags.Instance | BindingFlags.NonPublic);
if (method == null)
throw new MissingMethodException(typeof(GenesisSmartReader).FullName, "PrepareCalculatedChannelData");
method.Invoke(reader, null);
}
private static void InitializeThreeChannelState(GenesisSmartReader reader) private static void InitializeThreeChannelState(GenesisSmartReader reader)
{ {
SetPrivateField(reader, "volumeRawExtLast", new double[ChannelCount]); SetPrivateField(reader, "volumeRawExtLast", new double[ChannelCount]);
@@ -54,6 +72,22 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
SetPrivateField(reader, "timestampSec0", new[] { 0.0, 0.0, 0.0 }); SetPrivateField(reader, "timestampSec0", new[] { 0.0, 0.0, 0.0 });
} }
private static void EnableProcessingLoopForTests(GenesisSmartReader reader)
{
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
SetPrivateField(reader, "startDataProcessing", true);
reader.StopQueueData = false;
}
private static void WaitForStopToFinish(GenesisSmartReader reader, int timeoutMs = 3000)
{
var stopTask = GetPrivateField<Task>(reader, "_backgroundStopTask");
if (stopTask != null)
{
stopTask.Wait(timeoutMs);
}
}
[TestMethod] [TestMethod]
public void Start_ShouldOpenPort_AndEnableProcessing() public void Start_ShouldOpenPort_AndEnableProcessing()
{ {
@@ -65,7 +99,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
Assert.IsTrue(fake.IsOpen); Assert.IsTrue(fake.IsOpen);
Assert.IsTrue(fake.OpenCalls >= 1); Assert.IsTrue(fake.OpenCalls >= 1);
Assert.AreEqual(1, fake.DiscardInCalls); Assert.AreEqual(2, fake.DiscardInCalls);
Assert.AreEqual(2, fake.DiscardOutCalls);
} }
[TestMethod] [TestMethod]
@@ -79,12 +114,38 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
Assert.IsTrue(fake.IsOpen); Assert.IsTrue(fake.IsOpen);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
Assert.IsNotNull(optoData);
if (optoData[0] == null)
optoData[0] = new OptoTelegramRaw();
if (optoData[1] == null)
optoData[1] = new OptoTelegramRaw();
optoData[0].Flags = OptoTelegramFlags.OK;
optoData[0].TimestampExt = 100.0;
optoData[0].VolumeRawExt = 10.0;
optoData[1].Flags = OptoTelegramFlags.OK;
optoData[1].TimestampExt = 101.0;
optoData[1].VolumeRawExt = 11.0;
SetPrivateField(reader, "optoDataCount", 2);
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
reader.TestStartTelegramIx = 0;
reader.TestEndTelegramIx = 1;
reader.Stop(); reader.Stop();
WaitForStopToFinish(reader);
Assert.IsFalse(fake.IsOpen); Assert.IsFalse(fake.IsOpen);
Assert.AreEqual(2, fake.CloseCalls); Assert.IsTrue(fake.CloseCalls >= 1);
} }
[TestMethod] [TestMethod]
public void Run_ShouldCaptureStartTelegramIndex_WhenTimeReached() public void Run_ShouldCaptureStartTelegramIndex_WhenTimeReached()
{ {
@@ -105,36 +166,42 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
} }
[TestMethod] [TestMethod]
public void ProcessOptoLine_ShouldIncreaseOptoDataCount() public void ProcessOptoLine_ShouldInsertMultipleTelegrams_WithCorrectChannels()
{ {
var fake = new FakeSerialDriver(); var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
var line =
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331";
reader.optoSerialPort = fake;
fake.Open(); fake.Open();
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave); var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount"); InitializeThreeChannelState(reader);
Assert.AreEqual(1, optoDataCount); 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);
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);
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(3, optoDataCount);
Assert.AreEqual(0, optoData[0].IChannel());
Assert.AreEqual(1, optoData[1].IChannel());
Assert.AreEqual(2, optoData[2].IChannel());
} }
[DataTestMethod] [DataTestMethod]
[DataRow( [DataRow("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", 0)]
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", [DataRow("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", 1)]
0)] [DataRow("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", 2)]
[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_ShouldUpdateExpectedChannel(string line, int expectedChannelIndex) public void ProcessOptoLine_ShouldUpdateExpectedChannel(string line, int expectedChannelIndex)
{ {
var fake = new FakeSerialDriver(); var fake = new FakeSerialDriver();
@@ -145,8 +212,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.optoSerialPort = fake; reader.optoSerialPort = fake;
InitializeThreeChannelState(reader); InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave); reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast"); var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast"); var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast");
@@ -185,7 +253,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var ev = reader.Run(); var ev = reader.Run();
Assert.AreEqual(Event.ReadRegisterDone, ev); Assert.AreEqual(Event.ReadRegisterDone, ev);
Assert.AreEqual(4.0, reader.WMVolume, 1E-6); // average of (3, 4, 5) Assert.AreEqual(4.0, reader.WMVolume, 1E-6);
Assert.AreEqual(4000, reader.WMPulses); Assert.AreEqual(4000, reader.WMPulses);
} }
@@ -200,20 +268,27 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.optoSerialPort = fake; reader.optoSerialPort = fake;
InitializeThreeChannelState(reader); InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
var line = var line =
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD"; "@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD";
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave); reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
int currentTelegramIx = GetPrivateField<int>(reader, "currentTelegramIx"); int currentTelegramIx = GetPrivateField<int>(reader, "currentTelegramIx");
Assert.AreEqual(0, currentTelegramIx); Assert.AreEqual(0, currentTelegramIx);
} }
[DataTestMethod] [DataTestMethod]
[DataRow( "@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", 0)] [DataRow(
[DataRow( "@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", 1)] "@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
[DataRow( "@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", 2)] 0)]
[DataRow(
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
1)]
[DataRow(
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
2)]
public void ProcessOptoLine_ShouldInsertTelegramAndUpdateExpectedChannel(string line, int expectedChannelIndex) public void ProcessOptoLine_ShouldInsertTelegramAndUpdateExpectedChannel(string line, int expectedChannelIndex)
{ {
var fake = new FakeSerialDriver(); var fake = new FakeSerialDriver();
@@ -223,34 +298,35 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.Initialize(); reader.Initialize();
reader.optoSerialPort = fake; reader.optoSerialPort = fake;
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave); InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast"); var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast"); var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast");
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData"); var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount"); var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(1, optoDataCount, "Exactly one telegram should be inserted."); Assert.AreEqual(1, optoDataCount);
var inserted = optoData[0]; var inserted = optoData[0];
Assert.IsNotNull(inserted, "Inserted telegram should not be null."); Assert.IsNotNull(inserted);
Assert.AreEqual(expectedChannelIndex, inserted.IChannel(), "Inserted telegram channel does not match expected channel."); Assert.AreEqual(expectedChannelIndex, inserted.IChannel());
Assert.AreEqual(0, inserted.Counter, "First inserted telegram should have counter 0."); Assert.AreEqual(0, inserted.Counter);
Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags, "Inserted telegram should be marked OK."); Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags);
Assert.AreNotEqual(default(DateTime), inserted.DateTime, "Inserted telegram DateTime should be initialized.");
Assert.AreNotEqual(0.0, inserted.VolumeRaw, "Inserted telegram VolumeRaw should be set."); Assert.AreNotEqual(0.0, inserted.VolumeRawExt);
Assert.AreNotEqual(0.0, inserted.VolumeRawExt, "Inserted telegram VolumeRawExt should be set."); Assert.AreNotEqual(0.0, inserted.TimestampExt);
Assert.AreNotEqual(0.0, inserted.Timestamp, "Inserted telegram Timestamp should be set.");
Assert.AreNotEqual(0.0, inserted.TimestampExt, "Inserted telegram TimestampExt should be set.");
for (int i = 0; i < ChannelCount; i++) for (int i = 0; i < ChannelCount; i++)
{ {
if (i == expectedChannelIndex) if (i == expectedChannelIndex)
{ {
Assert.AreNotEqual(0.0, volumeRawExtLast[i], $"Expected channel {i} volume cache was not updated."); Assert.AreNotEqual(0.0, volumeRawExtLast[i], $"Expected channel {i} volume cache was not updated.");
Assert.AreNotEqual(0.0, timestampExtLast[i], $"Expected channel {i} timestamp cache was not updated."); Assert.AreNotEqual(0.0, timestampExtLast[i],
$"Expected channel {i} timestamp cache was not updated.");
} }
else else
{ {
@@ -259,38 +335,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
} }
} }
Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9, Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9);
"Inserted telegram VolumeRawExt should match updated channel cache."); Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9);
Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9,
"Inserted telegram TimestampExt should match updated channel cache.");
} }
[TestMethod] [TestMethod]
public void ProcessOptoLine_ShouldInsertMultipleTelegrams_WithCorrectChannels() public void VolumeLtrStart_And_VolumeLtrEnd_ShouldUsePreparedCachedChannelData()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ProcessOptoLine( "@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave);
reader.ProcessOptoLine( "@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave);
reader.ProcessOptoLine( "@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(3, optoDataCount);
Assert.AreEqual(0, optoData[0].IChannel());
Assert.AreEqual(1, optoData[1].IChannel());
Assert.AreEqual(2, optoData[2].IChannel());
}
[TestMethod]
public void VolumeLtrStart_And_VolumeLtrEnd_ShouldUsePerChannelAverages()
{ {
var fake = new FakeSerialDriver(); var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake); var reader = new GenesisSmartReader(CreateCfg(), () => fake);
@@ -298,14 +348,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData"); var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
// Build 18 records: 6 groups of channels 0,1,2
// Start group average = (10 + 20 + 30)/3 = 20
// Last 5 per channel:
// ch0: 11,12,13,14,15 => avg 13
// ch1: 21,22,23,24,25 => avg 23
// ch2: 31,32,33,34,35 => avg 33
// final average = (13 + 23 + 33)/3 = 23
int idx = 0; int idx = 0;
for (int group = 0; group < 6; group++) for (int group = 0; group < 6; group++)
{ {
@@ -315,11 +357,22 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
} }
SetPrivateField(reader, "optoDataCount", 18); SetPrivateField(reader, "optoDataCount", 18);
reader.TestStartTelegramIx = 2; // first full 3-channel group reader.TestStartTelegramIx = 1;
reader.TestEndTelegramIx = 17; // last record reader.TestEndTelegramIx = 17;
Assert.AreEqual(20.0, reader.VolumeLtrStart, 1e-9); InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(23.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStart, 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.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) private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
@@ -344,7 +397,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
int idx = 0; int idx = 0;
// only channels 0 and 1
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
optoData[idx++] = CreateOptoRecord(0, 10 + i, 100 + i); optoData[idx++] = CreateOptoRecord(0, 10 + i, 100 + i);
@@ -355,8 +407,384 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.TestStartTelegramIx = 1; reader.TestStartTelegramIx = 1;
reader.TestEndTelegramIx = idx - 1; reader.TestEndTelegramIx = idx - 1;
// ch0 avg = 12, ch1 avg = 22 => total avg = 17 InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(17.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(15.5, reader.VolumeLtrStart, 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]
public void ProcessOptoLine_Block_f_h1_h2_h3_f_ShouldResetBuffersOnlyAfterLastF()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 1;
bool reset;
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen on the first @f.");
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 @h 1.");
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 @h 2.");
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 immediately after @h 3.");
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
//now some cycles must be runing, so reset must happen.
//Assert.IsTrue(reset, "Reset must happen after trailing @f that closes the h1/h2/h3 block.");
}
[TestMethod]
public void ProcessingLoop_FullBlock_ShouldDiscardBuffersAfterClosingF()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 1;
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
reader.StartProcessingLoop();
try
{
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");
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]
public void ProcessOptoLine_LastF_AfterH3_ShouldRequestBufferReset()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
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);
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);
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);
Assert.IsTrue(reset, "Closing @f after @h 3 must request reset.");
}
[TestMethod]
public void ProcessOptoLine_LastF_AfterH3_ShouldNotRequestReset_WhenRepetitionCountIsGreaterThanOne()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 3;
bool reset;
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
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);
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);
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after the first completed block when repetition count is 3.");
}
[TestMethod]
public void ProcessOptoLine_ShouldResetOnlyAfterThirdCompletedBlock()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
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);
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);
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);
if (repetition < 3)
Assert.IsFalse(reset, "Reset must not happen before third full block.");
else
Assert.IsTrue(reset, "Reset must happen on third full block.");
}
}
[TestMethod]
public void ProcessOptoLine_ShouldResetOnlyAfterFifthCompletedBlock()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
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);
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);
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);
if (repetition < 5)
Assert.IsFalse(reset, "Reset must not happen before fifth full block.");
else
Assert.IsTrue(reset, "Reset must happen on fifth full block.");
}
}
[TestMethod]
public void ProcessingLoop_Should_Keep_FIFO_Order_For_Enqueued_Items()
{
var reader = new GenesisSmartReader();
var expected = Enumerable.Range(1, 200)
.Select(i => $"LINE_{i:D4}")
.ToList();
var actual = new List<string>();
var sync = new object();
var allProcessed = new CountdownEvent(expected.Count);
reader.TestProcessLineOverride = line => true;
EnableProcessingLoopForTests(reader);
reader.TestLineProcessed = line =>
{
lock (sync)
{
actual.Add(line);
}
allProcessed.Signal();
};
reader.StartProcessingLoop();
try
{
foreach (var line in expected)
{
reader.TestEnqueueLine(line);
}
bool completed = allProcessed.Wait(TimeSpan.FromSeconds(10));
Assert.IsTrue(completed, "Timed out waiting for all queued items to be processed.");
CollectionAssert.AreEqual(expected, actual,
"Dequeued/processed order does not match enqueued order.");
}
finally
{
reader.StopProcessingLoop();
allProcessed.Dispose();
}
}
[TestMethod]
public void ProcessingLoop_Should_Keep_FIFO_Order_With_Concurrent_Producers()
{
var reader = new GenesisSmartReader();
const int producerCount = 4;
const int itemsPerProducer = 100;
int totalItems = producerCount * itemsPerProducer;
var expected = new List<string>();
var processed = new List<string>();
var expectedLock = new object();
var processedLock = new object();
int sequence = 0;
var allProcessed = new CountdownEvent(totalItems);
try
{
reader.TestProcessLineOverride = line => true;
EnableProcessingLoopForTests(reader);
reader.TestLineProcessed = line =>
{
lock (processedLock)
{
processed.Add(line);
}
allProcessed.Signal();
};
reader.StartProcessingLoop();
var tasks = new List<Task>();
for (int p = 0; p < producerCount; p++)
{
int producerId = p;
tasks.Add(Task.Run(() =>
{
for (int i = 0; i < itemsPerProducer; i++)
{
int seq = Interlocked.Increment(ref sequence);
string line = string.Format("{0:D6}|P{1}|I{2:D4}", seq, producerId, i);
lock (expectedLock)
{
expected.Add(line);
reader.TestEnqueueLine(line);
}
}
}));
}
Task.WaitAll(tasks.ToArray());
bool completed = allProcessed.Wait(TimeSpan.FromSeconds(15));
Assert.IsTrue(completed, "Timed out waiting for all concurrent items to be processed.");
CollectionAssert.AreEqual(expected, processed,
"Processed order does not match actual enqueue order under concurrent producers.");
}
finally
{
reader.StopProcessingLoop();
allProcessed.Dispose();
}
}
[TestMethod]
public void PrepareCalculatedChannelData_SimulateMode_ReturnsExpectedFakeValues()
{
var sut = new GenesisSmartReader();
sut.PrepareCalculatedChannelDataSimulationForTest();
Assert.IsFalse(sut.NoSamples);
Assert.AreEqual(100.0, sut.VolumeLtrStartCh1, 0.0001);
Assert.AreEqual(105.0, sut.VolumeLtrStartCh2, 0.0001);
Assert.AreEqual(110.0, sut.VolumeLtrStartCh3, 0.0001);
Assert.AreEqual(150.0, sut.VolumeLtrEndCh1, 0.0001);
Assert.AreEqual(157.0, sut.VolumeLtrEndCh2, 0.0001);
Assert.AreEqual(164.0, sut.VolumeLtrEndCh3, 0.0001);
Assert.AreEqual(10.0, sut.TimestampSecStartCh1, 0.0001);
Assert.AreEqual(10.0, sut.TimestampSecStartCh2, 0.0001);
Assert.AreEqual(10.0, sut.TimestampSecStartCh3, 0.0001);
Assert.AreEqual(20.0, sut.TimestampSecEndCh1, 0.0001);
Assert.AreEqual(20.0, sut.TimestampSecEndCh2, 0.0001);
Assert.AreEqual(20.0, sut.TimestampSecEndCh3, 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);
}
} }
} }
@@ -0,0 +1,389 @@
using System;
using System.Reflection;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class GenesisSmartReaderThreadedReadTests
{
private const int ChannelCount = 3;
private static GenesisCfg CreateCfg()
{
var cfg = new GenesisCfg(null);
cfg.OptoComPortNr = 7;
cfg.RfidComPortNr = 8;
return cfg;
}
private static void SetPrivateField(object target, string fieldName, object value)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
field.SetValue(target, value);
}
private static T GetPrivateField<T>(object target, string fieldName)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
return (T)field.GetValue(target);
}
private static bool WaitUntil(Func<bool> condition, int timeoutMs = 1500, int pollMs = 20)
{
var start = Environment.TickCount;
while (Environment.TickCount - start < timeoutMs)
{
if (condition())
return true;
Thread.Sleep(pollMs);
}
return condition();
}
private static void InitializeThreeChannelState(GenesisSmartReader reader)
{
SetPrivateField(reader, "volumeRawExtLast", new double[ChannelCount]);
SetPrivateField(reader, "timestampExtLast", new double[ChannelCount]);
SetPrivateField(reader, "lastVolumeRaw", new double[ChannelCount]);
SetPrivateField(reader, "lastTimestamp", new double[ChannelCount]);
SetPrivateField(reader, "volumeLtr", new[] { 0.0, 0.0, 0.0 });
SetPrivateField(reader, "volumeLtr0", new[] { 0.0, 0.0, 0.0 });
SetPrivateField(reader, "timestampSec", new[] { 0.0, 0.0, 0.0 });
SetPrivateField(reader, "timestampSec0", new[] { 0.0, 0.0, 0.0 });
}
private static FieldInfo GetAnyField(object target, string fieldName)
{
var field = target.GetType().GetField(
fieldName,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
return field;
}
private static void SetField(object target, string fieldName, object value)
{
GetAnyField(target, fieldName).SetValue(target, value);
}
private static T GetField<T>(object target, string fieldName)
{
return (T)GetAnyField(target, fieldName).GetValue(target);
}
private static void WaitForStartupFlushToFinish(GenesisSmartReader reader, int timeoutMs = 3000)
{
bool finished = WaitUntil(() =>
{
return !GetPrivateField<bool>(reader, "_startupFlushActive");
}, timeoutMs);
Assert.IsTrue(finished, "Startup flush did not finish in time.");
}
private static void ForceProcessingEnabled(GenesisSmartReader reader)
{
SetField(reader, "_startupFlushActive", false);
SetField(reader, "_stopQueueData", false);
SetField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
SetField(reader, "startDataProcessing", true);
var signal = GetField<AutoResetEvent>(reader, "_queueSignal");
signal.Set();
}
private static void EnqueueIncomingLine(GenesisSmartReader reader, string line)
{
var queue = GetField<System.Collections.Concurrent.ConcurrentQueue<(DateTime Timestamp, string Line)>>(
reader, "_receivedLines");
var signal = GetField<AutoResetEvent>(reader, "_queueSignal");
queue.Enqueue((DateTime.UtcNow, line));
signal.Set();
}
private static void InstallSuccessfulProcessingOverride(GenesisSmartReader reader)
{
Func<string, bool> handler = line =>
{
var optoData = GetField<OptoTelegramRaw[]>(reader, "optoData");
int count = GetField<int>(reader, "optoDataCount");
optoData[count].Counter = count;
optoData[count].SetFlags(OptoTelegramFlags.OK);
SetField(reader, "optoDataCount", count + 1);
return true;
};
SetField(reader, "TestProcessLineOverride", handler);
}
[TestMethod]
public void Start_ShouldOpenPort_AndStartBackgroundReadLoop()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.Start();
Assert.IsTrue(fake.IsOpen);
Assert.IsTrue(fake.OpenCalls >= 1);
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
Assert.IsNotNull(readLoopTask);
}
[TestMethod]
public void ProcessingLoop_ShouldConsumeQueuedLine()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
ForceProcessingEnabled(reader);
InstallSuccessfulProcessingOverride(reader);
EnqueueIncomingLine(reader, "TEST_LINE_1");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetField<int>(reader, "optoDataCount");
return optoDataCount == 1;
}, 3000);
Assert.IsTrue(processed, "Expected processing loop to consume queued line.");
int finalCount = GetField<int>(reader, "optoDataCount");
Assert.AreEqual(1, finalCount);
}
[TestMethod]
public void ProcessingLoop_ShouldConsumeQueuedLine_AndInsertTelegram()
{
var fake = new FakeSerialDriver();
fake.Encoding = System.Text.Encoding.ASCII;
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
ForceProcessingEnabled(reader);
EnqueueIncomingLine(reader,
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(processed, "Expected processing loop to consume queued line.");
int finalCount = GetPrivateField<int>(reader, "optoDataCount");
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
Assert.AreEqual(1, finalCount);
Assert.AreEqual(1, optoData[0].IChannel());
}
[TestMethod]
public void ProcessingLoop_ShouldConsumeAllQueuedLines()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
ForceProcessingEnabled(reader);
InstallSuccessfulProcessingOverride(reader);
EnqueueIncomingLine(reader, "TEST_LINE_1");
EnqueueIncomingLine(reader, "TEST_LINE_2");
EnqueueIncomingLine(reader, "TEST_LINE_3");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetField<int>(reader, "optoDataCount");
return optoDataCount == 3;
}, 3000);
Assert.IsTrue(processed, "Expected all queued lines to be processed.");
int finalCount = GetField<int>(reader, "optoDataCount");
var queueAfter = GetField<System.Collections.Concurrent.ConcurrentQueue<(DateTime Timestamp, string Line)>>(
reader, "_receivedLines");
Assert.AreEqual(3, finalCount);
Assert.IsTrue(queueAfter.IsEmpty);
}
[TestMethod]
public void IncomingLines_ShouldBeProcessedByBackgroundProcessingLoop()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
WaitForStartupFlushToFinish(reader);
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(processed, "Expected background processing loop to process line.");
}
[TestMethod]
public void StopDataStreamProcessing_ShouldStopReadAndProcessingLoops()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
reader.StopDataStreamProcessing();
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
var readLoopCts = GetPrivateField<CancellationTokenSource>(reader, "_readLoopCts");
var processLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_processLoopTask");
var processLoopCts = GetPrivateField<CancellationTokenSource>(reader, "_processLoopCts");
Assert.IsNull(readLoopTask);
Assert.IsNull(readLoopCts);
Assert.IsNull(processLoopTask);
Assert.IsNull(processLoopCts);
}
[TestMethod]
public void IncomingLine_AfterStop_ShouldNotBeProcessed()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
reader.StopDataStreamProcessing();
int before = GetPrivateField<int>(reader, "optoDataCount");
fake.EnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
Thread.Sleep(300);
int after = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(before, after, "No new telegram should be processed after stop.");
}
[TestMethod]
public void InvalidLine_ShouldNotCrashProcessingLoop()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
WaitForStartupFlushToFinish(reader);
fake.EnqueueLine("INVALID_LINE_THAT_WILL_NOT_PARSE");
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(processed, "Processing loop should survive invalid input and continue.");
}
[TestMethod]
public void StartStop_StartAgain_ShouldStillProcessData()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
reader.StopDataStreamProcessing();
reader.Start();
WaitForStartupFlushToFinish(reader);
fake.EnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(processed, "Reader should still process data after restart.");
}
[TestMethod]
public void Start_ShouldOpenPort_AndStartBothBackgroundLoops()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.Start();
Assert.IsTrue(fake.IsOpen);
Assert.IsTrue(fake.OpenCalls >= 1);
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
var processLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_processLoopTask");
Assert.IsNotNull(readLoopTask);
Assert.IsNotNull(processLoopTask);
}
}
}
@@ -0,0 +1,427 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
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_ShouldRemainInvalid_WhenRawDataIsNull()
{
var sut = new GenesisSmartReader();
InitializeReaderForQ3Test(sut);
SetPrivateField(sut, "_rawStartEndByChannel", null);
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);
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
}
[TestMethod]
public void GetQ3Calibration_ShouldRemainInvalid_WhenNoSamplesIsTrue()
{
var sut = new GenesisSmartReader();
InitializeReaderForQ3Test(sut);
SetPrivateField(sut, "_rawStartEndByChannel", CreateMinimalStartEndData());
Assert.IsTrue(sut.NoSamples, "Expected NoSamples to be true for this test.");
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);
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
}
[TestMethod]
public void GetQ3Calibration_ShouldRemainInvalid_WhenRefVolumeIsZero()
{
var sut = new GenesisSmartReader();
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
Assert.IsFalse(sut.NoSamples);
var valid = new bool[3];
var calib = new double[3];
sut.GetQ3Calibration(
refVolume: 0.0,
refTime: 120.0,
initCalibFactor: ValidInitFactors,
ref valid,
ref calib);
Assert.IsFalse(sut.Q3CalibValid);
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
}
[TestMethod]
public void GetQ3Calibration_ShouldRemainInvalid_WhenRefTimeIsZero()
{
var sut = new GenesisSmartReader();
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
Assert.IsFalse(sut.NoSamples);
var valid = new bool[3];
var calib = new double[3];
sut.GetQ3Calibration(
refVolume: 1000.0,
refTime: 0.0,
initCalibFactor: ValidInitFactors,
ref valid,
ref calib);
Assert.IsFalse(sut.Q3CalibValid);
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
}
[TestMethod]
public void GetQ3Calibration_ShouldMarkInvalid_WhenInitFactorsAreZero()
{
var sut = new GenesisSmartReader();
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
Assert.IsFalse(sut.NoSamples);
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: init,
ref valid,
ref calib);
Assert.IsFalse(sut.Q3CalibValid);
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
CollectionAssert.AreEqual(new[] { 0.0, 0.0, 0.0 }, calib);
}
[TestMethod]
public void CalculateQ3Calibration_ShouldPopulatePerChannelValues_WithPreparedSimulationData()
{
var sut = new GenesisSmartReader();
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
InvokePrivateByName(sut, "SetQ3Calibration", new object[] { ValidInitFactors });
Assert.IsFalse(sut.NoSamples);
sut.CalculateQ3Calibration(200.0, 120.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)
{
SetPrivateField(reader, "_rawStartEndByChannel", null);
SetPrivateField(reader, "_recalculatedStartEndByChannel", null);
SetPrivateField(reader, "optoDataCount", 0);
reader.TestStartTelegramIx = -1;
reader.TestEndTelegramIx = -1;
}
private static OptoTelegramRaw[][] CreateMinimalStartEndData()
{
return new[]
{
new[]
{
CreateRecord(0.0, 0.0, 1),
CreateRecord(15625.0, 120.0, 1)
},
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[][] 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,
iChannel = channel,
Flags = OptoTelegramFlags.OK
};
}
private static object InvokePrivateByName(object target, string methodName, params object[] args)
{
Type type = target.GetType();
while (type != null)
{
var candidates = type.GetMethods(
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.DeclaredOnly)
.Where(m => m.Name == methodName)
.ToList();
foreach (var method in candidates)
{
var parameters = method.GetParameters();
if (parameters.Length != (args?.Length ?? 0))
continue;
try
{
return method.Invoke(target, args);
}
catch (ArgumentException)
{
}
}
type = type.BaseType;
}
Assert.Fail($"Method '{methodName}' not found or no matching overload accepted the provided arguments.");
return null;
}
private static void SetPrivateField(object target, string fieldName, object value)
{
Type type = target.GetType();
while (type != null)
{
var field = type.GetField(
fieldName,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
if (field != null)
{
field.SetValue(target, value);
return;
}
type = type.BaseType;
}
Assert.Fail($"Field '{fieldName}' not found.");
}
}
}
@@ -12,35 +12,36 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
[TestSubject(typeof(CliRunner))] [TestSubject(typeof(CliRunner))]
public class CliRunnerTest public class CliRunnerTest
{ {
[TestMethod] [TestMethod]
public void ExtractJson_Test() public void ExtractJson_Test()
{ {
String allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n"; string allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
CliRunner cliRunner = new CliRunner(false); CliRunner cliRunner = new CliRunner(false);
string json = cliRunner.ExtractJson(allOutput); string json = cliRunner.ExtractJson(allOutput);
Assert.IsTrue(!string.IsNullOrEmpty(json)); Assert.IsFalse(string.IsNullOrEmpty(json));
} }
[TestMethod] [TestMethod]
public void ExtractJson_Test2() public void ExtractJson_Test2()
{ {
String allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n"; string allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
CliRunner cliRunner = new CliRunner(false); CliRunner cliRunner = new CliRunner(false);
string json = cliRunner.ExtractJson(allOutput); string json = cliRunner.ExtractJson(allOutput);
Assert.IsTrue(!string.IsNullOrEmpty(json)); Assert.IsFalse(string.IsNullOrEmpty(json));
} }
[TestMethod] [TestMethod]
public void TryJsonStringDeserialize_Test() public void TryJsonStringDeserialize_Test()
{ {
String allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n"; string allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
CliRunner cliRunner = new CliRunner(false); CliRunner cliRunner = new CliRunner(false);
string json = cliRunner.ExtractJson(allOutput); string json = cliRunner.ExtractJson(allOutput);
var ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto); bool ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
Assert.IsTrue(ok, "Deserialization failed"); Assert.IsTrue(ok, "Deserialization failed");
Assert.IsNotNull(dto); Assert.IsNotNull(dto);
Assert.AreEqual("1000000267", dto.DeviceId); Assert.AreEqual("1000000267", dto.DeviceId);
@@ -53,120 +54,165 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
[TestMethod] [TestMethod]
public void RunMultipleTimesTestProgram_CheckParalelWork() public void RunMultipleTimesTestProgram_CheckParalelWork()
{ {
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74); SerialPortData serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
// Check if the executable exists in current directory
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath)) if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
{ {
Assert.Inconclusive($"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory."); Assert.Inconclusive(
$"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
return; return;
} }
CliRunner cliRunner = new CliRunner(false); CliRunner cliRunner = new CliRunner(false);
CliRunner cliRunnerOne = new CliRunner(false); CliRunner cliRunnerOne = new CliRunner(false);
DateTime StartTime = DateTime.Now; DateTime startTime = DateTime.Now;
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
cliRunnerOne.WaitAll();
TimeSpan OneEndTime = DateTime.Now - StartTime;
StartTime = DateTime.Now; cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}"); serialPort,
SerialPortData.EMeterArg.AllParams);
cliRunnerOne.WaitAll();
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++) for (int i = 0; i < 100; i++)
{ {
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams); cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
serialPort,
SerialPortData.EMeterArg.AllParams);
} }
cliRunner.WaitAll(); cliRunner.WaitAll();
DateTime EndTime = DateTime.Now; DateTime endTime = DateTime.Now;
Console.WriteLine($"End Time Loop: {EndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); Console.WriteLine($"End Time Loop: {endTime:yyyy-MM-dd HH:mm:ss}");
TimeSpan delta = EndTime - StartTime;
Console.WriteLine($"Delta Time One process {OneEndTime.Hours:D2}:{OneEndTime.Minutes:D2}:{OneEndTime.Seconds:D2}.{OneEndTime.Milliseconds:D3} " + TimeSpan delta = endTime - startTime;
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}"); Console.WriteLine(
$"Delta Time One process {oneEndTime.Hours:D2}:{oneEndTime.Minutes:D2}:{oneEndTime.Seconds:D2}.{oneEndTime.Milliseconds:D3} " +
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
Console.WriteLine($"Total tasks: {cliRunner.TaskPool.Count}"); Console.WriteLine($"Total tasks: {cliRunner.TaskPool.Count}");
foreach (CliTaskInfo taskInfo in cliRunner.TaskPool)
foreach (Task<JsonDataFromPoseidon> task in cliRunner.TaskPool)
{ {
if (task != null) Assert.IsNotNull(taskInfo);
Assert.IsNotNull(taskInfo.Task);
if (taskInfo.UseResult)
{ {
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
Assert.IsNotNull(task, "Expected Task<JsonDataFromPoseidon>.");
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result; JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
Console.WriteLine($"Result: {jsonDataFromPoseidon.DeviceId}"); Console.WriteLine($"Result: {jsonDataFromPoseidon?.DeviceId}");
Assert.IsNotNull(jsonDataFromPoseidon); Assert.IsNotNull(jsonDataFromPoseidon);
} }
else
{
Assert.Fail($"Task did not complete successfully. State={taskInfo.State}, Status={taskInfo.Task.Status}");
}
} }
// Check that the total time is greater than the sum of the individual times Assert.IsTrue((cliRunner.TaskPool.Count * oneEndTime.Ticks) > delta.Ticks);
Assert.IsTrue((cliRunner.TaskPool.Count*OneEndTime.Ticks) > delta.Ticks);
} }
[TestMethod] [TestMethod]
public async Task RunMultipleTimesTestProgram_CheckParalelWorkCumulative() public async Task RunMultipleTimesTestProgram_CheckParalelWorkCumulative()
{ {
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74); SerialPortData serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
// Check if the executable exists in current directory
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath)) if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
{ {
Assert.Inconclusive($"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory."); Assert.Inconclusive(
$"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
return; return;
} }
List<CliRunner> cliRunnerList = new List<CliRunner>(); List<CliRunner> cliRunnerList = new List<CliRunner>();
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{
cliRunnerList.Add(new CliRunner(false)); cliRunnerList.Add(new CliRunner(false));
}
CliRunner cliRunnerOne = new CliRunner(false); CliRunner cliRunnerOne = new CliRunner(false);
DateTime StartTime = DateTime.Now; DateTime startTime = DateTime.Now;
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
cliRunnerOne.WaitAll();
TimeSpan OneEndTime = DateTime.Now - StartTime;
StartTime = DateTime.Now; cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}"); serialPort,
SerialPortData.EMeterArg.AllParams);
cliRunnerOne.WaitAll();
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++) for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
{ {
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
serialPort,
SerialPortData.EMeterArg.AllParams); SerialPortData.EMeterArg.AllParams);
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
serialPort,
SerialPortData.EMeterArg.AllParams); SerialPortData.EMeterArg.AllParams);
} }
} }
// Wait for ALL tasks from ALL runners Task[] allTasks = cliRunnerList
var allTasks = cliRunnerList.SelectMany(r => r.TaskPool).ToArray(); .SelectMany(r => r.TaskPool)
.Where(ti => ti != null && ti.Task != null)
.Select(ti => ti.Task)
.ToArray();
await Task.WhenAll(allTasks); await Task.WhenAll(allTasks);
DateTime EndTime = DateTime.Now; DateTime endTime = DateTime.Now;
Console.WriteLine($"End Time Loop: {EndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); Console.WriteLine($"End Time Loop: {endTime:yyyy-MM-dd HH:mm:ss}");
TimeSpan delta = EndTime - StartTime;
Console.WriteLine($"Delta Time One process {OneEndTime.Hours:D2}:{OneEndTime.Minutes:D2}:{OneEndTime.Seconds:D2}.{OneEndTime.Milliseconds:D3} " + TimeSpan delta = endTime - startTime;
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}"); Console.WriteLine(
$"Delta Time One process {oneEndTime.Hours:D2}:{oneEndTime.Minutes:D2}:{oneEndTime.Seconds:D2}.{oneEndTime.Milliseconds:D3} " +
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
Console.WriteLine($"Total tasks: {cliRunnerList.Sum(runner => runner.TaskPool.Count)}"); Console.WriteLine($"Total tasks: {cliRunnerList.Sum(runner => runner.TaskPool.Count)}");
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++) for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
{ {
string results = $"iCliRunner: {iCliRunner}"; string results = $"iCliRunner: {iCliRunner}";
foreach (Task<JsonDataFromPoseidon> task in cliRunnerList[iCliRunner].TaskPool)
foreach (CliTaskInfo taskInfo in cliRunnerList[iCliRunner].TaskPool)
{ {
if (task != null) Assert.IsNotNull(taskInfo);
Assert.IsNotNull(taskInfo.Task);
if (taskInfo.UseResult)
{ {
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
Assert.IsNotNull(task, "Expected Task<JsonDataFromPoseidon>.");
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result; JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
results += ($" ID: {jsonDataFromPoseidon.DeviceId}"); results += $" ID: {jsonDataFromPoseidon?.DeviceId}";
Assert.IsNotNull(jsonDataFromPoseidon); Assert.IsNotNull(jsonDataFromPoseidon);
} }
else
{
Assert.Fail(
$"Task did not complete successfully. Runner={iCliRunner}, State={taskInfo.State}, Status={taskInfo.Task.Status}");
}
} }
Console.WriteLine(results); Console.WriteLine(results);
} }
// Check that the total time is greater than the sum of the individual times Assert.IsTrue((cliRunnerList.Count * oneEndTime.Ticks) > delta.Ticks);
Assert.IsTrue((cliRunnerList.Count*OneEndTime.Ticks) > delta.Ticks);
} }
} }
} }
@@ -0,0 +1,249 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
{
[TestClass]
[TestSubject(typeof(CliRunner))]
public class CliRunnerTimeoutTest
{
private static string GetCmdSleepTestPath()
{
var serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
return serialPort.SerialPortCmdClientPath;
}
private static void EnsureExeExists(string exePath)
{
if (!File.Exists(exePath))
{
Assert.Inconclusive(
$"Test executable '{exePath}' not found. " +
"Please ensure cmdSleepTest.exe exists in the test directory.");
}
}
[TestMethod]
public async Task Timeout_ShouldBeDetected_ForLongRunningTask()
{
string exePath = GetCmdSleepTestPath();
EnsureExeExists(exePath);
var cliRunner = new CliRunner(false);
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
await Task.Delay(200);
bool timedOut = cliRunner.TimeOutReceived(100);
Assert.IsTrue(timedOut, "Timeout should have been detected.");
}
[TestMethod]
public async Task CancelUndoneTasksAsTimedOut_ShouldMarkRunningTaskAsTimedOut()
{
string exePath = GetCmdSleepTestPath();
EnsureExeExists(exePath);
var cliRunner = new CliRunner(false);
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
await Task.Delay(150);
cliRunner.CancelUndoneTasksAsTimedOut();
cliRunner.RefreshTaskStates();
Assert.AreEqual(1, cliRunner.TaskPool.Count);
var taskInfo = cliRunner.TaskPool.Single();
Assert.IsNotNull(taskInfo);
Assert.AreEqual(CliTaskState.TimedOut, taskInfo.State);
Assert.IsFalse(taskInfo.UseResult, "Timed out task must not be used.");
}
[TestMethod]
public void CancelUndoneTasksAsTimedOut_ShouldKeepCompletedTaskCompleted()
{
string exePath = GetCmdSleepTestPath();
EnsureExeExists(exePath);
var cliRunner = new CliRunner(false);
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50");
try
{
cliRunner.WaitAll();
}
catch
{
// let assertions inspect states
}
cliRunner.RefreshTaskStates();
cliRunner.CancelUndoneTasksAsTimedOut();
cliRunner.RefreshTaskStates();
Assert.AreEqual(1, cliRunner.TaskPool.Count);
var taskInfo = cliRunner.TaskPool.Single();
Assert.IsNotNull(taskInfo);
Assert.AreEqual(CliTaskState.Completed, taskInfo.State);
Assert.IsTrue(taskInfo.UseResult, "Completed task should remain usable.");
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
Assert.IsNotNull(task);
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
Assert.IsNotNull(task.Result);
Assert.IsFalse(string.IsNullOrEmpty(task.Result.DeviceId));
}
[TestMethod]
public async Task CancelUndoneTasksAsTimedOut_ShouldKeepDoneTask_AndMarkUndoneTask()
{
string exePath = GetCmdSleepTestPath();
EnsureExeExists(exePath);
var cliRunner = new CliRunner(false);
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50");
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
await Task.Delay(250);
cliRunner.RefreshTaskStates();
Assert.AreEqual(2, cliRunner.TaskPool.Count);
Assert.AreEqual(1, cliRunner.TaskPool.Count(t => t.State == CliTaskState.Completed),
"Exactly one fast task should be completed before timeout handling.");
cliRunner.CancelUndoneTasksAsTimedOut();
cliRunner.RefreshTaskStates();
var doneTask = cliRunner.TaskPool.Single(t => t.State == CliTaskState.Completed);
var timedOutTask = cliRunner.TaskPool.Single(t => t.State == CliTaskState.TimedOut);
Assert.IsTrue(doneTask.UseResult);
Assert.IsFalse(timedOutTask.UseResult);
var completed = doneTask.Task as Task<JsonDataFromPoseidon>;
Assert.IsNotNull(completed);
Assert.AreEqual(TaskStatus.RanToCompletion, completed.Status);
Assert.IsNotNull(completed.Result);
}
[TestMethod]
public async Task AreTasksDone_ShouldReturnTrue_AfterTimeoutCancellation()
{
string exePath = GetCmdSleepTestPath();
EnsureExeExists(exePath);
var cliRunner = new CliRunner(false);
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
await Task.Delay(150);
Assert.IsFalse(cliRunner.AreTasksDone(), "Task should still be running before timeout cancellation.");
cliRunner.CancelUndoneTasksAsTimedOut();
cliRunner.RefreshTaskStates();
Assert.IsTrue(cliRunner.AreTasksDone(),
"After timed out tasks are marked, runner should report no running tasks.");
}
[TestMethod]
public async Task TimedOutTask_ShouldNotHaveUseResult()
{
string exePath = GetCmdSleepTestPath();
EnsureExeExists(exePath);
var cliRunner = new CliRunner(false);
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
await Task.Delay(150);
cliRunner.CancelUndoneTasksAsTimedOut();
cliRunner.RefreshTaskStates();
var taskInfo = cliRunner.TaskPool.Single();
Assert.AreEqual(CliTaskState.TimedOut, taskInfo.State);
Assert.IsFalse(taskInfo.UseResult);
}
[TestMethod]
public async Task MultipleSlowTasks_ShouldAllBeMarkedTimedOut()
{
string exePath = GetCmdSleepTestPath();
EnsureExeExists(exePath);
var cliRunner = new CliRunner(false);
for (int i = 0; i < 5; i++)
{
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
}
await Task.Delay(200);
cliRunner.CancelUndoneTasksAsTimedOut();
cliRunner.RefreshTaskStates();
Assert.AreEqual(5, cliRunner.TaskPool.Count);
Assert.AreEqual(5, cliRunner.TaskPool.Count(t => t.State == CliTaskState.TimedOut));
Assert.AreEqual(0, cliRunner.TaskPool.Count(t => t.UseResult));
}
[TestMethod]
public void FastTask_WithInvalidJson_ShouldFinishButNotProduceUsefulResult()
{
string exePath = GetCmdSleepTestPath();
EnsureExeExists(exePath);
var cliRunner = new CliRunner(false);
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50 invalidjson=true");
try
{
cliRunner.WaitAll();
}
catch
{
}
cliRunner.RefreshTaskStates();
Assert.AreEqual(1, cliRunner.TaskPool.Count);
var taskInfo = cliRunner.TaskPool.Single();
Assert.AreEqual(CliTaskState.Completed, taskInfo.State);
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
Assert.IsNotNull(task);
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
// depending on your TryJsonStringDeserialize fallback,
// result may be null or an empty/default object
// so avoid asserting UseResult=false here
// instead assert no valid device id
if (task.Result != null)
{
Assert.IsTrue(string.IsNullOrEmpty(task.Result.DeviceId),
"Invalid JSON should not produce a valid DeviceId.");
}
}
}
}
@@ -0,0 +1,76 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
{
[TestClass]
[TestSubject(typeof(CliRunner))]
public class CliRunnerTimeoutUnitTest
{
[TestMethod]
public async Task CancelUndoneTasksAsTimedOut_ShouldMarkOnlyRunningTasks()
{
var cliRunner = new CliRunner(false);
var completedTask = Task.FromResult("done");
var cts = new CancellationTokenSource();
var runningTask = Task.Delay(TimeSpan.FromSeconds(30), cts.Token);
cliRunner.AddTaskForTest(completedTask, "completed");
cliRunner.AddTaskForTest(runningTask, "running", cts);
await Task.Delay(50);
cliRunner.RefreshTaskStates();
cliRunner.CancelUndoneTasksAsTimedOut();
cliRunner.RefreshTaskStates();
Assert.AreEqual(2, cliRunner.TaskPool.Count);
var completed = cliRunner.TaskPool.First(t => t.Name == "completed");
var timedOut = cliRunner.TaskPool.First(t => t.Name == "running");
Assert.AreEqual(CliTaskState.Completed, completed.State);
Assert.IsTrue(completed.UseResult);
Assert.AreEqual(CliTaskState.TimedOut, timedOut.State);
Assert.IsFalse(timedOut.UseResult);
}
[TestMethod]
public void TimeOutReceived_ShouldReturnTrue_WhenElapsedExceedsTimeout()
{
var cliRunner = new CliRunner(false);
Thread.Sleep(30);
Assert.IsTrue(cliRunner.TimeOutReceived(1));
}
[TestMethod]
public async Task AreTasksDone_ShouldReturnTrue_WhenTimedOutTasksAreMarked()
{
var cliRunner = new CliRunner(false);
var cts = new CancellationTokenSource();
var runningTask = Task.Delay(TimeSpan.FromSeconds(30), cts.Token);
cliRunner.AddTaskForTest(runningTask, "running", cts);
await Task.Delay(50);
Assert.IsFalse(cliRunner.AreTasksDone());
cliRunner.CancelUndoneTasksAsTimedOut();
cliRunner.RefreshTaskStates();
Assert.IsTrue(cliRunner.AreTasksDone());
}
}
}
+7
View File
@@ -108,8 +108,13 @@
<Compile Include="Rig\Output\FileWriters\Enhanced\WriterTest.cs" /> <Compile Include="Rig\Output\FileWriters\Enhanced\WriterTest.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\common\OptoTelegramRawTest.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\common\OptoTelegramRawTest.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\FakeSerialDriver.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\FakeSerialDriver.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisReaderTests.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderChannelAveragingTests.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderChannelAveragingTests.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderChannelTests.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderStopTests.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderTest.cs" /> <Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderTest.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderThreadedReadTests.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReader_Q3Test.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\DiagnosticLedParserTest.cs" /> <Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\DiagnosticLedParserTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\HexFormatterTest.cs" /> <Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\HexFormatterTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrameBuilderTests.cs" /> <Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrameBuilderTests.cs" />
@@ -120,6 +125,8 @@
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadBaudRateDetectionTests.cs" /> <Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadBaudRateDetectionTests.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrameBuilderTest.cs" /> <Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrameBuilderTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTest.cs" /> <Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutUnitTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" /> <Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" /> <Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" /> <Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />