Compare commits

...

22 Commits

Author SHA1 Message Date
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
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
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
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
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
711dfc03ef Fix Q3 calibration logic in GenesisSmartReader: replace _rawStartEndByChannel with recalculatedVariablesByChannel for accurate data computation. 2026-04-14 15:54:28 +02:00
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
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
d2e18d615d Bump assembly version to 3.9.3053.1. 2026-04-13 14:17:56 +02:00
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
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
65ab19290f simulate mode - flow reached 2026-04-09 12:49:00 +02:00
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
2136695b80 Fix Stop 2026-04-08 16:37:27 +02:00
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
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
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
f7dc6fee5e immediate Stop improve 2026-04-07 16:37:19 +02:00
ba848a167d improve 2026-04-07 15:44:32 +02:00
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
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
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
32 changed files with 8766 additions and 1134 deletions

View File

@ -19,6 +19,9 @@ using System.Runtime.InteropServices;
// COM, set the ComVisible attribute to true on that type.
[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
[assembly: Guid("2c13538e-15ee-48b2-af0f-2c95afb67186")]
@ -29,5 +32,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
[assembly: AssemblyVersion("3.9.3031.1")]
[assembly: AssemblyFileVersion("3.9.3031.1")]
[assembly: AssemblyVersion("3.9.3058.1")]
[assembly: AssemblyFileVersion("3.9.3058.1")]

View File

@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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
// 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>
/// Looks up a localized string similar to Test in progress.
/// </summary>

File diff suppressed because it is too large Load Diff

View File

@ -1764,4 +1764,7 @@
<data name="Units" xml:space="preserve">
<value />
</data>
<data name="Test_in_calculation" xml:space="preserve">
<value>Probíhá výpočet</value>
</data>
</root>

View File

@ -2482,4 +2482,7 @@
<data name="Units" xml:space="preserve">
<value>Units</value>
</data>
<data name="Test_in_calculation" xml:space="preserve">
<value>Test in calculation</value>
</data>
</root>

View File

@ -259,4 +259,7 @@
<data name="Units" xml:space="preserve">
<value />
</data>
<data name="Test_in_calculation" xml:space="preserve">
<value>Prebieha výpočet</value>
</data>
</root>

View File

@ -16,8 +16,6 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
{
return new GenesisCfgCtrl();
}
///
/// Serialized parameters
@ -31,8 +29,10 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
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 CommunicationInterface CommunicationInterface; /// Communication Interface: RFID or NFC
///
public bool EnableShowChanels;
public int IBeginDataFlush;
/// <summary> Procedure parameters </summary>
[XmlIgnore]
@ -56,6 +56,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
ProcParams = CreateProcParamsProvider() as ProcParams;
CommunicationInterface = CommunicationInterface.RFID;
HeadCommunicationComPortNr = 0;
EnableShowChanels = false;
IBeginDataFlush = 2000;
}
public GenesisCfg(IComponentFactory factory)
@ -67,7 +69,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
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}";
}
}
}

View File

@ -58,6 +58,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
muxBoardNrTextBox.Text = config.MuxBoardNr.ToString();
groupTextBox.Text = config.Group.ToString();
comboBoxCommunicationInterface.SelectedItem = config.CommunicationInterface.ToString();
tBBeginDataFlush.Text = config.IBeginDataFlush.ToString();
checkBox_EnableShowChanels.Checked = config.EnableShowChanels;
tabPage2.Controls.Add(new GenesisHeadTestCtrl(config));
}
@ -74,6 +77,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
muxBoardNrTextBox.Enabled = true;
groupTextBox.Enabled = true;
comboBoxCommunicationInterface.Enabled = true;
tBBeginDataFlush.Enabled = true;
checkBox_EnableShowChanels.Enabled = true;
}
public CfgUpdateFlags VerifyCfg(ref string message)
@ -129,6 +134,12 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
flags |= CfgUpdateFlags.Error;
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;
}
@ -158,6 +169,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
config.Group = int.Parse(groupTextBox.Text);
config.CommunicationInterface = (CommunicationInterface)comboBoxCommunicationInterface.SelectedIndex;
config.HeadCommunicationComPortNr = int.Parse(headPortNrTextBox.Text);
config.IBeginDataFlush = int.Parse(tBBeginDataFlush.Text);
config.EnableShowChanels = checkBox_EnableShowChanels.Checked;
return flags;
}

View File

@ -25,388 +25,433 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.label4 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.comboBoxCommunicationInterface = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.rfidPortNrTextBox = new System.Windows.Forms.TextBox();
this.rfidSerialPortNrLabel = new System.Windows.Forms.Label();
this.optoDataGroupBox = new System.Windows.Forms.GroupBox();
this.tcpipPortLabel = new System.Windows.Forms.Label();
this.tcpipPortTextBox = new System.Windows.Forms.TextBox();
this.ipAddressLabel = new System.Windows.Forms.Label();
this.ipAddressTextBox = new System.Windows.Forms.TextBox();
this.radioButton1 = new System.Windows.Forms.RadioButton();
this.radioButton2 = new System.Windows.Forms.RadioButton();
this.optoSerialPortLabel = new System.Windows.Forms.Label();
this.optoSerialPortTextBox = new System.Windows.Forms.TextBox();
this.groupTextBox = new System.Windows.Forms.TextBox();
this.groupLabel = new System.Windows.Forms.Label();
this.muxBoardNrTextBox = new System.Windows.Forms.TextBox();
this.muxBoardNrLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.classNameLabel = new System.Windows.Forms.Label();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.label2 = new System.Windows.Forms.Label();
this.headPortNrTextBox = new System.Windows.Forms.TextBox();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.groupBox1.SuspendLayout();
this.optoDataGroupBox.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Location = new System.Drawing.Point(3, 3);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(611, 432);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.groupBox2);
this.tabPage1.Controls.Add(this.label4);
this.tabPage1.Controls.Add(this.label3);
this.tabPage1.Controls.Add(this.groupBox1);
this.tabPage1.Controls.Add(this.optoDataGroupBox);
this.tabPage1.Controls.Add(this.groupTextBox);
this.tabPage1.Controls.Add(this.groupLabel);
this.tabPage1.Controls.Add(this.muxBoardNrTextBox);
this.tabPage1.Controls.Add(this.muxBoardNrLabel);
this.tabPage1.Controls.Add(this.nameTextBox);
this.tabPage1.Controls.Add(this.nameLabel);
this.tabPage1.Controls.Add(this.classNameLabel);
this.tabPage1.Location = new System.Drawing.Point(4, 25);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(603, 403);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Config";
this.tabPage1.UseVisualStyleBackColor = true;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(208, 101);
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(40, 16);
this.label4.TabIndex = 25;
this.label4.Text = "1 .. 10";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(208, 72);
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(33, 16);
this.label3.TabIndex = 24;
this.label3.Text = "1 .. 4";
//
// groupBox1
//
this.groupBox1.Controls.Add(this.comboBoxCommunicationInterface);
this.groupBox1.Controls.Add(this.label1);
this.groupBox1.Controls.Add(this.rfidPortNrTextBox);
this.groupBox1.Controls.Add(this.rfidSerialPortNrLabel);
this.groupBox1.Location = new System.Drawing.Point(10, 259);
this.groupBox1.Margin = new System.Windows.Forms.Padding(4);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Padding = new System.Windows.Forms.Padding(4);
this.groupBox1.Size = new System.Drawing.Size(552, 68);
this.groupBox1.TabIndex = 23;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "RFID / NFC communication (in case mux. board is not used)";
//
// comboBoxCommunicationInterface
//
this.comboBoxCommunicationInterface.Enabled = false;
this.comboBoxCommunicationInterface.FormattingEnabled = true;
this.comboBoxCommunicationInterface.Items.AddRange(new object[] {
"RFID",
"NFC"});
this.comboBoxCommunicationInterface.Location = new System.Drawing.Point(201, 27);
this.comboBoxCommunicationInterface.Name = "comboBoxCommunicationInterface";
this.comboBoxCommunicationInterface.Size = new System.Drawing.Size(71, 24);
this.comboBoxCommunicationInterface.TabIndex = 9;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(41, 30);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(153, 16);
this.label1.TabIndex = 8;
this.label1.Text = "Communication Interface";
//
// rfidPortNrTextBox
//
this.rfidPortNrTextBox.Enabled = false;
this.rfidPortNrTextBox.Location = new System.Drawing.Point(439, 26);
this.rfidPortNrTextBox.Margin = new System.Windows.Forms.Padding(4);
this.rfidPortNrTextBox.Name = "rfidPortNrTextBox";
this.rfidPortNrTextBox.Size = new System.Drawing.Size(44, 22);
this.rfidPortNrTextBox.TabIndex = 7;
//
// rfidSerialPortNrLabel
//
this.rfidSerialPortNrLabel.AutoSize = true;
this.rfidSerialPortNrLabel.Location = new System.Drawing.Point(321, 30);
this.rfidSerialPortNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.rfidSerialPortNrLabel.Name = "rfidSerialPortNrLabel";
this.rfidSerialPortNrLabel.Size = new System.Drawing.Size(88, 16);
this.rfidSerialPortNrLabel.TabIndex = 6;
this.rfidSerialPortNrLabel.Text = "Serial port nr.:";
//
// optoDataGroupBox
//
this.optoDataGroupBox.Controls.Add(this.tcpipPortLabel);
this.optoDataGroupBox.Controls.Add(this.tcpipPortTextBox);
this.optoDataGroupBox.Controls.Add(this.ipAddressLabel);
this.optoDataGroupBox.Controls.Add(this.ipAddressTextBox);
this.optoDataGroupBox.Controls.Add(this.radioButton1);
this.optoDataGroupBox.Controls.Add(this.radioButton2);
this.optoDataGroupBox.Controls.Add(this.optoSerialPortLabel);
this.optoDataGroupBox.Controls.Add(this.optoSerialPortTextBox);
this.optoDataGroupBox.Location = new System.Drawing.Point(10, 131);
this.optoDataGroupBox.Margin = new System.Windows.Forms.Padding(4);
this.optoDataGroupBox.Name = "optoDataGroupBox";
this.optoDataGroupBox.Padding = new System.Windows.Forms.Padding(4);
this.optoDataGroupBox.Size = new System.Drawing.Size(552, 119);
this.optoDataGroupBox.TabIndex = 18;
this.optoDataGroupBox.TabStop = false;
this.optoDataGroupBox.Text = "Opto-data";
//
// tcpipPortLabel
//
this.tcpipPortLabel.AutoSize = true;
this.tcpipPortLabel.Location = new System.Drawing.Point(41, 87);
this.tcpipPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.tcpipPortLabel.Name = "tcpipPortLabel";
this.tcpipPortLabel.Size = new System.Drawing.Size(54, 16);
this.tcpipPortLabel.TabIndex = 4;
this.tcpipPortLabel.Text = "Port nr..:";
//
// tcpipPortTextBox
//
this.tcpipPortTextBox.Enabled = false;
this.tcpipPortTextBox.Location = new System.Drawing.Point(143, 84);
this.tcpipPortTextBox.Margin = new System.Windows.Forms.Padding(4);
this.tcpipPortTextBox.Name = "tcpipPortTextBox";
this.tcpipPortTextBox.Size = new System.Drawing.Size(51, 22);
this.tcpipPortTextBox.TabIndex = 5;
//
// ipAddressLabel
//
this.ipAddressLabel.AutoSize = true;
this.ipAddressLabel.Location = new System.Drawing.Point(41, 59);
this.ipAddressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.ipAddressLabel.Name = "ipAddressLabel";
this.ipAddressLabel.Size = new System.Drawing.Size(78, 16);
this.ipAddressLabel.TabIndex = 2;
this.ipAddressLabel.Text = "IP address.:";
//
// ipAddressTextBox
//
this.ipAddressTextBox.Enabled = false;
this.ipAddressTextBox.Location = new System.Drawing.Point(143, 55);
this.ipAddressTextBox.Margin = new System.Windows.Forms.Padding(4);
this.ipAddressTextBox.Name = "ipAddressTextBox";
this.ipAddressTextBox.Size = new System.Drawing.Size(129, 22);
this.ipAddressTextBox.TabIndex = 3;
//
// radioButton1
//
this.radioButton1.AutoSize = true;
this.radioButton1.Checked = true;
this.radioButton1.Enabled = false;
this.radioButton1.Location = new System.Drawing.Point(29, 23);
this.radioButton1.Margin = new System.Windows.Forms.Padding(4);
this.radioButton1.Name = "radioButton1";
this.radioButton1.Size = new System.Drawing.Size(99, 20);
this.radioButton1.TabIndex = 0;
this.radioButton1.TabStop = true;
this.radioButton1.Text = "Use TCP/IP";
this.radioButton1.UseVisualStyleBackColor = true;
//
// radioButton2
//
this.radioButton2.AutoSize = true;
this.radioButton2.Enabled = false;
this.radioButton2.Location = new System.Drawing.Point(312, 23);
this.radioButton2.Margin = new System.Windows.Forms.Padding(4);
this.radioButton2.Name = "radioButton2";
this.radioButton2.Size = new System.Drawing.Size(115, 20);
this.radioButton2.TabIndex = 1;
this.radioButton2.Text = "Use serial port";
this.radioButton2.UseVisualStyleBackColor = true;
//
// optoSerialPortLabel
//
this.optoSerialPortLabel.AutoSize = true;
this.optoSerialPortLabel.Location = new System.Drawing.Point(321, 55);
this.optoSerialPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.optoSerialPortLabel.Name = "optoSerialPortLabel";
this.optoSerialPortLabel.Size = new System.Drawing.Size(88, 16);
this.optoSerialPortLabel.TabIndex = 6;
this.optoSerialPortLabel.Text = "Serial port nr.:";
//
// optoSerialPortTextBox
//
this.optoSerialPortTextBox.Enabled = false;
this.optoSerialPortTextBox.Location = new System.Drawing.Point(439, 52);
this.optoSerialPortTextBox.Margin = new System.Windows.Forms.Padding(4);
this.optoSerialPortTextBox.Name = "optoSerialPortTextBox";
this.optoSerialPortTextBox.Size = new System.Drawing.Size(44, 22);
this.optoSerialPortTextBox.TabIndex = 7;
//
// groupTextBox
//
this.groupTextBox.Enabled = false;
this.groupTextBox.Location = new System.Drawing.Point(153, 97);
this.groupTextBox.Margin = new System.Windows.Forms.Padding(4);
this.groupTextBox.Name = "groupTextBox";
this.groupTextBox.Size = new System.Drawing.Size(44, 22);
this.groupTextBox.TabIndex = 22;
//
// groupLabel
//
this.groupLabel.AutoSize = true;
this.groupLabel.Location = new System.Drawing.Point(6, 101);
this.groupLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.groupLabel.Name = "groupLabel";
this.groupLabel.Size = new System.Drawing.Size(54, 16);
this.groupLabel.TabIndex = 21;
this.groupLabel.Text = "Group 2";
//
// muxBoardNrTextBox
//
this.muxBoardNrTextBox.Enabled = false;
this.muxBoardNrTextBox.Location = new System.Drawing.Point(153, 69);
this.muxBoardNrTextBox.Margin = new System.Windows.Forms.Padding(4);
this.muxBoardNrTextBox.Name = "muxBoardNrTextBox";
this.muxBoardNrTextBox.Size = new System.Drawing.Size(44, 22);
this.muxBoardNrTextBox.TabIndex = 20;
//
// muxBoardNrLabel
//
this.muxBoardNrLabel.AutoSize = true;
this.muxBoardNrLabel.Location = new System.Drawing.Point(6, 72);
this.muxBoardNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.muxBoardNrLabel.Name = "muxBoardNrLabel";
this.muxBoardNrLabel.Size = new System.Drawing.Size(131, 16);
this.muxBoardNrLabel.TabIndex = 19;
this.muxBoardNrLabel.Text = "Group 1 (mux. board)";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(153, 40);
this.nameTextBox.Margin = new System.Windows.Forms.Padding(4);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(160, 22);
this.nameTextBox.TabIndex = 17;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(6, 44);
this.nameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(44, 16);
this.nameLabel.TabIndex = 16;
this.nameLabel.Text = "Name";
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(149, 11);
this.classNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(78, 16);
this.classNameLabel.TabIndex = 15;
this.classNameLabel.Text = "ClassName";
//
// tabPage2
//
this.tabPage2.Location = new System.Drawing.Point(4, 25);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(603, 403);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Test";
this.tabPage2.UseVisualStyleBackColor = true;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.headPortNrTextBox);
this.groupBox2.Controls.Add(this.label2);
this.groupBox2.Location = new System.Drawing.Point(10, 335);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(552, 50);
this.groupBox2.TabIndex = 26;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Head Communication";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(321, 18);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(88, 16);
this.label2.TabIndex = 7;
this.label2.Text = "Serial port nr.:";
//
// headPortNrTextBox
//
this.headPortNrTextBox.Enabled = false;
this.headPortNrTextBox.Location = new System.Drawing.Point(439, 15);
this.headPortNrTextBox.Margin = new System.Windows.Forms.Padding(4);
this.headPortNrTextBox.Name = "headPortNrTextBox";
this.headPortNrTextBox.Size = new System.Drawing.Size(44, 22);
this.headPortNrTextBox.TabIndex = 8;
//
// IperlHeadCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.tabControl1);
this.Margin = new System.Windows.Forms.Padding(4);
this.Name = "GenesisCfgCtrl";
this.Size = new System.Drawing.Size(617, 438);
this.Load += new System.EventHandler(this.WaterMeterCfgCtrl_Load);
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage1.PerformLayout();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.optoDataGroupBox.ResumeLayout(false);
this.optoDataGroupBox.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.ResumeLayout(false);
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.headPortNrTextBox = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.comboBoxCommunicationInterface = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.rfidPortNrTextBox = new System.Windows.Forms.TextBox();
this.rfidSerialPortNrLabel = new System.Windows.Forms.Label();
this.optoDataGroupBox = new System.Windows.Forms.GroupBox();
this.checkBox_EnableShowChanels = new System.Windows.Forms.CheckBox();
this.tBBeginDataFlush = new System.Windows.Forms.TextBox();
this.labelFlush = new System.Windows.Forms.Label();
this.tcpipPortLabel = new System.Windows.Forms.Label();
this.tcpipPortTextBox = new System.Windows.Forms.TextBox();
this.ipAddressLabel = new System.Windows.Forms.Label();
this.ipAddressTextBox = new System.Windows.Forms.TextBox();
this.radioButton1 = new System.Windows.Forms.RadioButton();
this.radioButton2 = new System.Windows.Forms.RadioButton();
this.optoSerialPortLabel = new System.Windows.Forms.Label();
this.optoSerialPortTextBox = new System.Windows.Forms.TextBox();
this.groupTextBox = new System.Windows.Forms.TextBox();
this.groupLabel = new System.Windows.Forms.Label();
this.muxBoardNrTextBox = new System.Windows.Forms.TextBox();
this.muxBoardNrLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.classNameLabel = new System.Windows.Forms.Label();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox1.SuspendLayout();
this.optoDataGroupBox.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Location = new System.Drawing.Point(3, 4);
this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(687, 540);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.groupBox2);
this.tabPage1.Controls.Add(this.label4);
this.tabPage1.Controls.Add(this.label3);
this.tabPage1.Controls.Add(this.groupBox1);
this.tabPage1.Controls.Add(this.optoDataGroupBox);
this.tabPage1.Controls.Add(this.groupTextBox);
this.tabPage1.Controls.Add(this.groupLabel);
this.tabPage1.Controls.Add(this.muxBoardNrTextBox);
this.tabPage1.Controls.Add(this.muxBoardNrLabel);
this.tabPage1.Controls.Add(this.nameTextBox);
this.tabPage1.Controls.Add(this.nameLabel);
this.tabPage1.Controls.Add(this.classNameLabel);
this.tabPage1.Location = new System.Drawing.Point(4, 29);
this.tabPage1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.tabPage1.Size = new System.Drawing.Size(679, 507);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Config";
this.tabPage1.UseVisualStyleBackColor = true;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.headPortNrTextBox);
this.groupBox2.Controls.Add(this.label2);
this.groupBox2.Location = new System.Drawing.Point(11, 450);
this.groupBox2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.groupBox2.Size = new System.Drawing.Size(621, 52);
this.groupBox2.TabIndex = 26;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Head Communication";
//
// headPortNrTextBox
//
this.headPortNrTextBox.Enabled = false;
this.headPortNrTextBox.Location = new System.Drawing.Point(494, 19);
this.headPortNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.headPortNrTextBox.Name = "headPortNrTextBox";
this.headPortNrTextBox.Size = new System.Drawing.Size(49, 26);
this.headPortNrTextBox.TabIndex = 8;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(361, 22);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(107, 20);
this.label2.TabIndex = 7;
this.label2.Text = "Serial port nr.:";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(234, 126);
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(52, 20);
this.label4.TabIndex = 25;
this.label4.Text = "1 .. 10";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(234, 90);
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(43, 20);
this.label3.TabIndex = 24;
this.label3.Text = "1 .. 4";
//
// groupBox1
//
this.groupBox1.Controls.Add(this.comboBoxCommunicationInterface);
this.groupBox1.Controls.Add(this.label1);
this.groupBox1.Controls.Add(this.rfidPortNrTextBox);
this.groupBox1.Controls.Add(this.rfidSerialPortNrLabel);
this.groupBox1.Location = new System.Drawing.Point(11, 363);
this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupBox1.Size = new System.Drawing.Size(621, 85);
this.groupBox1.TabIndex = 23;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "RFID / NFC communication (in case mux. board is not used)";
//
// comboBoxCommunicationInterface
//
this.comboBoxCommunicationInterface.Enabled = false;
this.comboBoxCommunicationInterface.FormattingEnabled = true;
this.comboBoxCommunicationInterface.Items.AddRange(new object[] { "RFID", "NFC" });
this.comboBoxCommunicationInterface.Location = new System.Drawing.Point(226, 34);
this.comboBoxCommunicationInterface.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.comboBoxCommunicationInterface.Name = "comboBoxCommunicationInterface";
this.comboBoxCommunicationInterface.Size = new System.Drawing.Size(79, 28);
this.comboBoxCommunicationInterface.TabIndex = 9;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(46, 38);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(187, 20);
this.label1.TabIndex = 8;
this.label1.Text = "Communication Interface";
//
// rfidPortNrTextBox
//
this.rfidPortNrTextBox.Enabled = false;
this.rfidPortNrTextBox.Location = new System.Drawing.Point(494, 32);
this.rfidPortNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.rfidPortNrTextBox.Name = "rfidPortNrTextBox";
this.rfidPortNrTextBox.Size = new System.Drawing.Size(49, 26);
this.rfidPortNrTextBox.TabIndex = 7;
//
// rfidSerialPortNrLabel
//
this.rfidSerialPortNrLabel.AutoSize = true;
this.rfidSerialPortNrLabel.Location = new System.Drawing.Point(361, 38);
this.rfidSerialPortNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.rfidSerialPortNrLabel.Name = "rfidSerialPortNrLabel";
this.rfidSerialPortNrLabel.Size = new System.Drawing.Size(107, 20);
this.rfidSerialPortNrLabel.TabIndex = 6;
this.rfidSerialPortNrLabel.Text = "Serial port nr.:";
//
// optoDataGroupBox
//
this.optoDataGroupBox.Controls.Add(this.checkBox_EnableShowChanels);
this.optoDataGroupBox.Controls.Add(this.tBBeginDataFlush);
this.optoDataGroupBox.Controls.Add(this.labelFlush);
this.optoDataGroupBox.Controls.Add(this.tcpipPortLabel);
this.optoDataGroupBox.Controls.Add(this.tcpipPortTextBox);
this.optoDataGroupBox.Controls.Add(this.ipAddressLabel);
this.optoDataGroupBox.Controls.Add(this.ipAddressTextBox);
this.optoDataGroupBox.Controls.Add(this.radioButton1);
this.optoDataGroupBox.Controls.Add(this.radioButton2);
this.optoDataGroupBox.Controls.Add(this.optoSerialPortLabel);
this.optoDataGroupBox.Controls.Add(this.optoSerialPortTextBox);
this.optoDataGroupBox.Location = new System.Drawing.Point(11, 164);
this.optoDataGroupBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.optoDataGroupBox.Name = "optoDataGroupBox";
this.optoDataGroupBox.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.optoDataGroupBox.Size = new System.Drawing.Size(621, 189);
this.optoDataGroupBox.TabIndex = 18;
this.optoDataGroupBox.TabStop = false;
this.optoDataGroupBox.Text = "Opto-data";
//
// checkBox_EnableShowChanels
//
this.checkBox_EnableShowChanels.Checked = true;
this.checkBox_EnableShowChanels.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox_EnableShowChanels.Enabled = false;
this.checkBox_EnableShowChanels.Location = new System.Drawing.Point(351, 147);
this.checkBox_EnableShowChanels.Name = "checkBox_EnableShowChanels";
this.checkBox_EnableShowChanels.Size = new System.Drawing.Size(238, 24);
this.checkBox_EnableShowChanels.TabIndex = 10;
this.checkBox_EnableShowChanels.Text = "Enable Show Channels";
this.checkBox_EnableShowChanels.UseVisualStyleBackColor = true;
//
// tBBeginDataFlush
//
this.tBBeginDataFlush.Enabled = false;
this.tBBeginDataFlush.Location = new System.Drawing.Point(474, 105);
this.tBBeginDataFlush.MaxLength = 8;
this.tBBeginDataFlush.Name = "tBBeginDataFlush";
this.tBBeginDataFlush.Size = new System.Drawing.Size(69, 26);
this.tBBeginDataFlush.TabIndex = 9;
this.tBBeginDataFlush.Text = "2000";
this.tBBeginDataFlush.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
//
// labelFlush
//
this.labelFlush.Location = new System.Drawing.Point(328, 109);
this.labelFlush.Name = "labelFlush";
this.labelFlush.Size = new System.Drawing.Size(140, 22);
this.labelFlush.TabIndex = 8;
this.labelFlush.Text = "Begin Data Flush:";
//
// tcpipPortLabel
//
this.tcpipPortLabel.AutoSize = true;
this.tcpipPortLabel.Location = new System.Drawing.Point(46, 109);
this.tcpipPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.tcpipPortLabel.Name = "tcpipPortLabel";
this.tcpipPortLabel.Size = new System.Drawing.Size(68, 20);
this.tcpipPortLabel.TabIndex = 4;
this.tcpipPortLabel.Text = "Port nr..:";
//
// tcpipPortTextBox
//
this.tcpipPortTextBox.Enabled = false;
this.tcpipPortTextBox.Location = new System.Drawing.Point(161, 105);
this.tcpipPortTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.tcpipPortTextBox.Name = "tcpipPortTextBox";
this.tcpipPortTextBox.Size = new System.Drawing.Size(57, 26);
this.tcpipPortTextBox.TabIndex = 5;
//
// ipAddressLabel
//
this.ipAddressLabel.AutoSize = true;
this.ipAddressLabel.Location = new System.Drawing.Point(46, 74);
this.ipAddressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.ipAddressLabel.Name = "ipAddressLabel";
this.ipAddressLabel.Size = new System.Drawing.Size(93, 20);
this.ipAddressLabel.TabIndex = 2;
this.ipAddressLabel.Text = "IP address.:";
//
// ipAddressTextBox
//
this.ipAddressTextBox.Enabled = false;
this.ipAddressTextBox.Location = new System.Drawing.Point(161, 69);
this.ipAddressTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.ipAddressTextBox.Name = "ipAddressTextBox";
this.ipAddressTextBox.Size = new System.Drawing.Size(145, 26);
this.ipAddressTextBox.TabIndex = 3;
//
// radioButton1
//
this.radioButton1.AutoSize = true;
this.radioButton1.Checked = true;
this.radioButton1.Enabled = false;
this.radioButton1.Location = new System.Drawing.Point(33, 29);
this.radioButton1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.radioButton1.Name = "radioButton1";
this.radioButton1.Size = new System.Drawing.Size(109, 24);
this.radioButton1.TabIndex = 0;
this.radioButton1.TabStop = true;
this.radioButton1.Text = "Use TCP/IP";
this.radioButton1.UseVisualStyleBackColor = true;
//
// radioButton2
//
this.radioButton2.AutoSize = true;
this.radioButton2.Enabled = false;
this.radioButton2.Location = new System.Drawing.Point(351, 29);
this.radioButton2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.radioButton2.Name = "radioButton2";
this.radioButton2.Size = new System.Drawing.Size(129, 24);
this.radioButton2.TabIndex = 1;
this.radioButton2.Text = "Use serial port";
this.radioButton2.UseVisualStyleBackColor = true;
//
// optoSerialPortLabel
//
this.optoSerialPortLabel.AutoSize = true;
this.optoSerialPortLabel.Location = new System.Drawing.Point(361, 69);
this.optoSerialPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.optoSerialPortLabel.Name = "optoSerialPortLabel";
this.optoSerialPortLabel.Size = new System.Drawing.Size(107, 20);
this.optoSerialPortLabel.TabIndex = 6;
this.optoSerialPortLabel.Text = "Serial port nr.:";
//
// optoSerialPortTextBox
//
this.optoSerialPortTextBox.Enabled = false;
this.optoSerialPortTextBox.Location = new System.Drawing.Point(494, 65);
this.optoSerialPortTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.optoSerialPortTextBox.Name = "optoSerialPortTextBox";
this.optoSerialPortTextBox.Size = new System.Drawing.Size(49, 26);
this.optoSerialPortTextBox.TabIndex = 7;
//
// groupTextBox
//
this.groupTextBox.Enabled = false;
this.groupTextBox.Location = new System.Drawing.Point(172, 121);
this.groupTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupTextBox.Name = "groupTextBox";
this.groupTextBox.Size = new System.Drawing.Size(49, 26);
this.groupTextBox.TabIndex = 22;
//
// groupLabel
//
this.groupLabel.AutoSize = true;
this.groupLabel.Location = new System.Drawing.Point(7, 126);
this.groupLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.groupLabel.Name = "groupLabel";
this.groupLabel.Size = new System.Drawing.Size(67, 20);
this.groupLabel.TabIndex = 21;
this.groupLabel.Text = "Group 2";
//
// muxBoardNrTextBox
//
this.muxBoardNrTextBox.Enabled = false;
this.muxBoardNrTextBox.Location = new System.Drawing.Point(172, 86);
this.muxBoardNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.muxBoardNrTextBox.Name = "muxBoardNrTextBox";
this.muxBoardNrTextBox.Size = new System.Drawing.Size(49, 26);
this.muxBoardNrTextBox.TabIndex = 20;
//
// muxBoardNrLabel
//
this.muxBoardNrLabel.AutoSize = true;
this.muxBoardNrLabel.Location = new System.Drawing.Point(7, 90);
this.muxBoardNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.muxBoardNrLabel.Name = "muxBoardNrLabel";
this.muxBoardNrLabel.Size = new System.Drawing.Size(159, 20);
this.muxBoardNrLabel.TabIndex = 19;
this.muxBoardNrLabel.Text = "Group 1 (mux. board)";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(172, 50);
this.nameTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(180, 26);
this.nameTextBox.TabIndex = 17;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(7, 55);
this.nameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(51, 20);
this.nameLabel.TabIndex = 16;
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.TabPage tabPage1;

View File

@ -110,6 +110,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
{
Flush = 0,
ProcessAndSave,
CalculateStoredData,
}
public enum CommunicationInterface

View File

@ -13,46 +13,29 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
{
public class CliRunner
{
//static readonly ILog log = LogManager.GetLogger(typeof(CliRunner));
private readonly ILog log;
private List<Task> taskPool = new List<Task>();
private long startTime;
private long incommingTime;
public List<Task> TaskPool { get { return taskPool; } }
public void AddTask(Task task) { taskPool.Add(task); }
public void WaitAll() { Task.WaitAll(taskPool.ToArray()); }
public void Clear() { taskPool.Clear(); }
public void CancelAll() { Task.WhenAll(taskPool).ContinueWith(t => { }); }
private readonly List<CliTaskInfo> taskPool = new List<CliTaskInfo>();
private long startTimeMs;
private long incommingTimeMs;
public void StartAll()
public List<CliTaskInfo> TaskPool
{
taskPool.ForEach(t => t.Start());
}
public bool AreTasksDone()
{
return taskPool.All(task => task.IsCompleted);
get { return taskPool; }
}
public long StartTime
{
get => startTime;
get { return startTimeMs; }
}
public long IncommingTime
{
get => incommingTime;
get { return incommingTimeMs; }
}
public bool TimeOutReceived(long timeout)
{
return (DateTime.Now.Ticks - startTime) > timeout;
}
public CliRunner(bool isCliLogging)
{
startTime = DateTime.Now.Ticks;
ResetStartTime();
if (isCliLogging)
{
@ -60,88 +43,180 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
{
log = new ScopedLoggerFactory().CreateLogger<CliRunner>(
@"C:\TBF\Logs\CliRunner.txt",
10, // maxFileSizeMB
7, // maxBackups
10,
7,
log4net.Core.Level.Debug,
true, // zipRolledFiles
true, // singleZipPerDay
TimeSpan.FromMinutes(2) // zipScanInterval
true,
true,
TimeSpan.FromMinutes(2)
);
}
catch (Exception ex)
{
// Fallback to console or handle gracefully
Console.WriteLine($"Failed to initialize logger: {ex.Message}");
}
}
}
/// <summary>
///
/// </summary>
/// <param name="fileName"></param>
/// <param name="args"></param>
/// <typeparam name="T"></typeparam>
public void AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg)
public void ResetStartTime()
{
var task = SendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
taskPool.Add(task);
startTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
public void AddSendAsync(string fileName, string args)
{
var task = SendAsync(fileName, args);
taskPool.Add(task);
}
public async Task<string> SendAsync(string fileName, string args, CancellationToken ct = default)
{
var psi = new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
process.Start();
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
try
public void Clear()
{
foreach (var item in taskPool)
{
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
}
catch (OperationCanceledException)
{
if (!process.HasExited)
try
{
item?.Cts?.Dispose();
}
catch
{
process.Kill();
}
throw;
try
{
if (item?.Process != null)
{
item.Process.Dispose();
}
}
catch
{
}
}
//comming answer from serial port - good place for time stamp
incommingTime = DateTime.Now.Ticks;
string allOutput = (stdOutTask.Result ?? "") + (stdErrTask.Result ?? "");
log?.Debug(allOutput);
return allOutput;
taskPool.Clear();
}
public void AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new()
public void WaitAll()
{
var task = RunAndCaptureJsonAsync<T>(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
taskPool.Add(task);
var tasks = taskPool
.Where(t => t != null && t.Task != null)
.Select(t => t.Task)
.ToArray();
if (tasks.Length == 0)
return;
try
{
Task.WaitAll(tasks);
}
catch (AggregateException)
{
RefreshTaskStates();
}
}
public async Task<T> RunAndCaptureJsonAsync<T>(string fileName, string args, CancellationToken ct = default) where T : new()
public bool AreTasksDone()
{
RefreshTaskStates();
return taskPool.Count > 0 && taskPool.All(t => t.State != CliTaskState.Running);
}
public bool TimeOutReceived(long timeoutMs)
{
long nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
return (nowMs - startTimeMs) > timeoutMs;
}
public void RefreshTaskStates()
{
foreach (var item in taskPool)
{
if (item == null || item.Task == null)
continue;
if (item.State == CliTaskState.TimedOut)
continue;
if (!item.Task.IsCompleted)
{
item.State = CliTaskState.Running;
}
else if (item.Task.IsCanceled)
{
item.State = CliTaskState.Canceled;
}
else if (item.Task.IsFaulted)
{
item.State = CliTaskState.Faulted;
}
else
{
item.State = CliTaskState.Completed;
}
}
}
public void CancelUndoneTasksAsTimedOut()
{
foreach (var item in taskPool)
{
if (item == null || item.Task == null)
continue;
if (item.Task.IsCompleted)
{
if (item.Task.IsCanceled)
item.State = CliTaskState.Canceled;
else if (item.Task.IsFaulted)
item.State = CliTaskState.Faulted;
else
item.State = CliTaskState.Completed;
continue;
}
item.State = CliTaskState.TimedOut;
try
{
item.Cts?.Cancel();
}
catch (Exception ex)
{
log?.Warn("Cancel token failed", ex);
}
try
{
if (item.Process != null && !item.Process.HasExited)
{
item.Process.Kill();
}
}
catch (Exception ex)
{
log?.Warn("Kill process failed", ex);
}
}
}
public void AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg)
{
AddSendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
}
public void AddSendAsync(string fileName, string args)
{
ResetStartTime();
var cts = new CancellationTokenSource();
var info = new CliTaskInfo
{
Cts = cts,
Name = "SendAsync"
};
var task = SendAsync(fileName, args, info, cts.Token);
info.Task = task;
taskPool.Add(info);
}
public async Task<string> SendAsync(string fileName, string args, CliTaskInfo info, CancellationToken ct = default)
{
var psi = new ProcessStartInfo
{
@ -153,80 +228,210 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
CreateNoWindow = true
};
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
process.Start();
using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
{
info.Process = process;
var stdoutTask = process.StandardOutput.ReadToEndAsync();
var stderrTask = process.StandardError.ReadToEndAsync();
try
{
process.Start();
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct));
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
string allOutput = (stdoutTask.Result ?? "") + (stderrTask.Result ?? "");
log?.Debug(allOutput);
try
{
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
}
catch (OperationCanceledException)
{
if (info.State != CliTaskState.TimedOut)
info.State = CliTaskState.Canceled;
string json = ExtractJson(allOutput);
if (TryJsonStringDeserialize(json, out T result)) return result;
return default;
if (!process.HasExited)
{
process.Kill();
}
throw;
}
incommingTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string allOutput = (stdOutTask.Result ?? "") + (stdErrTask.Result ?? "");
log?.Debug(allOutput);
info.State = CliTaskState.Completed;
return allOutput;
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
info.State = CliTaskState.Faulted;
log?.Error("SendAsync failed", ex);
throw;
}
finally
{
info.Process = null;
}
}
}
public bool TryJsonStringDeserialize<T>(string json, out T runAndCaptureJsonAsync) where T : new()
public void AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new()
{
AddRunAndCaptureJsonAsync<T>(
data.SerialPortCmdClientPath,
data.DefaultArgSettings(eMeterArg));
}
public void AddRunAndCaptureJsonAsync<T>(string fileName, string args) where T : new()
{
ResetStartTime();
var cts = new CancellationTokenSource();
var info = new CliTaskInfo
{
Cts = cts,
Name = $"RunAndCaptureJsonAsync<{typeof(T).Name}>"
};
var task = RunAndCaptureJsonAsync<T>(fileName, args, info, cts.Token);
info.Task = task;
taskPool.Add(info);
}
public async Task<T> RunAndCaptureJsonAsync<T>(string fileName, string args, CliTaskInfo info, CancellationToken ct = default) where T : new()
{
var psi = new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
{
info.Process = process;
try
{
process.Start();
var stdoutTask = process.StandardOutput.ReadToEndAsync();
var stderrTask = process.StandardError.ReadToEndAsync();
try
{
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct));
}
catch (OperationCanceledException)
{
if (info.State != CliTaskState.TimedOut)
info.State = CliTaskState.Canceled;
if (!process.HasExited)
{
process.Kill();
}
throw;
}
incommingTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string allOutput = (stdoutTask.Result ?? "") + (stderrTask.Result ?? "");
log?.Debug(allOutput);
string json = ExtractJson(allOutput);
T result;
if (TryJsonStringDeserialize(json, out result))
{
info.State = CliTaskState.Completed;
return result;
}
info.State = CliTaskState.Completed;
return default(T);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
info.State = CliTaskState.Faulted;
log?.Error("RunAndCaptureJsonAsync failed", ex);
throw;
}
finally
{
info.Process = null;
}
}
}
public bool TryJsonStringDeserialize<T>(string json, out T value) where T : new()
{
if (json != null)
{
try
{
runAndCaptureJsonAsync = JsonConvert.DeserializeObject<T>(json);
value = JsonConvert.DeserializeObject<T>(json);
return true;
}
catch (Exception ex)
{
log.Debug(ex.Message);
runAndCaptureJsonAsync = TryConvert<T>(json);
log?.Debug(ex.Message);
value = TryConvert<T>(json);
return true;
}
}
runAndCaptureJsonAsync = default;
value = default(T);
return false;
}
private static T TryConvert<T>(string json) where T : new()
{
T obj = new T();
T obj = new T();
try
try
{
JObject jObject = JObject.Parse(json);
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
JObject jObject = JObject.Parse(json);
if (!prop.CanWrite)
continue;
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
JToken token;
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
{
if (!prop.CanWrite) continue;
JToken token;
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
try
{
object value = token.ToObject(prop.PropertyType);
prop.SetValue(obj, value);
}
catch
{
try
{
object value = token.ToObject(prop.PropertyType);
prop.SetValue(obj, value);
}
catch
{
// leave default if conversion fails
}
}
// else → keep default value
}
}
catch (Exception ex)
{
Console.WriteLine($"TryConvert failed: {ex.Message}");
}
}
catch (Exception ex)
{
Console.WriteLine($"TryConvert failed: {ex.Message}");
}
return obj;
return obj;
}
public string ExtractJson(string text)
@ -241,6 +446,20 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
return null;
}
internal void AddTaskForTest(Task task, string name = "TestTask", CancellationTokenSource cts = null)
{
taskPool.Add(new CliTaskInfo
{
Task = task,
Cts = cts,
Name = name,
State = task.IsCompleted
? (task.IsCanceled ? CliTaskState.Canceled :
task.IsFaulted ? CliTaskState.Faulted :
CliTaskState.Completed)
: CliTaskState.Running
});
}
}
}

View File

@ -0,0 +1,25 @@
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
{
public class CliTaskInfo
{
public Task Task { get; set; }
public CancellationTokenSource Cts { get; set; }
public Process Process { get; set; }
public CliTaskState State { get; set; } = CliTaskState.Running;
public string Name { get; set; }
public bool UseResult
{
get
{
return State == CliTaskState.Completed
&& Task != null
&& Task.Status == TaskStatus.RanToCompletion;
}
}
}
}

View File

@ -0,0 +1,13 @@
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
{
public enum CliTaskState
{
Running,
Completed,
TimedOut,
Canceled,
Faulted
}
}

View File

@ -336,10 +336,12 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
/// for debug purposes what time will consume answer
/// </summary>
private long startTimeInMilis = -1, fullTimeInMilis = -1;
/// 30 seconds
private static long SafetyTimeOut = 30 * 1000;
private long incommingTime = -1;
private bool _lastOpTimedOut;
/// 30 seconds
public long DeltaTime { get{return fullTimeInMilis;}}
public long IncommingTime { get{return incommingTime;}}
@ -356,108 +358,146 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
if (_currentOp == CurrentPoseidonOp.SendStartDataStream)
{
CliRunner.Clear();
_lastOpTimedOut = false;
CliRunner.AddSendAsync(serialPort, SerialPortData.EMeterArg.AllParams);
return Event.Busy;
_currentOp = CurrentPoseidonOp.SendStartDataStream_Runing;
return Event.Busy;
}
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Runing)
{
if (CliRunner.AreTasksDone()
|| CliRunner.TimeOutReceived(SafetyTimeOut))
if (CliRunner.AreTasksDone())
{
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
}
return Event.Busy;
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
{
_lastOpTimedOut = true;
CliRunner.CancelUndoneTasksAsTimedOut();
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
}
return Event.Busy;
}
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Done)
{
var firstTask = CliRunner.TaskPool.FindLast(t => t is Task<string>);
var firstTaskInfo = CliRunner.TaskPool
.FindLast(t => t.Task is Task<string> && t.UseResult);
if (firstTask != null && firstTask is Task<string>)
if (firstTaskInfo != null)
{
string data = null;
data = (firstTask as Task<string>).Result;
var task = (Task<string>)firstTaskInfo.Task;
string data = task.Result;
if (data != null && TryGetDeviceId(data, out wmSerialNr))
if (!string.IsNullOrEmpty(data))
{
TryGetDeviceId(data, out wmSerialNr);
}
}
else if (_lastOpTimedOut)
{
log.Warn(
$"PoseidonReader {Name}: SendStartDataStream timed out, no completed result will be used.");
}
_currentOp = CurrentPoseidonOp.Done;
CliRunner.Clear();
_currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
return Event.Done;
}
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
|| _currentOp == CurrentPoseidonOp.ReadDataStream_End)
{
startTimeInMilis = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
startTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
incommingTime = -1;
_isReadingStart = (_currentOp == CurrentPoseidonOp.ReadDataStream_Start);
CliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
CliRunner.Clear();
_lastOpTimedOut = false;
CliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
SerialPortData.EMeterArg.AllParams);
_currentOp = CurrentPoseidonOp.ReadDatastream_Running;
return Event.Busy;
}else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
}
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
{
if (CliRunner.AreTasksDone()
|| CliRunner.TimeOutReceived(SafetyTimeOut))
if (CliRunner.AreTasksDone())
{
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
//set time stamp - end of reading
incommingTime = CliRunner.IncommingTime;
}
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
{
_lastOpTimedOut = true;
CliRunner.CancelUndoneTasksAsTimedOut();
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
incommingTime = CliRunner.IncommingTime;
}
return Event.Busy;
}
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Done)
{
fullTimeInMilis = (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - startTimeInMilis;
log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis} ");
JsonDataFromPoseidon data = null;
var first = CliRunner.TaskPool.FindLast(t => t is Task<JsonDataFromPoseidon>);
if (first != null && first is Task<JsonDataFromPoseidon>)
{
data = (first as Task<JsonDataFromPoseidon>).Result;
fullTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTimeInMilis;
log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis}");
//GET serial number
JsonDataFromPoseidon data = null;
var firstTaskInfo = CliRunner.TaskPool
.FindLast(t => t.Task is Task<JsonDataFromPoseidon> && t.UseResult);
if (firstTaskInfo != null)
{
var task = (Task<JsonDataFromPoseidon>)firstTaskInfo.Task;
data = task.Result;
}
else
{
if (_lastOpTimedOut)
{
log.Warn($"PoseidonReader {Name}: ReadDatastream timed out, no completed result available.");
}
else
{
log.Warn("No completed JsonDataFromPoseidon task available.");
}
}
if (data != null)
{
if (string.IsNullOrEmpty(wmSerialNr))
{
try
{
wmSerialNr = data?.DeviceId ?? wmSerialNr;
wmSerialNr = data.DeviceId ?? wmSerialNr;
}
catch (Exception e)
{
log.Error("Serial Nr - parse error!");
log.Error("Serial Nr - parse error!", e);
}
}
//GET volume
if (data != null && Double.TryParse(data.Reading, out double Volume))
double volume;
if (Double.TryParse(data.Reading, out volume))
{
double VolumeLi = Units.ConvertFrom(Unit.USgal, Volume);
//double VolumeM3 = Units.ConvertTo(Unit.m3, VolumeLi);
if (_isReadingStart)
{
//volume
beginWMState = VolumeLi;
}
else
{
//volume
endWMState = VolumeLi;
}
}
double volumeLi = Units.ConvertFrom(Unit.USgal, volume);
if (_isReadingStart)
beginWMState = volumeLi;
else
endWMState = volumeLi;
}
}
//Finish reading and loop
_currentOp = CurrentPoseidonOp.Done;
CliRunner.Clear();
_currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
return Event.Done;
}
return Event.None;
return Event.None;
}
/// <summary>Stop this operation</summary>
public void Stop()
{

View File

@ -1276,22 +1276,49 @@ namespace TBF.Rig.Sequences
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[i] != null &&
!BatchRslts.Batch.WaterMeters[i].Disabled)
BatchRslts.Batch.WaterMeters[i] != null)
{
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];
if (!wm.Compound() && !wm.HeatMeter())
{
/// If this is a single meter
Results.Entities.MeterTestRslt mtrRslt = ProcessData.BatchRslts.GetMeterTestRslt(tstRslt.Name(), i, CompoundMeterId.Single);
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;
}
if (mtrRslt != null)
if (mtrRslt != null)
{
bool isCamera = (mtrRslt.RegReaderType == (int)RegisterReaderType.Camera);

View File

@ -13,6 +13,7 @@ using TBF.Rig.GenericDevices;
using TBF.Boxes;
using TBF.Resources;
using TBF.Rig.TestMethods.GenesisCommunication.GenesisHead;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
using TBF.UiBridge;
namespace TBF.Rig.TestMethods.FlyingStart
@ -329,6 +330,9 @@ namespace TBF.Rig.TestMethods.FlyingStart
temperature_set:
//TODO genesis - check with genesis switch
if (atleastOneGenesis)
{
@ -379,18 +383,26 @@ namespace TBF.Rig.TestMethods.FlyingStart
IList<IOperation> cameraMeasurementOps = new List<IOperation>();
foreach (var camera in cameras) cameraMeasurementOps.Add(camera.MeasurementOp());
foreach (var rr in sensPath.RegisterReaders)
{
if (rr is IRegReaderDatastream)
(rr as IRegReaderDatastream).TestIsGoingToStartSoon(test, repetitionNr);
if (rr is GenesisHead) // Genesis - CORDONEL head
(rr as GenesisHead).StartRead(test.Name, BatchRslts.Batch.BatchNr, repetitionNr);
}
/// Prepare datastream read operations
IList<IOperation> readDatastreamOps = new List<IOperation>();
if (sensPath != null && sensPath.RegisterReaders != null)
{
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
(rr as GenesisHead).StartRead(test.Name, BatchRslts.Batch.BatchNr, repetitionNr);
}
}
//------------------------------------------------
//------------------------------------------------
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))
.AddOperation(checkUiOp)
.AddOperations(readTempPressOps)
.AddOperations(readDatastreamOps)
.AddOperations(cameraMeasurementOps)
.AddOperation(cBrd.FlyingStartStopTestOp(test, test.QfromM3ph(), test.QtoM3ph(), totalPulses))
.AddOperation(processDataLoggingOp)
@ -454,6 +467,21 @@ namespace TBF.Rig.TestMethods.FlyingStart
/// Measurement loop end
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
if (atleastOneGenesis)

View File

@ -8,11 +8,14 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using Results.Entities;
using TBF.Boxes;
using TBF.Resources;
using TBF.Rig;
using TBF.Rig.GenericDevices;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
using TBF.Rig.RegisterReaders.PoseidonReader;
using TBF.Rig.RegisterReaders.PoseidonReader.implementations;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
@ -607,6 +610,11 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
}
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
StopRecordingStatistics();
@ -968,6 +976,7 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
}
else
{
bool bUpgradeCountOfMeters = false;
///
/// Single meters
///
@ -982,6 +991,7 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
TestMethods.iPerlCommunication.iPerlHead.IperlHead iPerl = regReader as TestMethods.iPerlCommunication.iPerlHead.IperlHead;
GenericDevices.IRegReaderLiveCamera cameraRoi = regReader as GenericDevices.IRegReaderLiveCamera;
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)
{
@ -1070,6 +1080,23 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
var calError = Formulas.ErrorFromVolumes(meterRslt.VolumeMeter, meterRslt.VolumeRef);
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)
{
@ -1254,7 +1281,15 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
#endif
}
}
if (bUpgradeCountOfMeters)
{
//TODO remove test!!
//BatchRslts.WMPositionsCount = BatchRslts.WMPositionsCount + 1;
}
}
tstRslt.Components = Results.Entities.Components.UpdateList(BatchRslts.ComponentsList,
new Results.Entities.Components((BenchInfo != null) ? BenchInfo.TestBenchId : 1,
@ -1313,6 +1348,98 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
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,
Compound.CombinedTestParams compoundTestParams,

View File

@ -110,7 +110,7 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
ltrPerPulse = nominalFlow / (3.6 * nominalFreq); /// Nominal freq. is sum of particular nominal frquencies
log.FatalFormat("{0} initialized: {1} nom.flow={2} m3/h nom.freq={3} Hz bitfield=0x{4:X2}",
Name, nominalFlow, nominalFreq, flowMetersBitfield);
Name, true, nominalFlow, nominalFreq, flowMetersBitfield);
}
public double ReadFlow()

View File

@ -3,6 +3,7 @@
///
using System;
using System.Collections.Generic;
using Common;
using log4net;
using Config.Entities;
using TBF.Rig.ControlBoard.Uni;
@ -206,6 +207,11 @@ namespace TBF.Rig.Uni.RegValve
public Event Run()
{
log.DebugFormat("Op.Run() opState={0}", opState);
if (this.uniCB?.DebugLevel == DebugMode.Simulate)
{
return Event.FlowReached;
}
if (StateMachine.Time > expireTime) return Event.RegulValveTimeOut;

View File

@ -1494,6 +1494,8 @@
<DependentUpon>RRCfgCtrl.cs</DependentUpon>
</Compile>
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunner.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliTaskInfo.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliTaskState.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\Factory.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\JsonDataFromPoseidon.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonCfg.cs" />

View File

@ -1,8 +1,8 @@
using System;
using System.IO;
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;
@ -11,16 +11,52 @@ using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class GenesisReaderTests
{
[TestMethod]
public void RealInput_ShouldCalculate_StartEndVolumes_AndTimes()
private static GenesisCfg CreateCfg()
{
var reader = new GenesisSmartReader();
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();
@ -30,12 +66,35 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
int firstValidIx = -1;
int lastValidIx = -1;
foreach (var line in realInputLines)
Console.WriteLine("=== BEGIN INPUT PARSING ===");
for (int i = 0; i < realInputLines.Length; i++)
{
bool blockCompleted;
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out blockCompleted);
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)
@ -43,70 +102,89 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
lastValidIx = optoDataCount - 1;
processedCount = optoDataCount;
Console.WriteLine(
$"[{i}] ACCEPTED -> processedCount={processedCount}, firstValidIx={firstValidIx}, lastValidIx={lastValidIx}");
}
else
{
Console.WriteLine($"[{i}] NOT ACCEPTED");
}
Console.WriteLine("--------------------------------------------------");
}
Assert.IsTrue(processedCount > 0, "No valid calibration telegrams were parsed.");
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;
object[] markArgs = { 0, 0 };
InvokePrivate(reader, "AddTestStartEndMarksToData", markArgs);
InvokePrivate(reader, "AddTestStartEndMarksToData", new object[] { 0, 0 });
InvokePrivate(reader, "DataStreamPostProcessing");
InvokePrivate(reader, "PrepareCalculatedChannelData");
// first run: inspect values from debugger/log/output and then replace
Console.WriteLine($"VolumeLtrStart={reader.VolumeLtrStart}");
Console.WriteLine($"VolumeLtrEnd={reader.VolumeLtrEnd}");
Console.WriteLine($"TimestampSecStart={reader.TimestampSecStart}");
Console.WriteLine($"TimestampSecEnd={reader.TimestampSecEnd}");
const double expectedVolumeStart = 0.0; // replace with real value
const double expectedVolumeEnd = 0.0; // replace with real value
const double expectedTimeStart = 0.0; // replace with real value
const double expectedTimeEnd = 0.0; // replace with real value
const double tolerance = 0.000001;
Assert.AreEqual(expectedVolumeStart, reader.VolumeLtrStart, tolerance, "VolumeLtrStart mismatch");
Assert.AreEqual(expectedVolumeEnd, reader.VolumeLtrEnd, tolerance, "VolumeLtrEnd mismatch");
Assert.AreEqual(expectedTimeStart, reader.TimestampSecStart, tolerance, "TimestampSecStart mismatch");
Assert.AreEqual(expectedTimeEnd, reader.TimestampSecEnd, tolerance, "TimestampSecEnd mismatch");
Assert.IsTrue(reader.NoSamples,
"With only one telegram per channel, recalculated start/end samples should still be unavailable.");
}
[TestMethod]
public void RealInput_ShouldSupport_RolloverNormalization()
public void RealInput_ShouldParseRolloverInputLines()
{
var reader = new GenesisSmartReader();
var reader = new GenesisSmartReader(CreateCfg());
InitializeReaderForTest(reader);
string[] realInputLines = LoadRealInputLinesWithRollover();
Assert.IsTrue(realInputLines.Length > 0, "No rollover input lines were provided.");
foreach (var line in realInputLines)
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 optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.IsTrue(optoDataCount > 1, "Need at least 2 valid telegrams.");
int finalOptoDataCount = GetPrivateField<int>(reader, "optoDataCount");
reader.TestStartTelegramIx = 0;
reader.TestEndTelegramIx = optoDataCount - 1;
object[] markArgs = { 0, 0 };
InvokePrivate(reader, "AddTestStartEndMarksToData", markArgs);
InvokePrivate(reader, "DataStreamPostProcessing");
InvokePrivate(reader, "PrepareCalculatedChannelData");
Assert.IsTrue(reader.TimestampSecEnd >= reader.TimestampSecStart,
"Normalized end time should be >= start time");
Assert.IsTrue(reader.VolumeLtrEnd >= reader.VolumeLtrStart,
"Normalized end volume should be >= start volume");
Assert.AreEqual(3, finalOptoDataCount,
"Expected all provided rollover input lines to be parsed as telegrams.");
}
[TestMethod]
public void PrepareCalculatedChannelDataSimulationForTest_ShouldProduceUsableSamples()
{
var reader = new GenesisSmartReader(CreateCfg());
InitializeReaderForTest(reader);
reader.PrepareCalculatedChannelDataSimulationForTest();
Assert.IsFalse(reader.NoSamples);
Assert.AreEqual(105.0, reader.VolumeLtrStartAverage, 0.0001);
Assert.AreEqual(157.0, reader.VolumeLtrEndAwerage, 0.0001);
Assert.AreEqual(10.0, reader.TimestampSecStart, 0.0001);
Assert.AreEqual(20.0, reader.TimestampSecEnd, 0.0001);
}
private static void InitializeReaderForTest(GenesisSmartReader reader)
{
const int channelCount = 3;
@ -137,25 +215,20 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
SetPrivateField(reader, "partOfTelegram", string.Empty);
SetPrivateField(reader, "startDataProcessing", true);
reader.StopQueueData = false;
reader.TestStartTelegramIx = 0;
reader.TestEndTelegramIx = 0;
}
private static string[] LoadRealInputLines()
{
return new[]
{
// group 1
// 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",
// group 2 (next real lines from your log)
"@h 1 0 0A1F59C5 00017A44 00115C46 72E1596C 00000400 00001999 7D91B653 7D4F37E7 000191E7 0C 062E4A9D 5332",
"@h 2 0 0A1B1FE9 00017B80 00116B57 72B77428 00000400 0000199B 7DC09689 7B570007 000191E7 0C 062E5327 95BE",
"@h 3 0 0A1DF1D6 00017EA9 00118038 741F80F4 00000400 00001999 7E58A62F 7CC2C607 000191E7 0C 062E5BAF D40F"
};
}
@ -163,58 +236,83 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
return new[]
{
// --- FIRST 3 (before rollover) ---
// 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",
// --- LAST 3 (after rollover / later in log) ---
"@h 1 0 00000010 00000020 00000030 00000040 00000400 00000001 00000050 00000060 00000070 0C 00000080 1111",
"@h 2 0 00000011 00000021 00000031 00000041 00000400 00000002 00000051 00000061 00000071 0C 00000081 2222",
"@h 3 0 00000012 00000022 00000032 00000042 00000400 00000003 00000052 00000062 00000072 0C 00000082 3333",
};
}
private static void SetPrivateField(object target, string fieldName, object value)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
Assert.IsNotNull(field, $"Field '{fieldName}' not found.");
field.SetValue(target, 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)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
Assert.IsNotNull(field, $"Field '{fieldName}' not found.");
return (T)field.GetValue(target);
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)
{
var methods = target.GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(m => m.Name == methodName)
.ToList();
Type type = target.GetType();
Assert.IsTrue(methods.Count > 0, $"Method '{methodName}' not found.");
var method = methods.First();
if (args == null)
return method.Invoke(target, null);
var parameters = method.GetParameters();
if (parameters.Length != args.Length)
while (type != null)
{
throw new InvalidOperationException(
$"Method '{methodName}' expects {parameters.Length} parameters, but {args.Length} were provided.");
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;
}
var invokeArgs = new object[args.Length];
Array.Copy(args, invokeArgs, args.Length);
return method.Invoke(target, invokeArgs);
Assert.Fail($"Method '{methodName}' not found.");
return null;
}
}
}

View File

@ -132,14 +132,14 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(219.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(20.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual(200.0, reader.VolumeLtrStartRaw, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartAverage, 1e-9);
Assert.AreEqual(219.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
}
[TestMethod]
@ -162,11 +162,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
index++;
}
// channel 1 loses its last two valid records
// valid ch1 becomes: 200..207 with timestamps 1..8
// longest channel deltaTime is 9 (channels 0 and 2)
// ch1 deltaVolume = 7, deltaTime = 7 => recalculated deltaVolume = 7 * 9 / 7 = 9
// recalculated end ch1 = 200 + 9 = 209
optoData[25].Flags = OptoTelegramFlags.InvalidTelegram;
optoData[28].Flags = OptoTelegramFlags.InvalidTelegram;
@ -176,17 +171,18 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(209.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(209.666666666667, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(209.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
Assert.AreEqual(208.33333333333334, reader.VolumeLtrEndRaw, 1e-9);
Assert.AreEqual(100.0, reader.VolumeLtrStartCh1, 1e-9);
Assert.AreEqual(200.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
Assert.AreEqual(109.0, reader.VolumeLtrEndCh1, 1e-9);
Assert.AreEqual(209.0, reader.VolumeLtrEndCh2, 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);
@ -255,12 +251,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(100.0, reader.VolumeLtrStartCh1, 1e-9);
Assert.AreEqual(200.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
Assert.AreEqual(119.0, reader.VolumeLtrEndCh1, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEndCh2, 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);
@ -271,13 +267,13 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
Assert.AreEqual(20.0, reader.TimestampSecEndCh2, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEndCh3, 1e-9);
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(219.666666666667, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual(200.0, reader.VolumeLtrStartRaw, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartAverage, 1e-9);
Assert.AreEqual(219.666666666667, reader.VolumeLtrEndAwerage, 1e-9);
}
[TestMethod]
@ -300,14 +296,14 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(15.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(19.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(19.5, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(10.0, reader.VolumeLtrStartCh1, 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(14.0, reader.VolumeLtrEndCh1, 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);

View File

@ -460,7 +460,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var result = (OptoTelegramRaw[][])InvokePrivate(
"RecalculateVolumeAndTimeDeltaPerChannel",
(object)input,
input.Length);
input.Length,
0,
input.Length - 1);
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Length);
@ -499,7 +501,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var result = (OptoTelegramRaw[][])InvokePrivate(
"RecalculateVolumeAndTimeDeltaPerChannel",
(object)input,
input.Length);
input.Length,
0,
input.Length - 1);
Assert.IsNull(result);
}

View File

@ -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.");
}
}
}

View File

@ -1,5 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Common;
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig;
@ -65,6 +71,22 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
SetPrivateField(reader, "timestampSec", 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]
public void Start_ShouldOpenPort_AndEnableProcessing()
@ -92,11 +114,37 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
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();
WaitForStopToFinish(reader);
Assert.IsFalse(fake.IsOpen);
Assert.AreEqual(2, fake.CloseCalls);
Assert.IsTrue(fake.CloseCalls >= 1);
}
[TestMethod]
public void Run_ShouldCaptureStartTelegramIndex_WhenTimeReached()
@ -127,6 +175,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.Initialize();
reader.optoSerialPort = fake;
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
bool reset;
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
@ -161,6 +212,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.optoSerialPort = fake;
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
@ -216,6 +268,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.optoSerialPort = fake;
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
var line =
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD";
@ -227,9 +280,15 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
}
[DataTestMethod]
[DataRow("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", 0)]
[DataRow("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", 1)]
[DataRow("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", 2)]
[DataRow(
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
0)]
[DataRow(
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
1)]
[DataRow(
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
2)]
public void ProcessOptoLine_ShouldInsertTelegramAndUpdateExpectedChannel(string line, int expectedChannelIndex)
{
var fake = new FakeSerialDriver();
@ -239,6 +298,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.Initialize();
reader.optoSerialPort = fake;
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
@ -246,27 +308,25 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(1, optoDataCount, "Exactly one telegram should be inserted.");
Assert.AreEqual(1, optoDataCount);
var inserted = optoData[0];
Assert.IsNotNull(inserted, "Inserted telegram should not be null.");
Assert.IsNotNull(inserted);
Assert.AreEqual(expectedChannelIndex, inserted.IChannel(), "Inserted telegram channel does not match expected channel.");
Assert.AreEqual(0, inserted.Counter, "First inserted telegram should have counter 0.");
Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags, "Inserted telegram should be marked OK.");
Assert.AreNotEqual(default(DateTime), inserted.DateTime, "Inserted telegram DateTime should be initialized.");
Assert.AreEqual(expectedChannelIndex, inserted.IChannel());
Assert.AreEqual(0, inserted.Counter);
Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags);
Assert.AreNotEqual(0.0, inserted.VolumeRaw, "Inserted telegram VolumeRaw should be set.");
Assert.AreNotEqual(0.0, inserted.VolumeRawExt, "Inserted telegram VolumeRawExt should be set.");
Assert.AreNotEqual(0.0, inserted.Timestamp, "Inserted telegram Timestamp should be set.");
Assert.AreNotEqual(0.0, inserted.TimestampExt, "Inserted telegram TimestampExt should be set.");
Assert.AreNotEqual(0.0, inserted.VolumeRawExt);
Assert.AreNotEqual(0.0, inserted.TimestampExt);
for (int i = 0; i < ChannelCount; i++)
{
if (i == expectedChannelIndex)
{
Assert.AreNotEqual(0.0, volumeRawExtLast[i], $"Expected channel {i} volume cache was not updated.");
Assert.AreNotEqual(0.0, timestampExtLast[i], $"Expected channel {i} timestamp cache was not updated.");
Assert.AreNotEqual(0.0, timestampExtLast[i],
$"Expected channel {i} timestamp cache was not updated.");
}
else
{
@ -275,11 +335,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
}
}
Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9,
"Inserted telegram VolumeRawExt should match updated channel cache.");
Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9,
"Inserted telegram TimestampExt should match updated channel cache.");
Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9);
Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9);
}
[TestMethod]
@ -306,13 +363,16 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual((15.0 + 25.0 + 36.0) / 3.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(100.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(105.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRaw, 1e-9);
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRaw, 1e-9);
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartAverage, 1e-9);
Assert.AreEqual((15.0 + 25.0 + 36.0) / 3.0, reader.VolumeLtrEndAwerage, 1e-9);
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRawRaw, 1e-9);
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRawRaw, 1e-9);
}
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
@ -350,7 +410,21 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(19.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(19.5, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(15.5, reader.VolumeLtrStartAverage, 1e-9);
Assert.AreEqual(19.5, reader.VolumeLtrEndAwerage, 1e-9);
Assert.AreEqual(15.5, reader.VolumeLtrStartRawRaw, 1e-9);
Assert.AreEqual(19.0, reader.VolumeLtrEndRawRaw, 1e-9);
Assert.AreEqual(11.0, reader.VolumeLtrStartRawCh1, 1e-9);
Assert.AreEqual(20.0, reader.VolumeLtrStartRawCh2, 1e-9);
Assert.AreEqual(0.0, reader.VolumeLtrStartRawCh3, 1e-9);
Assert.AreEqual(14.0, reader.VolumeLtrEndRawCh1, 1e-9);
Assert.AreEqual(24.0, reader.VolumeLtrEndRawCh2, 1e-9);
Assert.AreEqual(0.0, reader.VolumeLtrEndRawCh3, 1e-9);
}
[TestMethod]
@ -382,43 +456,42 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
Assert.IsFalse(reset, "Reset must not happen immediately after @h 3.");
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
Assert.IsTrue(reset, "Reset must happen after trailing @f that closes the h1/h2/h3 block.");
//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 ReadOptoData_FullBlock_ShouldDiscardBuffersAfterClosingF()
public void ProcessingLoop_FullBlock_ShouldDiscardBuffersAfterClosingF()
{
var fake = new FakeSerialDriver();
fake.EnqueueLine("@f AA754B 4D0CEE78 5D89");
fake.EnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
fake.EnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
fake.EnqueueLine("@f AA7C01 4D0CFE76 B08F");
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
fake.Open();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 1;
var readMethod = typeof(GenesisSmartReader).GetMethod(
"ReadOptoData",
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new[] { typeof(DataStreamState) },
null);
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
Assert.IsNotNull(readMethod);
for (int i = 0; i < 5; i++)
reader.StartProcessingLoop();
try
{
readMethod.Invoke(reader, new object[] { DataStreamState.ProcessAndSave });
}
reader.TestEnqueueLine("@f AA754B 4D0CEE78 5D89");
reader.TestEnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
reader.TestEnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
reader.TestEnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
reader.TestEnqueueLine("@f AA7C01 4D0CFE76 B08F");
Assert.AreEqual(1, fake.DiscardInCalls, "Input buffer should be discarded once after completed block.");
Assert.AreEqual(1, fake.DiscardOutCalls, "Output buffer should be discarded once after completed block.");
Thread.Sleep(300);
Assert.AreEqual(1, fake.DiscardInCalls, "Input buffer should be discarded once after completed block.");
Assert.AreEqual(1, fake.DiscardOutCalls, "Output buffer should be discarded once after completed block.");
}
finally
{
reader.StopProcessingLoop();
}
}
[TestMethod]
@ -432,10 +505,13 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 1;
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
bool reset;
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Opening @f must not reset buffers.");
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
@ -490,24 +566,24 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 3;
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
bool reset;
for (int repetition = 1; repetition <= 3; repetition++)
{
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen on opening @f, repetition " + repetition);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after @h1, repetition " + repetition);
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after @h2, repetition " + repetition);
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after @h3, repetition " + repetition);
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
@ -529,24 +605,24 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 5;
InitializeThreeChannelState(reader);
EnableProcessingLoopForTests(reader);
bool reset;
for (int repetition = 1; repetition <= 5; repetition++)
{
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen on opening @f, repetition " + repetition);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after @h1, repetition " + repetition);
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after @h2, repetition " + repetition);
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after @h3, repetition " + repetition);
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
@ -556,5 +632,159 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
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);
}
}
}

View File

@ -68,6 +68,77 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
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()
@ -84,9 +155,9 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
Assert.IsNotNull(readLoopTask);
}
[TestMethod]
public void BackgroundReadLoop_ShouldMoveIncomingLinesToQueue()
public void ProcessingLoop_ShouldConsumeQueuedLine()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
@ -95,48 +166,58 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
InitializeThreeChannelState(reader);
reader.Start();
fake.EnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
ForceProcessingEnabled(reader);
InstallSuccessfulProcessingOverride(reader);
bool queued = WaitUntil(() =>
EnqueueIncomingLine(reader, "TEST_LINE_1");
bool processed = WaitUntil(() =>
{
var queue = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
return !queue.IsEmpty;
});
int optoDataCount = GetField<int>(reader, "optoDataCount");
return optoDataCount == 1;
}, 3000);
Assert.IsTrue(queued, "Expected background task to enqueue received serial line.");
Assert.IsTrue(processed, "Expected processing loop to consume queued line.");
int finalCount = GetField<int>(reader, "optoDataCount");
Assert.AreEqual(1, finalCount);
}
[TestMethod]
public void RunDeviceBefore_ShouldDrainQueue_AndInsertTelegram()
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();
fake.EnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
ForceProcessingEnabled(reader);
bool queued = WaitUntil(() =>
EnqueueIncomingLine(reader,
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
bool processed = WaitUntil(() =>
{
var queue = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
return !queue.IsEmpty;
});
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(queued, "Expected line to be queued before processing.");
Assert.IsTrue(processed, "Expected processing loop to consume queued line.");
reader.RunDeviceBefore();
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
int finalCount = GetPrivateField<int>(reader, "optoDataCount");
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
Assert.AreEqual(1, optoDataCount);
Assert.AreEqual(1, finalCount);
Assert.AreEqual(1, optoData[0].IChannel());
}
[TestMethod]
public void RunDeviceBefore_ShouldDrainAllQueuedLines()
public void ProcessingLoop_ShouldConsumeAllQueuedLines()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
@ -145,29 +226,148 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
InitializeThreeChannelState(reader);
reader.Start();
fake.EnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
fake.EnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
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 queued = WaitUntil(() =>
bool processed = WaitUntil(() =>
{
var queue = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
return !queue.IsEmpty;
});
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(queued);
reader.RunDeviceBefore();
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
var queueAfter = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
Assert.AreEqual(3, optoDataCount);
Assert.IsTrue(queueAfter.IsEmpty, "Queue should be empty after RunDeviceBefore drains it.");
Assert.IsTrue(processed, "Expected background processing loop to process line.");
}
[TestMethod]
public void StopDataStreamProcessing_ShouldStopReadLoop_AndClosePort()
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);
@ -176,45 +376,14 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.Start();
Assert.IsTrue(fake.IsOpen);
Assert.IsTrue(fake.OpenCalls >= 1);
reader.StopDataStreamProcessing();
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
var processLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_processLoopTask");
Assert.IsFalse(fake.IsOpen);
var cts = GetPrivateField<System.Threading.CancellationTokenSource>(reader, "_readLoopCts");
var task = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
Assert.IsNull(cts);
Assert.IsNull(task);
}
[TestMethod]
public void IncomingLines_ShouldNotBeProcessedUntilRunDeviceBeforeIsCalled()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
bool queued = WaitUntil(() =>
{
var queue = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
return !queue.IsEmpty;
});
Assert.IsTrue(queued);
int optoDataCountBefore = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(0, optoDataCountBefore, "Background thread should only enqueue, not process.");
reader.RunDeviceBefore();
int optoDataCountAfter = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(1, optoDataCountAfter);
Assert.IsNotNull(readLoopTask);
Assert.IsNotNull(processLoopTask);
}
}
}

View File

@ -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.");
}
}
}

View File

@ -12,35 +12,36 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
[TestSubject(typeof(CliRunner))]
public class CliRunnerTest
{
[TestMethod]
public void ExtractJson_Test()
{
String allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
string allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
CliRunner cliRunner = new CliRunner(false);
string json = cliRunner.ExtractJson(allOutput);
Assert.IsTrue(!string.IsNullOrEmpty(json));
Assert.IsFalse(string.IsNullOrEmpty(json));
}
[TestMethod]
public void ExtractJson_Test2()
{
String allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
string allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
CliRunner cliRunner = new CliRunner(false);
string json = cliRunner.ExtractJson(allOutput);
Assert.IsTrue(!string.IsNullOrEmpty(json));
Assert.IsFalse(string.IsNullOrEmpty(json));
}
[TestMethod]
public void TryJsonStringDeserialize_Test()
{
String allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
string allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
CliRunner cliRunner = new CliRunner(false);
string json = cliRunner.ExtractJson(allOutput);
var ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
bool ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
Assert.IsTrue(ok, "Deserialization failed");
Assert.IsNotNull(dto);
Assert.AreEqual("1000000267", dto.DeviceId);
@ -53,120 +54,165 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
[TestMethod]
public void RunMultipleTimesTestProgram_CheckParalelWork()
{
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74);
// Check if the executable exists in current directory
SerialPortData serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
{
Assert.Inconclusive($"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
Assert.Inconclusive(
$"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
return;
}
CliRunner cliRunner = new CliRunner(false);
CliRunner cliRunnerOne = new CliRunner(false);
DateTime StartTime = DateTime.Now;
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
DateTime startTime = DateTime.Now;
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
serialPort,
SerialPortData.EMeterArg.AllParams);
cliRunnerOne.WaitAll();
TimeSpan OneEndTime = DateTime.Now - StartTime;
StartTime = DateTime.Now;
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}");
TimeSpan oneEndTime = DateTime.Now - startTime;
startTime = DateTime.Now;
Console.WriteLine($"Start Time Loop: {startTime:yyyy-MM-dd HH:mm:ss}");
for (int i = 0; i < 100; i++)
{
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
serialPort,
SerialPortData.EMeterArg.AllParams);
}
cliRunner.WaitAll();
DateTime EndTime = DateTime.Now;
Console.WriteLine($"End Time Loop: {EndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
TimeSpan delta = EndTime - StartTime;
Console.WriteLine($"Delta Time One process {OneEndTime.Hours:D2}:{OneEndTime.Minutes:D2}:{OneEndTime.Seconds:D2}.{OneEndTime.Milliseconds:D3} " +
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
DateTime endTime = DateTime.Now;
Console.WriteLine($"End Time Loop: {endTime:yyyy-MM-dd HH:mm:ss}");
TimeSpan delta = endTime - startTime;
Console.WriteLine(
$"Delta Time One process {oneEndTime.Hours:D2}:{oneEndTime.Minutes:D2}:{oneEndTime.Seconds:D2}.{oneEndTime.Milliseconds:D3} " +
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
Console.WriteLine($"Total tasks: {cliRunner.TaskPool.Count}");
foreach (Task<JsonDataFromPoseidon> task in cliRunner.TaskPool)
foreach (CliTaskInfo taskInfo in cliRunner.TaskPool)
{
if (task != null)
Assert.IsNotNull(taskInfo);
Assert.IsNotNull(taskInfo.Task);
if (taskInfo.UseResult)
{
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
Assert.IsNotNull(task, "Expected Task<JsonDataFromPoseidon>.");
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
Console.WriteLine($"Result: {jsonDataFromPoseidon.DeviceId}");
Console.WriteLine($"Result: {jsonDataFromPoseidon?.DeviceId}");
Assert.IsNotNull(jsonDataFromPoseidon);
}
else
{
Assert.Fail($"Task did not complete successfully. State={taskInfo.State}, Status={taskInfo.Task.Status}");
}
}
// Check that the total time is greater than the sum of the individual times
Assert.IsTrue((cliRunner.TaskPool.Count*OneEndTime.Ticks) > delta.Ticks);
Assert.IsTrue((cliRunner.TaskPool.Count * oneEndTime.Ticks) > delta.Ticks);
}
[TestMethod]
public async Task RunMultipleTimesTestProgram_CheckParalelWorkCumulative()
{
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74);
// Check if the executable exists in current directory
SerialPortData serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
{
Assert.Inconclusive($"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
Assert.Inconclusive(
$"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
return;
}
List<CliRunner> cliRunnerList = new List<CliRunner>();
for (int i = 0; i < 20; i++)
{
cliRunnerList.Add(new CliRunner(false));
}
CliRunner cliRunnerOne = new CliRunner(false);
DateTime StartTime = DateTime.Now;
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
DateTime startTime = DateTime.Now;
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
serialPort,
SerialPortData.EMeterArg.AllParams);
cliRunnerOne.WaitAll();
TimeSpan OneEndTime = DateTime.Now - StartTime;
StartTime = DateTime.Now;
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}");
TimeSpan oneEndTime = DateTime.Now - startTime;
startTime = DateTime.Now;
Console.WriteLine($"Start Time Loop: {startTime:yyyy-MM-dd HH:mm:ss}");
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
{
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
serialPort,
SerialPortData.EMeterArg.AllParams);
for (int i = 0; i < 20; i++)
{
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
serialPort,
SerialPortData.EMeterArg.AllParams);
}
}
// Wait for ALL tasks from ALL runners
var allTasks = cliRunnerList.SelectMany(r => r.TaskPool).ToArray();
Task[] allTasks = cliRunnerList
.SelectMany(r => r.TaskPool)
.Where(ti => ti != null && ti.Task != null)
.Select(ti => ti.Task)
.ToArray();
await Task.WhenAll(allTasks);
DateTime EndTime = DateTime.Now;
Console.WriteLine($"End Time Loop: {EndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
TimeSpan delta = EndTime - StartTime;
Console.WriteLine($"Delta Time One process {OneEndTime.Hours:D2}:{OneEndTime.Minutes:D2}:{OneEndTime.Seconds:D2}.{OneEndTime.Milliseconds:D3} " +
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
DateTime endTime = DateTime.Now;
Console.WriteLine($"End Time Loop: {endTime:yyyy-MM-dd HH:mm:ss}");
TimeSpan delta = endTime - startTime;
Console.WriteLine(
$"Delta Time One process {oneEndTime.Hours:D2}:{oneEndTime.Minutes:D2}:{oneEndTime.Seconds:D2}.{oneEndTime.Milliseconds:D3} " +
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
Console.WriteLine($"Total tasks: {cliRunnerList.Sum(runner => runner.TaskPool.Count)}");
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
{
string results = $"iCliRunner: {iCliRunner}";
foreach (Task<JsonDataFromPoseidon> task in cliRunnerList[iCliRunner].TaskPool)
foreach (CliTaskInfo taskInfo in cliRunnerList[iCliRunner].TaskPool)
{
if (task != null)
Assert.IsNotNull(taskInfo);
Assert.IsNotNull(taskInfo.Task);
if (taskInfo.UseResult)
{
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
Assert.IsNotNull(task, "Expected Task<JsonDataFromPoseidon>.");
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
results += ($" ID: {jsonDataFromPoseidon.DeviceId}");
results += $" ID: {jsonDataFromPoseidon?.DeviceId}";
Assert.IsNotNull(jsonDataFromPoseidon);
}
else
{
Assert.Fail(
$"Task did not complete successfully. Runner={iCliRunner}, State={taskInfo.State}, Status={taskInfo.Task.Status}");
}
}
Console.WriteLine(results);
}
// Check that the total time is greater than the sum of the individual times
Assert.IsTrue((cliRunnerList.Count*OneEndTime.Ticks) > delta.Ticks);
Assert.IsTrue((cliRunnerList.Count * oneEndTime.Ticks) > delta.Ticks);
}
}
}

View File

@ -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.");
}
}
}
}

View File

@ -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());
}
}
}

View File

@ -111,8 +111,10 @@
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisReaderTests.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\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\hexLogger\HexFormatterTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrameBuilderTests.cs" />
@ -123,6 +125,8 @@
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadBaudRateDetectionTests.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrameBuilderTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutUnitTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />