Compare commits

...
Author SHA1 Message Date
michal 33ef914984 Update GenesisReaderTests: add real input parsing, format variation handling, and rollover cases; remove outdated Q3 calibration tests; increment version to 3.9.3056.1 2026-04-15 09:56:54 +02:00
michal 3eb0b87f0d Add Q3 calibration tests for GenesisSmartReader and refactor test cases:
- Introduce new `GenesisSmartReader_Q3Test` class with comprehensive Q3 calibration scenarios.
- Refactor existing tests to utilize simulation data for better consistency and validation.
- Adjust Q3 calibration logic in `GenesisSmartReader` to enhance calculation accuracy and validation criteria.
2026-04-15 09:22:09 +02:00
michal 711dfc03ef Fix Q3 calibration logic in GenesisSmartReader: replace _rawStartEndByChannel with recalculatedVariablesByChannel for accurate data computation. 2026-04-14 15:54:28 +02:00
michal 93459ccfab Refactor water meter handling in SequenceBase and FlyingStartMassCollectionSeq:
- Enhance logic for enabling/disabling channels based on water meter data.
- Introduce Q3 calibration validation in `GenesisSmartReader` with configurable thresholds.
- Update `GenesisCfgCtrl` UI to disable certain inputs.
2026-04-14 15:41:13 +02:00
michal 009aefb7c9 Refactor GenesisSmartReader and SequenceBase logic, enhance channel handling in water meters, and update GUI components:
- Adjust `Disabled` logic for water meters in `SequenceBase` allowing conditional enabling of channels based on serial number.
- Introduce `EnableShowChanels` property in `GenesisSmartReader` and integrate into `FlyingStartMassCollectionSeq`.
- Modify `StartupFlushMs` to derive configuration dynamically.
- Update `GenesisCfgCtrl` with a checkbox for enabling channels and input for flush timing.
- Increment assembly version to `3.9.3054.1`.
2026-04-14 08:03:02 +02:00
michal d2e18d615d Bump assembly version to 3.9.3053.1. 2026-04-13 14:17:56 +02:00
michal 69b475eb77 Refactor GenesisSmartReader for improved simulation and testing:
- Add `PrepareCalculatedChannelDataSimulationForTest` and supporting utility methods for generating and cloning test data.
- Update `StartupFlushMs` and sampling intervals for increased flexibility.
- Revise water meter handling logic in `FlyingStartMassCollectionSeq` to support multiple channels.
2026-04-13 14:17:34 +02:00
michal 2210bb8240 Enhance FlyingStartMassCollectionSeq with GenesisSmartReader support; add water meter handling logic and bump assembly version to 3.9.3050.1. 2026-04-11 21:55:34 +02:00
michal 65ab19290f simulate mode - flow reached 2026-04-09 12:49:00 +02:00
michal 94c7279da1 Add localized string resources for "Test in calculation" and integrate it into FlyingStartMassCollectionSeq
- Add `Test_in_calculation` localized strings to `.resx` files for English, Slovak, and Czech.
- Update `FlyingStartMassCollectionSeq` to display the new activity message during the calculation phase.
2026-04-09 10:08:50 +02:00
michal 2136695b80 Fix Stop 2026-04-08 16:37:27 +02:00
michal c0f9b0ffa0 Refactor GenesisSmartReader stop logic for enhanced thread safety and post-processing; bump assembly version to 3.9.3047.1. 2026-04-08 15:44:59 +02:00
michal c4ef22436f Update GenesisSmartReader Stop logic and bump assembly version to 3.9.3045.1:
- Refactor `Stop` for improved thread safety using `lock` on `_stateSync` and `_stopSync`.
- Enhance `Stop` behavior by introducing state updates during immediate stop requests.
- Fix redundant operations and reorganize log and processing statements for clarity.
- Update `RequestImmediateStopInternal` to accept a new state parameter.
2026-04-08 15:06:45 +02:00
michal 0b73871518 Refactor GenesisSmartReader and add advanced test utilities:
- Introduce `ForceProcessingEnabled`, `InstallSuccessfulProcessingOverride`, and `EnqueueIncomingLine` helpers to streamline test setup.
- Add new test suite `GenesisSmartReaderStopTests` to validate `Stop` behavior for different processing states.
- Enhance `GenesisSmartReader` state management with a dedicated `Reset` method and improved `Stop` logic.
- Improve buffer initialization in `ProcessOptoLine` to address null-value edge cases.
- Update assembly version to `3.9.3043.1`.
2026-04-08 13:51:55 +02:00
michal f7dc6fee5e immediate Stop improve 2026-04-07 16:37:19 +02:00
michal ba848a167d improve 2026-04-07 15:44:32 +02:00
michal dcb0bf9a88 Update GenesisSmartReader logging and bump assembly version to 3.9.3040.1:
- Enhance `Stop` logs with `registerReader` and `optoSerialPort` details for improved traceability.
2026-04-07 15:27:20 +02:00
michal f6433134c1 Add WaitForStartupFlushToFinish and EnableProcessingLoopForTests utilities for improved test reliability:
- Incorporate `WaitForStartupFlushToFinish` in `GenesisSmartReaderThreadedReadTests` to ensure startup flush completion before processing.
- Introduce `EnableProcessingLoopForTests` in `GenesisSmartReaderTest` to streamline test initialization.
- Fix thread-safety issues in startup flush handling and reorder operations in `GenesisSmartReader.Stop`.
- Bump assembly version to `3.9.3039.1`.
2026-04-07 14:45:37 +02:00
michal 7e3df77b5b Refactor GenesisSmartReader processing logic and tests for robust handling:
- Replace property getters with concise expressions in `GenesisSmartReader`.
- Implement FIFO and concurrent producer-consumer unit tests for `ProcessingLoop`.
- Add startup flush mechanism for improved input handling on initialization.
- Refactor processing loop with structured start/stop tasks and cancellation tokens.
- Fix thread-safety issues and enhance logic in `Stop` and `StopDataStreamProcessing`.
- Add internal visibility for tests in `TBFTests` via `InternalsVisibleTo`.
- Update assembly version to `3.9.3038.1`.
2026-04-07 10:42:27 +02:00
michal 08dff1272b Add overflow volume/timestamp handling and related tests for GenesisSmartReader:
- Extend `CalibrationRecord` constructor to include overflow fields (`OverflowVolumeCm`, `OverflowTimeS`).
- Implement additional unit tests in `OptoTelegramRawTest` to validate multi-rollover scenarios and extended calculations.
- Add `GenesisSmartReaderThreadedReadTests` to cover threaded read-loop functionality and serial processing.
- Enhance logging in `Pump` classes (`TurnOff` methods).
- Update project file to include new test files.
2026-04-02 09:25:17 +02:00
michal d5c8fd1c8f Refactor GenesisSmartReader with enhanced channel-specific volume and timestamp processing:
- Replace `AverageNullable` with `AverageCachedTime` and introduce `GetCachedVolume`/`GetCachedTime` helpers.
- Add recalibration logic with `_recalculatedStartEndByChannel` and `_rawStartEndByChannel`.
- Implement `PrepareCalculatedChannelData` for refined multi-channel synchronization.
- Add unit tests (`GenesisSmartReaderChannelTests`) for improved coverage and validation of new methods.
- Update `OptoTelegramRaw.Copy` to fix field assignment issues.
2026-03-27 15:12:43 +01:00
michal a34740e838 Update GenesisSmartReaderTest to handle DiscardOutCalls and configure block repetition resets:
- Modify tests to validate `DiscardOutCalls` alongside `DiscardInCalls`.
- Add support for configuring `ResetAfterBlockRepetitions` in test initialization.
2026-03-27 13:19:10 +01:00
michal 836ba5a41f Refactor GenesisSmartReader processing logic and add advanced multi-channel volume and timestamp handling:
- Introduce `TimeDeltaPerChannel` and `GroupRecordsPerChannel` for calculating time and volume deltas across channels.
- Add recalibration logic to synchronize volume and timestamp data with channel-specific maxima.
- Fix `ResetDataBuffer` implementation and replace redundant `ReasetDataBuffer` calls.
- Update handling of flow markers and block completion logic for improved buffer management.
- Add `Copy` method to `OptoTelegramRaw` for efficient cloning and recalculation.
- Bump `AssemblyVersion` and `AssemblyFileVersion` to `3.9.3019.1`.
2026-03-27 13:18:37 +01:00
michal 23c83ab452 Enhance GenesisSmartReader multi-channel processing and add tests for telegram handling:
- Refactor `ProcessOptoLine` logic to support output buffer resets at configurable repetition intervals.
- Add tests:
  - `ProcessOptoLine_Block_f_h1_h2_h3_f_ShouldResetBuffersOnlyAfterLastF`
  - `ReadOptoData_FullBlock_ShouldDiscardBuffersAfterClosingF`
  - `ProcessOptoLine_LastF_AfterH3_ShouldRequestBufferReset`
  - `ProcessOptoLine_ShouldReset
2026-03-26 14:45:36 +01:00
michal bc9e583c29 FIX Add ChannelAverages to project and remove unused imports in GenesisHead. 2026-03-26 07:29:13 +01:00
33 changed files with 10797 additions and 1130 deletions
+5 -2
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.3013.1")]
[assembly: AssemblyFileVersion("3.9.3013.1")]
[assembly: AssemblyVersion("3.9.3056.1")]
[assembly: AssemblyFileVersion("3.9.3056.1")]
+9 -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
+3
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>
+3
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>
+3
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>
+2
View File
@@ -111,6 +111,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
public void TurnOff()
{
log.DebugFormat("{0}.TurnOff()", Name);
if ((pumpFm1 != null) && (pumpFm2 != null))
{
pumpFm1.TurnOff();
@@ -120,6 +121,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
{
// TODO
}
log.DebugFormat("DONE {0}.TurnOff()", Name);
}
+2
View File
@@ -332,6 +332,8 @@ namespace TBF.Rig.Danfoss.VLT2800
Telegram.UpdateTelegramChecksum(msg);
}
SendData(msg);
log.DebugFormat("DONE {0}.TurnOff()", Name);
}
+2
View File
@@ -264,6 +264,8 @@ namespace TBF.Rig.Modbus.PumpFM.DanfossVLT
modbus.SendMessage(msg, Name);
}
log.DebugFormat("DONE {0}.TurnOff()", Name);
}
+2
View File
@@ -416,6 +416,8 @@ namespace TBF.Rig.Modbus.PumpFM.Grundfoss
// --- DIAGNOSTIKA ---
LiveLogDiag.Log1("GF TurnOff msg = " + BitConverter.ToString(msg));
}
log.DebugFormat("DONE {0}.TurnOff()", Name);
}
/// <summary>
@@ -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}";
}
}
}
@@ -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;
}
@@ -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;
@@ -6,7 +6,7 @@ using System;
using System.Globalization;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol;
using Xylem.Common.Metrology.Measurements;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
@@ -25,6 +25,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
public static readonly int Length = 42;
private static CultureInfo culture;
public MeasurementRecord data;
///
/// Strobed value
@@ -124,11 +125,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
}
// -------- TIMESTAMP (seconds) --------
private const double TS_RANGE = StreamingDecoder.CpuTimeOverflowS;
// -------- VOLUME (liters) --------
private const double VOL_RANGE = StreamingDecoder.DefaultAccuDutOverflowVolumeCm * 1000;
/// <summary>
/// update data by CalibrationRecord
@@ -149,7 +146,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
if (data == null)
throw new ArgumentNullException(nameof(data));
UpdateData( data.Channel, data.VolumeCm, data.TimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
this.data = data;
UpdateData( data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
}
/// <summary>
@@ -170,8 +168,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
{
if (data == null)
throw new ArgumentNullException(nameof(data));
UpdateData( data.Channel, data.VolumeCm, data.TimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
this.data = data;
UpdateData( data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
}
/// <summary>
@@ -187,7 +185,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
private void UpdateData(
int channel,
double volumeCm,
double OverflowVolumeCm,
double timeS,
double OverflowTimeS,
int counter,
float refFlow,
ref double volumeRawExtLast,
@@ -200,29 +200,30 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
Flags = OptoTelegramFlags.OK;
FlowRaw = 0;
VolumeRaw = volumeCm * 1000.0; // liters
double nV = NormalizeByOverflow(volumeCm, OverflowVolumeCm);
VolumeRaw = nV * 1000.0; // liters
CheckSum = 0;
Impedance = 0;
EmfRaw = 0;
MagneticFieldRaw = 0;
double ts = NormalizeTimestamp(timeS);
double ts = NormalizeByOverflow(timeS, OverflowTimeS);
Timestamp = ts;
VolumeRawExt = UnwrapVolume(VolumeRaw, ref volumeRawExtLast);
TimestampExt = UnwrapTimestamp(ts, ref timestampExtLast);
VolumeRawExt = UnwrapVolume(VolumeRaw,OverflowVolumeCm * 1000, ref volumeRawExtLast);
TimestampExt = UnwrapTimestamp(ts,OverflowTimeS, ref timestampExtLast);
}
private static double NormalizeTimestamp(double timeS)
private static double NormalizeByOverflow(double value, double overfValue = 0)
{
double ts = timeS % TS_RANGE;
if (ts < 0)
ts += TS_RANGE;
double nValue = value % overfValue;
if (nValue < 0)
nValue += overfValue;
return ts;
return nValue;
}
private static double UnwrapVolume(double currentVolume, ref double volumeRawExtLast)
private static double UnwrapVolume(double currentVolume, double overfValue, ref double volumeRawExtLast)
{
double result;
@@ -232,16 +233,25 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
}
else
{
result = currentVolume < volumeRawExtLast
? currentVolume + VOL_RANGE
: currentVolume;
double lastMod = volumeRawExtLast % overfValue;
if (lastMod < 0)
lastMod += overfValue;
double delta = currentVolume - lastMod;
if (delta < -overfValue / 2.0)
delta += overfValue;
else if (delta > overfValue / 2.0)
delta -= overfValue;
result = volumeRawExtLast + delta;
}
volumeRawExtLast = result;
return result;
}
private static double UnwrapTimestamp(double currentTimestamp, ref double timestampExtLast)
private static double UnwrapTimestamp(double currentTimestamp,double overfValue, ref double timestampExtLast)
{
double result;
@@ -251,16 +261,16 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
}
else
{
double lastMod = timestampExtLast % TS_RANGE;
double lastMod = timestampExtLast % overfValue;
if (lastMod < 0)
lastMod += TS_RANGE;
lastMod += overfValue;
double delta = currentTimestamp - lastMod;
if (delta < -TS_RANGE / 2.0)
delta += TS_RANGE;
else if (delta > TS_RANGE / 2.0)
delta -= TS_RANGE;
if (delta < -overfValue / 2.0)
delta += overfValue;
else if (delta > overfValue / 2.0)
delta -= overfValue;
result = timestampExtLast + delta;
}
@@ -349,5 +359,33 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
Label());
}
}
public void Copy(OptoTelegramRaw optoTelegramRaw)
{
this.Flags = optoTelegramRaw.Flags;
this.DateTime = optoTelegramRaw.DateTime;
this.RefFlow = optoTelegramRaw.RefFlow;
this.Counter = optoTelegramRaw.Counter;
this.EmfRaw = optoTelegramRaw.EmfRaw;
this.MagneticFieldRaw = optoTelegramRaw.MagneticFieldRaw;
this.FlowRaw = optoTelegramRaw.FlowRaw;
this.VolumeRaw = optoTelegramRaw.VolumeRaw;
this.VolumeRawExt = optoTelegramRaw.VolumeRawExt;
this.Impedance = optoTelegramRaw.Impedance;
this.Timestamp = optoTelegramRaw.Timestamp;
this.TimestampExt = optoTelegramRaw.TimestampExt;
this.CheckSum = optoTelegramRaw.CheckSum;
this.iChannel = optoTelegramRaw.iChannel;
}
public string rawDataToString()
{
if (data != null)
{
return string.Format("Channel: {0}, VolumeCm:{1}, OverflowVolumeCm:{2}, TimeS: {3}, OverflowTimeS:{4} ",
data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS);
}
return string.Empty;
}
}
}
@@ -227,6 +227,20 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils
return _serialPort.ReadExisting();
}
public void ResetInputBuffer()
{
if (!IsOpen)
throw new InvalidOperationException("Serial port not open");
_serialPort.DiscardInBuffer();
}
public void ResetOutputBuffer()
{
if (!IsOpen)
throw new InvalidOperationException("Serial port not open");
_serialPort.DiscardOutBuffer();
}
public Encoding Encoding => _serialPort?.Encoding ?? _encoding;
public int BytesToRead => (_serialPort != null && _serialPort.IsOpen) ? _serialPort.BytesToRead : 0;
@@ -110,6 +110,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
{
Flush = 0,
ProcessAndSave,
CalculateStoredData,
}
public enum CommunicationInterface
File diff suppressed because it is too large Load Diff
+36 -8
View File
@@ -678,7 +678,8 @@ namespace TBF.Rig.Sequences
/// On error or when STOP pressed
///
foreach (var fmPump in PumpsWithFM) fmPump.TurnOff();
log.Debug("STOP or ERROR: Pumps with FM stopped!");
if (inPath != null)
{
///
@@ -1275,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);
@@ -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)
@@ -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;
@@ -170,8 +173,8 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
string testName = Results.Utils.GetTestName(test.Name, test.Repeats, repetitionNr);
TestStartTime = DateTime.Now;
bool atleastOneGenesis = false;
atleastOneGenesis = GenesisHeadBatch.Start(sensPath.RegisterReaders, Program.LocalSettings.LastSNTexts);
// bool atleastOneGenesis = false;
// atleastOneGenesis = GenesisHeadBatch.Start(sensPath.RegisterReaders, Program.LocalSettings.LastSNTexts);
//------------------------------------------------
Bridge.OnActivity(this, Strings.Checking_tank_capacity);
@@ -452,23 +455,23 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
}
}
if (atleastOneGenesis)
{
log.Info($"Genesis - Starting... Test Name:{test.Name.ToLower()}.");
if (test.Name.ToLower().Contains("calib"))
{
log.Info("Genesis - Calibration starting.");
GenesisHeadBatch.BatchHolder.Value.MetersLogin();
GenesisHeadBatch.BatchHolder.Value.MetersInitCalibration();
}
if (test.Name.ToLower().Contains("init"))
{
log.Info("Genesis - init starting.");
GenesisHeadBatch.BatchHolder.Value.MetersLogin();
GenesisHeadBatch.BatchHolder.Value.MetersInitMeasurement();
}
}
// if (atleastOneGenesis)
// {
// log.Info($"Genesis - Starting... Test Name:{test.Name.ToLower()}.");
// if (test.Name.ToLower().Contains("calib"))
// {
// log.Info("Genesis - Calibration starting.");
// GenesisHeadBatch.BatchHolder.Value.MetersLogin();
// GenesisHeadBatch.BatchHolder.Value.MetersInitCalibration();
//
// }
// if (test.Name.ToLower().Contains("init"))
// {
// log.Info("Genesis - init starting.");
// GenesisHeadBatch.BatchHolder.Value.MetersLogin();
// GenesisHeadBatch.BatchHolder.Value.MetersInitMeasurement();
// }
// }
if (drainTheTank)
{
@@ -607,22 +610,40 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
}
while (!e.Contains(Event.TestCompleted) && !e.Contains(Event.Next)); /// 'Next' button is enabled in Debug version only
//------------------------------------------------
Bridge.OnActivity(this, Strings.Test_in_calculation);
//------------------------------------------------
/// Measurement loop end
StopRecordingStatistics();
if (atleastOneGenesis)
{
if (test.Name.ToLower().Contains("calib"))
{
log.Info("Genesis - Calibration stopped.");
GenesisHeadBatch.BatchHolder.Value.MetersStopCalibration();
}
else
{
log.Info("Genesis - Init measurement stopped.");
GenesisHeadBatch.BatchHolder.Value.MetersStopMeasurement();
}
}
if (sensPath != null && sensPath.RegisterReaders != null)
{
foreach (var rr in sensPath.RegisterReaders)
{
var datastreamRR = rr as ISmartReader;
if (datastreamRR != null)
{
datastreamRR.StopDataStreamProcessing();
}
}
}
// if (atleastOneGenesis)
// {
// if (test.Name.ToLower().Contains("calib"))
// {
// log.Info("Genesis - Calibration stopped.");
// GenesisHeadBatch.BatchHolder.Value.MetersStopCalibration();
// }
// else
// {
// log.Info("Genesis - Init measurement stopped.");
// GenesisHeadBatch.BatchHolder.Value.MetersStopMeasurement();
// }
// }
///
/// (Berlin:) Water is stopped immediately after the test and before the 2nd mass measurement in case:
@@ -955,6 +976,7 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
}
else
{
bool bUpgradeCountOfMeters = false;
///
/// Single meters
///
@@ -969,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)
{
@@ -1057,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)
{
@@ -1241,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,
@@ -1300,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.TimestampSecStartCh1,
genesisSmart.TimestampSecEndCh1, genesisSmart.VolumeLtrStartCh1, genesisSmart.VolumeLtrEndCh1);
}
else if (iCH == 1)
{
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartCh2,
genesisSmart.TimestampSecEndCh2, genesisSmart.VolumeLtrStartCh2, genesisSmart.VolumeLtrEndCh2);
}
else if (iCH == 2)
{
CalculateMeterResults(chanelXMeterRslt, genesisSmart, tstRslt, genesisSmart.TimestampSecStartCh3,
genesisSmart.TimestampSecEndCh3, genesisSmart.VolumeLtrStartCh3, genesisSmart.VolumeLtrEndCh3);
}
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,
@@ -15,8 +15,7 @@ using Xylem.Common.Metrology.Measurements;
using Xylem.Common.Metrology.Measurements.Consts;
using Common;
using System.IO.Ports;
using GenesisMeter = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter;
using MeterBatch = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.WaterMeterCore.MeterBatch;
namespace TBF.Rig.TestMethods.GenesisCommunication.GenesisHead
{
+6
View File
@@ -3,6 +3,7 @@
///
using System;
using System.Collections.Generic;
using Common;
using log4net;
using Config.Entities;
using TBF.Rig.ControlBoard.Uni;
@@ -200,6 +201,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;
+1
View File
@@ -1293,6 +1293,7 @@
<Compile Include="Rig\RegisterReaders\DataStream\Reader\ReaderCfg.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\common\HexFormatter.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\common\OptoTelegramRaw.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\ChannelAverages.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\Crc16Ccitt.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\DataPackages\EventArguments\BaseDataEventArgs.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\communication\Genesis\DataPackages\EventArguments\BendDetectDataEventArgs.cs" />
@@ -12,13 +12,17 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
private static CalibrationRecord CreateCalibrationRecord(
int channel = 1,
double volumeCm = 1.234,
double timeS = 12.5)
double overflowVolumeCm = 8.38860799804687,
double timeS = 12.5,
double overflowTimeS = 65536.0)
{
return new CalibrationRecord
{
Channel = channel,
VolumeCm = volumeCm,
TimeS = timeS
OverflowVolumeCm = overflowVolumeCm,
TimeS = timeS,
OverflowTimeS = overflowTimeS
};
}
@@ -26,7 +30,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
public void UpdateFromSmart_FirstSample_ShouldInitializeFields()
{
var telegram = new OptoTelegramRaw();
var data = CreateCalibrationRecord(channel: 2, volumeCm: 2.5, timeS: 100.0);
var data = CreateCalibrationRecord(
channel: 2,
volumeCm: 2.5,
overflowVolumeCm: 8.38860799804687,
timeS: 100.0,
overflowTimeS: 65536.0);
double lastVolume = double.NaN;
double lastTimestamp = double.NaN;
@@ -54,32 +63,479 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
Assert.AreEqual(0, telegram.MagneticFieldRaw);
}
[TestMethod]
public void UpdateFromSmart_RealGenesisDecodedSample_ShouldMapExpectedValues()
{
var telegram = new OptoTelegramRaw();
var data = CreateCalibrationRecord(
channel: 2,
volumeCm: 0.48025591796875,
overflowVolumeCm: 8.38860799804687,
timeS: 62727.8408813477,
overflowTimeS: 65536.0);
double lastVolume = double.NaN;
double lastTimestamp = double.NaN;
telegram.UpdateFromSmart(data, counter: 1, refFlow: 0.0f, ref lastVolume, ref lastTimestamp);
Assert.AreEqual(1, telegram.IChannel());
Assert.AreEqual(1, telegram.Counter);
Assert.AreEqual(0.0f, telegram.RefFlow);
Assert.AreEqual(OptoTelegramFlags.OK, telegram.Flags);
Assert.AreEqual(480.25591796875, telegram.VolumeRaw, 1e-9);
Assert.AreEqual(480.25591796875, telegram.VolumeRawExt, 1e-9);
Assert.AreEqual(480.25591796875, lastVolume, 1e-9);
Assert.AreEqual(62727.8408813477, telegram.Timestamp, 1e-9);
Assert.AreEqual(62727.8408813477, telegram.TimestampExt, 1e-9);
Assert.AreEqual(62727.8408813477, lastTimestamp, 1e-9);
Assert.AreEqual(0, telegram.FlowRaw);
Assert.AreEqual(0, telegram.CheckSum);
Assert.AreEqual(0, telegram.Impedance);
Assert.AreEqual(0, telegram.EmfRaw);
Assert.AreEqual(0, telegram.MagneticFieldRaw);
}
[TestMethod]
public void UpdateFromSmart_WhenVolumeDecreases_ShouldApplyVolumeRollover()
{
var telegram = new OptoTelegramRaw();
var data = CreateCalibrationRecord(channel: 1, volumeCm: 1.0, timeS: 10.0);
var overflowVolumeCm = 8.38860799804687;
var overflowRaw = overflowVolumeCm * 1000.0;
double previousExtendedVolume = 5000.0;
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: 1.0,
overflowVolumeCm: overflowVolumeCm,
timeS: 10.0,
overflowTimeS: 65536.0);
// Must be close enough to overflow so that current=1000 is interpreted as a wrap
double previousExtendedVolume = 8000.0;
double previousExtendedTimestamp = 10.0;
telegram.UpdateFromSmart(data, counter: 1, refFlow: 0.5f, ref previousExtendedVolume, ref previousExtendedTimestamp);
telegram.UpdateFromSmart(
data,
counter: 1,
refFlow: 0.5f,
ref previousExtendedVolume,
ref previousExtendedTimestamp);
double expectedVolumeRaw = 1000.0;
double expectedExtended = expectedVolumeRaw + StreamingDecoder.DefaultAccuDutOverflowVolumeCm * 1000.0;
double expectedExtended = expectedVolumeRaw + overflowRaw;
Assert.AreEqual(expectedVolumeRaw, telegram.VolumeRaw, 1e-9);
Assert.AreEqual(expectedExtended, telegram.VolumeRawExt, 1e-6);
Assert.AreEqual(expectedExtended, previousExtendedVolume, 1e-6);
}
[TestMethod]
public void UpdateFromSmart_MultipleVolumeRollover_ShouldKeepIncreasingExtendedVolume()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
// Safe sequence: small forward steps, natural wrap, small forward steps
double[] samples =
{
7.5,
8.0,
0.2,
1.0,
1.8,
2.6,
3.4,
4.2,
5.0,
5.8,
6.6,
7.4,
8.1,
0.3,
1.1
};
double previousExt = double.NaN;
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
double expectedRaw = samples[i] * 1000.0;
Assert.AreEqual(expectedRaw, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
if (!double.IsNaN(previousExt))
{
Assert.IsTrue(
telegram.VolumeRawExt >= previousExt,
$"Volume should not decrease at index {i}. Prev={previousExt}, Current={telegram.VolumeRawExt}");
}
previousExt = telegram.VolumeRawExt;
}
}
[TestMethod]
public void UpdateFromSmart_MultipleVolumeRollover_FirstToLastDeltaShouldBePositive()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] samples =
{
7.5, 8.0, 0.2, 1.0, 1.8, 2.6, 3.4, 4.2, 5.0, 5.8, 6.6, 7.4, 8.1, 0.3, 1.1
};
double firstExt = double.NaN;
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
if (i == 0)
firstExt = telegram.VolumeRawExt;
}
Assert.IsTrue(lastVolumeExt > firstExt,
$"Volume should increase from first to last. First={firstExt}, Last={lastVolumeExt}");
}
[TestMethod]
public void UpdateFromSmart_MultipleVolumeRollover_ShouldMatchPhysicalDelta()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] samples =
{
7.5,
8.0,
0.2,
1.0,
1.8,
2.6,
3.4,
4.2,
5.0,
5.8,
6.6,
7.4,
8.1,
0.3,
1.1
};
double firstExt = double.NaN;
double lastExt = double.NaN;
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
if (i == 0)
firstExt = telegram.VolumeRawExt;
if (i == samples.Length - 1)
lastExt = telegram.VolumeRawExt;
}
double start = samples[0];
double end = samples[samples.Length - 1];
int wraps = 0;
for (int i = 1; i < samples.Length; i++)
{
if (samples[i] < samples[i - 1])
wraps++;
}
double expectedDeltaCm = wraps * overflowVolumeCm + end - start;
double expectedDeltaRaw = expectedDeltaCm * 1000.0;
double actualDeltaRaw = lastExt - firstExt;
Assert.AreEqual(
expectedDeltaRaw,
actualDeltaRaw,
1e-6,
$"Delta mismatch. Wraps={wraps}, Expected={expectedDeltaRaw}, Actual={actualDeltaRaw}");
}
[TestMethod]
public void UpdateFromSmart_MultipleTimestampWrap_ShouldMatchPhysicalDelta()
{
var telegram = new OptoTelegramRaw();
double overflowTimeS = 65536.0;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
// Safe sequence: small forward steps, natural wrap, small forward steps, second wrap
double[] times =
{
65530.0,
65535.0,
2.0, // wrap 1
10.0,
100.0,
1000.0,
10000.0,
30000.0,
50000.0,
65534.0,
3.0, // wrap 2
20.0
};
double firstExt = double.NaN;
double lastExt = double.NaN;
for (int i = 0; i < times.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: 1.0,
overflowVolumeCm: 8.38860799804687,
timeS: times[i],
overflowTimeS: overflowTimeS);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
if (i == 0)
firstExt = telegram.TimestampExt;
if (i == times.Length - 1)
lastExt = telegram.TimestampExt;
}
double start = times[0];
double end = times[times.Length - 1];
int wraps = 0;
for (int i = 1; i < times.Length; i++)
{
if (times[i] < times[i - 1])
wraps++;
}
double expectedDelta = wraps * overflowTimeS + end - start;
double actualDelta = lastExt - firstExt;
Assert.AreEqual(
expectedDelta,
actualDelta,
1e-6,
$"Timestamp delta mismatch. Wraps={wraps}, Expected={expectedDelta}, Actual={actualDelta}");
}
[TestMethod]
public void UpdateFromSmart_MultipleTimestampWrap_ShouldKeepIncreasingExtendedTimestamp()
{
var telegram = new OptoTelegramRaw();
double overflowTimeS = 65536.0;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] times =
{
65530.0,
65535.0,
2.0,
10.0,
100.0,
1000.0,
10000.0,
30000.0,
50000.0,
65534.0,
3.0,
20.0
};
double previousExt = double.NaN;
for (int i = 0; i < times.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: 1.0,
overflowVolumeCm: 8.38860799804687,
timeS: times[i],
overflowTimeS: overflowTimeS);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
if (!double.IsNaN(previousExt))
{
Assert.IsTrue(
telegram.TimestampExt >= previousExt,
$"Timestamp should not decrease at index {i}. Prev={previousExt}, Current={telegram.TimestampExt}");
}
previousExt = telegram.TimestampExt;
}
}
[TestMethod]
public void UpdateFromSmart_MultipleVolumeRollover_ShouldProduceExpectedExtendedValues()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double overflowRaw = overflowVolumeCm * 1000.0;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] samples =
{
7.5,
8.0,
0.2,
0.8,
1.4
};
double[] expectedExt =
{
7500.0,
8000.0,
200.0 + overflowRaw,
800.0 + overflowRaw,
1400.0 + overflowRaw
};
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
Assert.AreEqual(samples[i] * 1000.0, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
Assert.AreEqual(expectedExt[i], telegram.VolumeRawExt, 1e-6, $"Mismatch at index {i}");
}
}
[TestMethod]
public void UpdateFromSmart_TwoVolumeRollovers_ShouldKeepExtendedVolumeIncreasing()
{
var telegram = new OptoTelegramRaw();
double overflowVolumeCm = 8.38860799804687;
double overflowRaw = overflowVolumeCm * 1000.0;
double lastVolumeExt = double.NaN;
double lastTimestampExt = double.NaN;
double[] samples =
{
6.8,
8.1,
0.3,
1.2,
2.1,
3.0,
4.0,
7.5,
8.1,
0.4,
1.1
};
double[] expectedExt =
{
6800.0,
8100.0,
overflowRaw + 300.0,
overflowRaw + 1200.0,
overflowRaw + 2100.0,
overflowRaw + 3000.0,
overflowRaw + 4000.0,
overflowRaw + 7500.0,
overflowRaw + 8100.0,
2.0 * overflowRaw + 400.0,
2.0 * overflowRaw + 1100.0
};
double previous = double.NaN;
for (int i = 0; i < samples.Length; i++)
{
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: samples[i],
overflowVolumeCm: overflowVolumeCm,
timeS: i,
overflowTimeS: 65536.0);
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
Assert.AreEqual(samples[i] * 1000.0, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
Assert.AreEqual(expectedExt[i], telegram.VolumeRawExt, 1e-6, $"Mismatch at index {i}");
if (!double.IsNaN(previous))
{
Assert.IsTrue(
telegram.VolumeRawExt > previous,
$"Extended volume did not increase at index {i}. Prev={previous}, Current={telegram.VolumeRawExt}");
}
previous = telegram.VolumeRawExt;
}
}
[TestMethod]
public void UpdateFromSmart_WhenTimestampWraps_ShouldUnwrapForward()
{
var telegram = new OptoTelegramRaw();
double tsRange = StreamingDecoder.CpuTimeOverflowS;
var data = CreateCalibrationRecord(channel: 1, volumeCm: 1.0, timeS: 1.0);
var data = CreateCalibrationRecord(
channel: 1,
volumeCm: 1.0,
overflowVolumeCm: 8.38860799804687,
timeS: 1.0,
overflowTimeS: tsRange);
double previousExtendedVolume = 1000.0;
double previousExtendedTimestamp = tsRange - 0.25;
@@ -16,10 +16,11 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
public int CloseCalls { get; private set; }
public int DiscardInCalls { get; private set; }
public int DiscardOutCalls { get; private set; }
public Encoding Encoding { get; set; } = Encoding.ASCII;
public int BytesToRead => _lines.Count > 0 ? 1 : 0;
public int BytesToRead => _isOpen && _lines.Count > 0 ? 1 : 0;
public int BytesToWrite => 0;
public void EnqueueLine(string line) => _lines.Enqueue(line);
@@ -0,0 +1,365 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
using TBF.Rig.Sequences;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class GenesisReaderTests
{
[TestMethod]
public void Debug_ProcessOptoLine_FormatVariants()
{
var reader = new GenesisSmartReader();
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"
};
for (int i = 0; i < variants.Length; i++)
{
bool blockCompleted = false;
reader.ProcessOptoLine(variants[i], DataStreamState.ProcessAndSave, out blockCompleted);
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Console.WriteLine($"Variant {i}: {variants[i]}");
Console.WriteLine($" blockCompleted={blockCompleted}");
Console.WriteLine($" optoDataCount={optoDataCount}");
Console.WriteLine("--------------------------------");
}
Assert.Fail("Inspect which variant, if any, is accepted.");
}
[TestMethod]
public void RealInput_ShouldCalculate_StartEndVolumes_AndTimes()
{
var reader = new GenesisSmartReader();
InitializeReaderForTest(reader);
string[] realInputLines = LoadRealInputLines();
Assert.IsTrue(realInputLines.Length > 0, "No test input lines were provided.");
int processedCount = 0;
int firstValidIx = -1;
int lastValidIx = -1;
Console.WriteLine("=== BEGIN INPUT PARSING ===");
for (int i = 0; i < realInputLines.Length; i++)
{
string line = realInputLines[i];
bool blockCompleted = false;
Exception parseException = null;
try
{
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out blockCompleted);
}
catch (Exception ex)
{
parseException = ex;
}
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Console.WriteLine($"[{i}] line = {line}");
Console.WriteLine($"[{i}] blockCompleted = {blockCompleted}");
Console.WriteLine($"[{i}] optoDataCount = {optoDataCount}");
if (parseException != null)
{
Console.WriteLine($"[{i}] EXCEPTION = {parseException.GetType().Name}: {parseException.Message}");
}
if (optoDataCount > processedCount)
{
if (firstValidIx < 0)
firstValidIx = processedCount;
lastValidIx = optoDataCount - 1;
processedCount = optoDataCount;
Console.WriteLine(
$"[{i}] ACCEPTED -> processedCount={processedCount}, firstValidIx={firstValidIx}, lastValidIx={lastValidIx}");
}
else
{
Console.WriteLine($"[{i}] NOT ACCEPTED");
}
Console.WriteLine("--------------------------------------------------");
}
Console.WriteLine("=== END INPUT PARSING ===");
Console.WriteLine($"Final processedCount = {processedCount}");
Console.WriteLine($"Final firstValidIx = {firstValidIx}");
Console.WriteLine($"Final lastValidIx = {lastValidIx}");
if (processedCount <= 0)
{
Assert.Fail(
"No valid calibration telegrams were parsed.\n" +
"Check the following:\n" +
"1. exact telegram prefix (for example @h vs @he)\n" +
"2. exact separators (spaces vs tabs)\n" +
"3. exact CRC / checksum\n" +
"4. whether ProcessOptoLine expects a complete multi-line block format\n" +
"See test output for per-line diagnostics.");
}
reader.TestStartTelegramIx = firstValidIx;
reader.TestEndTelegramIx = lastValidIx;
Console.WriteLine("=== BEFORE POST PROCESSING ===");
Console.WriteLine($"TestStartTelegramIx = {reader.TestStartTelegramIx}");
Console.WriteLine($"TestEndTelegramIx = {reader.TestEndTelegramIx}");
object[] markArgs = { 0, 0 };
try
{
InvokePrivate(reader, "AddTestStartEndMarksToData", markArgs);
Console.WriteLine("AddTestStartEndMarksToData OK");
InvokePrivate(reader, "DataStreamPostProcessing");
Console.WriteLine("DataStreamPostProcessing OK");
InvokePrivate(reader, "PrepareCalculatedChannelData");
Console.WriteLine("PrepareCalculatedChannelData OK");
}
catch (Exception ex)
{
Assert.Fail(
"Post-processing failed.\n" +
$"Exception: {ex.GetType().Name}: {ex.Message}\n" +
$"StackTrace:\n{ex.StackTrace}");
}
Console.WriteLine("=== CALCULATED VALUES ===");
Console.WriteLine($"NoSamples = {reader.NoSamples}");
Console.WriteLine($"VolumeLtrStart = {reader.VolumeLtrStart}");
Console.WriteLine($"VolumeLtrEnd = {reader.VolumeLtrEnd}");
Console.WriteLine($"TimestampSecStart = {reader.TimestampSecStart}");
Console.WriteLine($"TimestampSecEnd = {reader.TimestampSecEnd}");
Assert.IsFalse(double.IsNaN(reader.VolumeLtrStart), "VolumeLtrStart is NaN");
Assert.IsFalse(double.IsNaN(reader.VolumeLtrEnd), "VolumeLtrEnd is NaN");
Assert.IsFalse(double.IsNaN(reader.TimestampSecStart), "TimestampSecStart is NaN");
Assert.IsFalse(double.IsNaN(reader.TimestampSecEnd), "TimestampSecEnd is NaN");
Assert.IsTrue(reader.TimestampSecEnd >= reader.TimestampSecStart,
$"Timestamp ordering invalid: start={reader.TimestampSecStart}, end={reader.TimestampSecEnd}");
Assert.IsTrue(reader.VolumeLtrEnd >= reader.VolumeLtrStart,
$"Volume ordering invalid: start={reader.VolumeLtrStart}, end={reader.VolumeLtrEnd}");
// When parser input is finally correct, replace these expected values:
const double expectedVolumeStart = 0.0;
const double expectedVolumeEnd = 0.0;
const double expectedTimeStart = 0.0;
const double expectedTimeEnd = 0.0;
const double tolerance = 0.000001;
Console.WriteLine("=== EXPECTED VS ACTUAL ===");
Console.WriteLine($"expectedVolumeStart = {expectedVolumeStart}, actual = {reader.VolumeLtrStart}");
Console.WriteLine($"expectedVolumeEnd = {expectedVolumeEnd}, actual = {reader.VolumeLtrEnd}");
Console.WriteLine($"expectedTimeStart = {expectedTimeStart}, actual = {reader.TimestampSecStart}");
Console.WriteLine($"expectedTimeEnd = {expectedTimeEnd}, actual = {reader.TimestampSecEnd}");
Assert.AreEqual(expectedVolumeStart, reader.VolumeLtrStart, tolerance, "VolumeLtrStart mismatch");
Assert.AreEqual(expectedVolumeEnd, reader.VolumeLtrEnd, tolerance, "VolumeLtrEnd mismatch");
Assert.AreEqual(expectedTimeStart, reader.TimestampSecStart, tolerance, "TimestampSecStart mismatch");
Assert.AreEqual(expectedTimeEnd, reader.TimestampSecEnd, tolerance, "TimestampSecEnd mismatch");
}
[TestMethod]
public void RealInput_ShouldSupport_RolloverNormalization()
{
var reader = new GenesisSmartReader();
InitializeReaderForTest(reader);
string[] realInputLines = LoadRealInputLinesWithRollover();
Assert.IsTrue(realInputLines.Length > 0, "No rollover input lines were provided.");
for (int i = 0; i < realInputLines.Length; i++)
{
string line = realInputLines[i];
bool blockCompleted;
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out blockCompleted);
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Console.WriteLine($"[{i}] line={line}");
Console.WriteLine($"[{i}] optoDataCount={optoDataCount}, blockCompleted={blockCompleted}");
}
int finalOptoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.IsTrue(
finalOptoDataCount > 1,
"Need at least 2 valid telegrams. Check exact telegram format / CRC in LoadRealInputLinesWithRollover().");
reader.TestStartTelegramIx = 0;
reader.TestEndTelegramIx = finalOptoDataCount - 1;
object[] markArgs = { 0, 0 };
InvokePrivate(reader, "AddTestStartEndMarksToData", markArgs);
InvokePrivate(reader, "DataStreamPostProcessing");
InvokePrivate(reader, "PrepareCalculatedChannelData");
Console.WriteLine($"VolumeLtrStart={reader.VolumeLtrStart}");
Console.WriteLine($"VolumeLtrEnd={reader.VolumeLtrEnd}");
Console.WriteLine($"TimestampSecStart={reader.TimestampSecStart}");
Console.WriteLine($"TimestampSecEnd={reader.TimestampSecEnd}");
Assert.IsTrue(reader.TimestampSecEnd >= reader.TimestampSecStart,
"Normalized end time should be >= start time");
Assert.IsTrue(reader.VolumeLtrEnd >= reader.VolumeLtrStart,
"Normalized end volume should be >= start volume");
}
private static void InitializeReaderForTest(GenesisSmartReader reader)
{
const int channelCount = 3;
SetPrivateField(reader, "volumeRawExtLast", new double[channelCount]);
SetPrivateField(reader, "timestampExtLast", new double[channelCount]);
SetPrivateField(reader, "lastTimestamp", new double[channelCount]);
SetPrivateField(reader, "timestampSec", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "timestampSec0", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "lastVolumeRaw", new double[channelCount]);
SetPrivateField(reader, "volumeLtr", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "volumeLtr0", Enumerable.Repeat(double.NaN, channelCount).ToArray());
var optoData = new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize];
for (int i = 0; i < optoData.Length; i++)
optoData[i] = new OptoTelegramRaw();
SetPrivateField(reader, "optoData", optoData);
SetPrivateField(reader, "optoDataCount", 0);
SetPrivateField(reader, "toBeFlushed", new OptoTelegramRaw());
SetPrivateField(reader, "flowDirectionDetection", new FlowDirectionDetection());
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
SetPrivateField(reader, "synchronized", false);
SetPrivateField(reader, "synchronized2", false);
SetPrivateField(reader, "partOfTelegram", string.Empty);
SetPrivateField(reader, "startDataProcessing", true);
reader.TestStartTelegramIx = 0;
reader.TestEndTelegramIx = 0;
}
private static string[] LoadRealInputLines()
{
return new[]
{
// Replace these with REAL, unchanged captured telegram lines from the device.
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
};
}
private static string[] LoadRealInputLinesWithRollover()
{
return new[]
{
// Replace these with REAL, unchanged captured telegram lines spanning the rollover case.
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
};
}
private static void SetPrivateField(object target, string fieldName, object value)
{
Type type = target.GetType();
while (type != null)
{
var field = type.GetField(
fieldName,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
if (field != null)
{
field.SetValue(target, value);
return;
}
type = type.BaseType;
}
Assert.Fail($"Field '{fieldName}' not found.");
}
private static T GetPrivateField<T>(object target, string fieldName)
{
Type type = target.GetType();
while (type != null)
{
var field = type.GetField(
fieldName,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
if (field != null)
return (T)field.GetValue(target);
type = type.BaseType;
}
Assert.Fail($"Field '{fieldName}' not found.");
return default;
}
private static object InvokePrivate(object target, string methodName, object[] args = null)
{
Type type = target.GetType();
while (type != null)
{
var methods = type.GetMethods(
BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.DeclaredOnly)
.Where(m => m.Name == methodName)
.ToList();
foreach (var method in methods)
{
if (args == null && method.GetParameters().Length == 0)
return method.Invoke(target, null);
if (args != null && method.GetParameters().Length == args.Length)
return method.Invoke(target, args);
}
type = type.BaseType;
}
Assert.Fail($"Method '{methodName}' not found.");
return null;
}
}
}
@@ -2,7 +2,6 @@ using System;
using System.Reflection;
using Common;
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;
@@ -14,6 +13,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
private const int ChannelCount = 3;
[TestInitialize]
public void TestInitialize()
{
SetPrivateStaticField(typeof(GenesisSmartReader), "chenelsSwichCount", ChannelCount);
}
private static void SetPrivateField(object target, string fieldName, object value)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
@@ -32,13 +37,25 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
return (T)field.GetValue(target);
}
private static void InvokePrivateMethod(object target, string methodName)
private static void SetPrivateStaticField(Type type, string fieldName, object value)
{
var method = target.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
if (method == null)
throw new MissingMethodException(target.GetType().FullName, methodName);
var field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(type.FullName, fieldName);
method.Invoke(target, null);
field.SetValue(null, value);
}
private static void InvokePrepareCalculatedChannelData(GenesisSmartReader reader)
{
var method = typeof(GenesisSmartReader).GetMethod(
"PrepareCalculatedChannelData",
BindingFlags.Instance | BindingFlags.NonPublic);
if (method == null)
throw new MissingMethodException(typeof(GenesisSmartReader).FullName, "PrepareCalculatedChannelData");
method.Invoke(reader, null);
}
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt, int counter)
@@ -53,8 +70,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
DateTime = DateTime.Now
};
}
private static GenesisCfg CreateCfg()
{
var cfg = new GenesisCfg(null);
@@ -75,21 +91,27 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
return cfg;
}
[TestMethod]
public void SimulatedInterleaved20x3ChannelStream_ShouldComputeStartAndEndVolumeAndTimeCorrectly()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
SetPrivateField(reader, "volumeRawExtLast", new double[3]);
SetPrivateField(reader, "timestampExtLast", new double[3]);
SetPrivateField(reader, "flowDirectionDetection", new FlowDirectionDetection());
private static GenesisSmartReader CreateReaderWithOptoBuffer()
{
var reader = new GenesisSmartReader(CreateCfg(), null);
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
for (int i = 0; i < optoData.Length; i++)
{
optoData[i] = new OptoTelegramRaw();
}
return reader;
}
[TestMethod]
public void SimulatedInterleaved20x3ChannelStream_ShouldComputeRecalculatedStartAndEndVolumeAndTimeCorrectly()
{
var reader = CreateReaderWithOptoBuffer();
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
int index = 0;
for (int g = 0; g < 20; g++)
@@ -108,66 +130,83 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.TestStartTelegramIx = 2;
reader.TestEndTelegramIx = index - 1;
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(217.0, reader.VolumeLtrEnd, 1e-9);
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(18.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartRaw, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
}
[TestMethod]
public void VolumeLtrEnd_ShouldIgnoreInvalidTrailingRecords_AndUsePreviousValidPerChannelSamples()
public void VolumeLtrEnd_ShouldRecalculateShorterChannelToLongestChannelTimeSpan()
{
var reader = new GenesisSmartReader(CreateCfg(), null);
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
var reader = CreateReaderWithOptoBuffer();
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
for (int i = 0; i < optoData.Length; i++)
optoData[i] = new OptoTelegramRaw();
int index = 0;
// Build 10 groups of valid 3-channel data
for (int g = 0; g < 10; g++)
{
optoData[index++] = CreateOptoRecord(0, 100 + g, 1 + g, index);
optoData[index++] = CreateOptoRecord(1, 200 + g, 1 + g, index);
optoData[index++] = CreateOptoRecord(2, 300 + g, 1 + g, index);
optoData[index] = CreateOptoRecord(0, 100 + g, 1 + g, index);
index++;
optoData[index] = CreateOptoRecord(1, 200 + g, 1 + g, index);
index++;
optoData[index] = CreateOptoRecord(2, 300 + g, 1 + g, index);
index++;
}
// Corrupt the last two channel-1 records
optoData[25].Flags = OptoTelegramFlags.InvalidTelegram; // group 8, channel 1
optoData[28].Flags = OptoTelegramFlags.InvalidTelegram; // group 9, channel 1
// 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;
SetPrivateField(reader, "optoDataCount", index);
reader.TestStartTelegramIx = 2;
reader.TestEndTelegramIx = index - 1;
// Start still comes from first full valid 3-channel snapshot:
// (100 + 200 + 300) / 3 = 200
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
InvokePrepareCalculatedChannelData(reader);
// End with last 5 valid per channel:
// ch0 valid last 5: 105,106,107,108,109 => avg 107
// ch1 valid last 5: 203,204,205,206,207 => avg 205
// because 208 and 209 were invalid
// ch2 valid last 5: 305,306,307,308,309 => avg 307
// total avg = (107 + 205 + 307) / 3 = 206.3333333333...
Assert.AreEqual(206.33333333333334, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(208.33333333333334, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(208.33333333333334, reader.VolumeLtrEndRaw, 1e-9);
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartRaw, 1e-9);
Assert.AreEqual(110.0, reader.VolumeLtrEndCh1, 1e-9);
Assert.AreEqual(210.0, reader.VolumeLtrEndCh2, 1e-9);
Assert.AreEqual(309.0, reader.VolumeLtrEndCh3, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(10.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh1, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh2, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh3, 1e-9);
Assert.AreEqual(10.0, reader.TimestampSecEndCh1, 1e-9);
Assert.AreEqual(10.0, reader.TimestampSecEndCh2, 1e-9);
Assert.AreEqual(10.0, reader.TimestampSecEndCh3, 1e-9);
}
[TestMethod]
public void VolumeLtrStart_And_End_ShouldBeZero_WhenAllSamplesAreInvalid()
{
var reader = new GenesisSmartReader(CreateCfg(), null);
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
var reader = CreateReaderWithOptoBuffer();
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
for (int i = 0; i < optoData.Length; i++)
optoData[i] = new OptoTelegramRaw();
for (int i = 0; i < 9; i++)
{
@@ -184,8 +223,108 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.TestStartTelegramIx = 2;
reader.TestEndTelegramIx = 8;
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(0.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(0.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(0.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(0.0, reader.TimestampSecEnd, 1e-9);
}
[TestMethod]
public void SimulatedInterleaved20x3ChannelStream_ShouldComputePerChannelStartAndEndValues()
{
var reader = CreateReaderWithOptoBuffer();
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
int index = 0;
for (int g = 0; g < 20; g++)
{
optoData[index] = CreateOptoRecord(0, 100 + g, 1 + g, index);
index++;
optoData[index] = CreateOptoRecord(1, 200 + g, 1 + g, index);
index++;
optoData[index] = CreateOptoRecord(2, 300 + g, 1 + g, index);
index++;
}
SetPrivateField(reader, "optoDataCount", index);
reader.TestStartTelegramIx = 2;
reader.TestEndTelegramIx = index - 1;
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
Assert.AreEqual(120.0, reader.VolumeLtrEndCh1, 1e-9);
Assert.AreEqual(220.0, reader.VolumeLtrEndCh2, 1e-9);
Assert.AreEqual(319.0, reader.VolumeLtrEndCh3, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh1, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh2, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStartCh3, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEndCh1, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEndCh2, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEndCh3, 1e-9);
Assert.AreEqual(101.0, reader.VolumeLtrStartCh1, 1e-9);
Assert.AreEqual(201.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
Assert.AreEqual(200.666666666667, reader.VolumeLtrStartRaw, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
Assert.AreEqual(120.0, reader.VolumeLtrEndCh1, 1e-9);
Assert.AreEqual(220.0, reader.VolumeLtrEndCh2, 1e-9);
Assert.AreEqual(319.0, reader.VolumeLtrEndCh3, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
}
[TestMethod]
public void VolumeLtrEnd_ShouldAverageOnlyAvailableChannels()
{
var reader = CreateReaderWithOptoBuffer();
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
int idx = 0;
for (int i = 0; i < 5; i++)
{
optoData[idx++] = CreateOptoRecord(0, 10 + i, 100 + i, idx);
optoData[idx++] = CreateOptoRecord(1, 20 + i, 100 + i, idx);
}
SetPrivateField(reader, "optoDataCount", idx);
reader.TestStartTelegramIx = 1;
reader.TestEndTelegramIx = idx - 1;
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(19.0, reader.VolumeLtrEnd, 1e-9);
Assert.AreEqual(11.0, reader.VolumeLtrStartCh1, 1e-9);
Assert.AreEqual(20.0, reader.VolumeLtrStartCh2, 1e-9);
Assert.AreEqual(0.0, reader.VolumeLtrStartCh3, 1e-9);
Assert.AreEqual(15.0, reader.VolumeLtrEndCh1, 1e-9);
Assert.AreEqual(24.0, reader.VolumeLtrEndCh2, 1e-9);
Assert.AreEqual(0.0, reader.VolumeLtrEndCh3, 1e-9);
Assert.AreEqual(100.0, reader.TimestampSecStart, 1e-9);
Assert.AreEqual(104.0, reader.TimestampSecEnd, 1e-9);
}
}
}
@@ -0,0 +1,511 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class GenesisSmartReaderChannelTests
{
private GenesisSmartReader _reader;
[TestInitialize]
public void Setup()
{
_reader = new GenesisSmartReader();
}
private static OptoTelegramRaw Rec(
int channel,
double time,
double volume,
OptoTelegramFlags flags = OptoTelegramFlags.OK)
{
return new OptoTelegramRaw
{
iChannel = channel,
TimestampExt = time,
VolumeRawExt = volume,
Flags = flags
};
}
private object InvokePrivate(string methodName, params object[] args)
{
var mi = typeof(GenesisSmartReader).GetMethod(
methodName,
BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(mi, $"Method '{methodName}' was not found.");
return mi.Invoke(_reader, args);
}
private static T GetPrivateField<T>(object instance, string fieldName)
{
var fi = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(fi, $"Field '{fieldName}' was not found.");
return (T)fi.GetValue(instance);
}
private static void SetPrivateField(object instance, string fieldName, object value)
{
var fi = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(fi, $"Field '{fieldName}' was not found.");
fi.SetValue(instance, value);
}
[TestMethod]
public void GroupRecordsPerChannel_Groups_Valid_Records_By_Channel()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 110),
Rec(1, 14, 220),
Rec(2, 15, 330),
};
var grouped = (OptoTelegramRaw[][])InvokePrivate(
"GroupRecordsPerChannel",
(object)input, 0, input.Length - 1, input.Length);
Assert.AreEqual(3, grouped.Length);
Assert.AreEqual(2, grouped[0].Length);
Assert.AreEqual(2, grouped[1].Length);
Assert.AreEqual(2, grouped[2].Length);
Assert.AreEqual(100, grouped[0][0].VolumeRawExt);
Assert.AreEqual(110, grouped[0][1].VolumeRawExt);
Assert.AreEqual(200, grouped[1][0].VolumeRawExt);
Assert.AreEqual(220, grouped[1][1].VolumeRawExt);
Assert.AreEqual(300, grouped[2][0].VolumeRawExt);
Assert.AreEqual(330, grouped[2][1].VolumeRawExt);
}
[TestMethod]
public void GroupRecordsPerChannel_Ignores_Invalid_Records()
{
var input = new[]
{
Rec(0, 10, 100, OptoTelegramFlags.OK),
Rec(1, 11, 200, OptoTelegramFlags.InvalidTelegram),
Rec(2, 12, 300, OptoTelegramFlags.OK),
Rec(0, 13, 110, OptoTelegramFlags.SyncError),
Rec(1, 14, 220, OptoTelegramFlags.OK),
};
var grouped = (OptoTelegramRaw[][])InvokePrivate(
"GroupRecordsPerChannel",
(object)input, 0, input.Length - 1, input.Length);
Assert.AreEqual(1, grouped[0].Length);
Assert.AreEqual(1, grouped[1].Length);
Assert.AreEqual(1, grouped[2].Length);
Assert.AreEqual(100, grouped[0][0].VolumeRawExt);
Assert.AreEqual(220, grouped[1][0].VolumeRawExt);
Assert.AreEqual(300, grouped[2][0].VolumeRawExt);
}
[TestMethod]
public void GroupRecordsPerChannel_Swaps_Start_And_End_When_Reversed()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(0, 12, 120),
};
var grouped = (OptoTelegramRaw[][])InvokePrivate(
"GroupRecordsPerChannel",
(object)input, 2, 0, input.Length);
Assert.AreEqual(2, grouped[0].Length);
Assert.AreEqual(1, grouped[1].Length);
Assert.AreEqual(0, grouped[2].Length);
}
[TestMethod]
public void GroupRecordsPerChannel_Returns_Empty_For_Invalid_Range()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
};
var grouped = (OptoTelegramRaw[][])InvokePrivate(
"GroupRecordsPerChannel",
(object)input, -1, 1, input.Length);
Assert.AreEqual(3, grouped.Length);
Assert.IsTrue(grouped.All(x => x.Length == 0));
}
[TestMethod]
public void TimeDeltaPerChannel_Computes_First_Last_Deltas()
{
var grouped = new[]
{
new[]
{
Rec(0, 10, 100),
Rec(0, 18, 170),
Rec(0, 15, 120)
},
new[]
{
Rec(1, 20, 500),
Rec(1, 23, 530)
},
new[]
{
Rec(2, 30, 900)
}
};
var result = (ValueTuple<int, double?, double?>[])InvokePrivate(
"TimeDeltaPerChannel",
(object)grouped);
Assert.AreEqual(0, result[0].Item1);
Assert.AreEqual(5, result[0].Item2);
Assert.AreEqual(20, result[0].Item3);
Assert.AreEqual(1, result[1].Item1);
Assert.AreEqual(3, result[1].Item2);
Assert.AreEqual(30, result[1].Item3);
Assert.AreEqual(2, result[2].Item1);
Assert.IsNull(result[2].Item2);
Assert.IsNull(result[2].Item3);
}
[TestMethod]
public void VolumeStartPerChannel_Returns_Last_Seen_Value_Backward_From_Index()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(0, 12, 120),
Rec(2, 13, 300),
Rec(1, 14, 220),
};
var result = (double?[])InvokePrivate(
"VolumeStartPerChannel",
(object)input, input.Length, 4);
Assert.AreEqual(120, result[0]);
Assert.AreEqual(220, result[1]);
Assert.AreEqual(300, result[2]);
}
[TestMethod]
public void TimeStartPerChannel_Returns_Last_Seen_Time_Backward_From_Index()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(0, 12, 120),
Rec(2, 13, 300),
Rec(1, 14, 220),
};
var result = (double?[])InvokePrivate(
"TimeStartPerChannel",
(object)input, input.Length, 4);
Assert.AreEqual(12, result[0]);
Assert.AreEqual(14, result[1]);
Assert.AreEqual(13, result[2]);
}
[TestMethod]
public void VolumeEndPerChannel_Averages_Last_N_Samples_Per_Channel()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 120),
Rec(1, 14, 220),
Rec(2, 15, 330),
Rec(0, 16, 140),
Rec(1, 17, 240),
Rec(2, 18, 360),
};
var result = (double?[])InvokePrivate(
"VolumeEndPerChannel",
(object)input, input.Length, 8, 2);
Assert.AreEqual((140 + 120) / 2.0, result[0]);
Assert.AreEqual((240 + 220) / 2.0, result[1]);
Assert.AreEqual((360 + 330) / 2.0, result[2]);
}
[TestMethod]
public void TimeEndPerChannel_Averages_Last_N_Samples_Per_Channel()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 120),
Rec(1, 14, 220),
Rec(2, 15, 330),
Rec(0, 16, 140),
Rec(1, 17, 240),
Rec(2, 18, 360),
};
var result = (double?[])InvokePrivate(
"TimeEndPerChannel",
(object)input, input.Length, 8, 2);
Assert.AreEqual((16 + 13) / 2.0, result[0]);
Assert.AreEqual((17 + 14) / 2.0, result[1]);
Assert.AreEqual((18 + 15) / 2.0, result[2]);
}
[TestMethod]
public void VolumeStartPerChannel_Returns_All_Nulls_For_OutOfRange_Index()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
};
var result = (double?[])InvokePrivate(
"VolumeStartPerChannel",
(object)input, input.Length, -1);
Assert.IsNull(result[0]);
Assert.IsNull(result[1]);
Assert.IsNull(result[2]);
}
[TestMethod]
public void TimeEndPerChannel_Uses_One_Sample_When_SamplesPerChannel_Is_Zero()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 120),
Rec(1, 14, 220),
Rec(2, 15, 330),
};
var result = (double?[])InvokePrivate(
"TimeEndPerChannel",
(object)input, input.Length, 5, 0);
Assert.AreEqual(13, result[0]);
Assert.AreEqual(14, result[1]);
Assert.AreEqual(15, result[2]);
}
[TestMethod]
public void Precreated_OptoData_Private_Methods_Read_Correct_Values()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
Rec(0, 13, 120),
Rec(1, 14, 220),
Rec(2, 15, 330),
Rec(0, 16, 140),
Rec(1, 17, 240),
Rec(2, 18, 360),
};
SetPrivateField(_reader, "optoData", input);
SetPrivateField(_reader, "optoDataCount", input.Length);
_reader.TestStartTelegramIx = 5;
_reader.TestEndTelegramIx = 8;
Assert.AreEqual(input.Length, GetPrivateField<int>(_reader, "optoDataCount"));
var startVolumes = (double?[])InvokePrivate(
"VolumeStartPerChannel",
(object)input, input.Length, 5);
var endVolumes = (double?[])InvokePrivate(
"VolumeEndPerChannel",
(object)input, input.Length, 8, 2 * GenesisSmartReader.StartEndFilterSamplesCount2 + 1);
var startTimes = (double?[])InvokePrivate(
"TimeStartPerChannel",
(object)input, input.Length, 5);
var endTimes = (double?[])InvokePrivate(
"TimeEndPerChannel",
(object)input, input.Length, 8, 2 * GenesisSmartReader.StartEndFilterSamplesCount2 + 1);
Assert.AreEqual(120, startVolumes[0]);
Assert.AreEqual(220, startVolumes[1]);
Assert.AreEqual(330, startVolumes[2]);
Assert.AreEqual(13, startTimes[0]);
Assert.AreEqual(14, startTimes[1]);
Assert.AreEqual(15, startTimes[2]);
Assert.AreEqual((140 + 120 + 100) / 3.0, endVolumes[0]);
Assert.AreEqual((240 + 220 + 200) / 3.0, endVolumes[1]);
Assert.AreEqual((360 + 330 + 300) / 3.0, endVolumes[2]);
Assert.AreEqual((16 + 13 + 10) / 3.0, endTimes[0]);
Assert.AreEqual((17 + 14 + 11) / 3.0, endTimes[1]);
Assert.AreEqual((18 + 15 + 12) / 3.0, endTimes[2]);
}
[TestMethod]
public void Public_Channel_Properties_Default_To_Zero_When_NoSamples_Is_True()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 11, 200),
Rec(2, 12, 300),
};
SetPrivateField(_reader, "optoData", input);
SetPrivateField(_reader, "optoDataCount", input.Length);
_reader.TestStartTelegramIx = 5;
_reader.TestEndTelegramIx = 8;
Assert.IsTrue(_reader.NoSamples);
Assert.AreEqual(0, _reader.VolumeLtrStartCh1);
Assert.AreEqual(0, _reader.VolumeLtrStartCh2);
Assert.AreEqual(0, _reader.VolumeLtrStartCh3);
Assert.AreEqual(0, _reader.VolumeLtrEndCh1);
Assert.AreEqual(0, _reader.VolumeLtrEndCh2);
Assert.AreEqual(0, _reader.VolumeLtrEndCh3);
Assert.AreEqual(0, _reader.TimestampSecStartCh1);
Assert.AreEqual(0, _reader.TimestampSecStartCh2);
Assert.AreEqual(0, _reader.TimestampSecStartCh3);
Assert.AreEqual(0, _reader.TimestampSecEndCh1);
Assert.AreEqual(0, _reader.TimestampSecEndCh2);
Assert.AreEqual(0, _reader.TimestampSecEndCh3);
}
[TestMethod]
public void Copy_Should_Copy_From_Source_Record()
{
var source = Rec(2, 123.5, 456.7, OptoTelegramFlags.OK_TestEnd);
source.Counter = 77;
source.RefFlow = 9.5f;
source.FlowRaw = 111;
source.VolumeRaw = 222;
source.Impedance = 333;
source.CheckSum = 44;
var target = new OptoTelegramRaw();
target.Copy(source);
Assert.AreEqual(source.iChannel, target.iChannel);
Assert.AreEqual(source.TimestampExt, target.TimestampExt);
Assert.AreEqual(source.VolumeRawExt, target.VolumeRawExt);
Assert.AreEqual(source.Flags, target.Flags);
Assert.AreEqual(source.Counter, target.Counter);
Assert.AreEqual(source.RefFlow, target.RefFlow);
Assert.AreEqual(source.FlowRaw, target.FlowRaw);
Assert.AreEqual(source.VolumeRaw, target.VolumeRaw);
Assert.AreEqual(source.Impedance, target.Impedance);
Assert.AreEqual(source.CheckSum, target.CheckSum);
}
[TestMethod]
public void RecalculateVolumeAndTimeDeltaPerChannel_Returns_Recalculated_Output()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(0, 20, 200),
Rec(1, 10, 500),
Rec(1, 30, 560),
Rec(2, 10, 1000),
Rec(2, 15, 1050),
};
var result = (OptoTelegramRaw[][])InvokePrivate(
"RecalculateVolumeAndTimeDeltaPerChannel",
(object)input,
input.Length,
0,
input.Length - 1);
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Length);
for (int ch = 0; ch < 3; ch++)
{
Assert.IsNotNull(result[ch]);
Assert.AreEqual(2, result[ch].Length);
Assert.IsNotNull(result[ch][0]);
Assert.IsNotNull(result[ch][1]);
Assert.AreEqual(10, result[ch][0].TimestampExt, 0.0001);
Assert.AreEqual(30, result[ch][1].TimestampExt, 0.0001);
}
Assert.AreEqual(100, result[0][0].VolumeRawExt, 0.0001);
Assert.AreEqual(300, result[0][1].VolumeRawExt, 0.0001);
Assert.AreEqual(500, result[1][0].VolumeRawExt, 0.0001);
Assert.AreEqual(560, result[1][1].VolumeRawExt, 0.0001);
Assert.AreEqual(1000, result[2][0].VolumeRawExt, 0.0001);
Assert.AreEqual(1200, result[2][1].VolumeRawExt, 0.0001);
}
[TestMethod]
public void RecalculateVolumeAndTimeDeltaPerChannel_Returns_Null_When_No_Channel_Has_Two_Records()
{
var input = new[]
{
Rec(0, 10, 100),
Rec(1, 20, 200),
Rec(2, 30, 300),
};
var result = (OptoTelegramRaw[][])InvokePrivate(
"RecalculateVolumeAndTimeDeltaPerChannel",
(object)input,
input.Length,
0,
input.Length - 1);
Assert.IsNull(result);
}
}
}
@@ -0,0 +1,153 @@
using System;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class 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 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().");
// These values are populated only if DataStreamPostProcessing() ran.
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRaw, 1e-9,
"Expected post-processing to prepare raw start values.");
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRaw, 1e-9,
"Expected post-processing to prepare raw end values.");
}
[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();
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().");
// Should still be default because DataStreamPostProcessing() must NOT run.
Assert.AreEqual(0.0, reader.VolumeLtrStartRaw, 1e-9,
"Post-processing should not run when previous state was ProcessAndSave.");
Assert.AreEqual(0.0, reader.VolumeLtrEndRaw, 1e-9,
"Post-processing should not run when previous state was ProcessAndSave.");
}
[TestMethod]
public void RequestImmediateStop_ShouldNotChangeCalculateStoredDataState()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
SetPrivateField(reader, "dataStreamState", DataStreamState.CalculateStoredData);
SetPrivateField(reader, "startDataProcessing", true);
SetPrivateField(reader, "_stopQueueData", false);
reader.RequestImmediateStop();
var state = GetPrivateField<DataStreamState>(reader, "dataStreamState");
var stopQueueData = GetPrivateField<bool>(reader, "_stopQueueData");
var startDataProcessing = GetPrivateField<bool>(reader, "startDataProcessing");
Assert.AreEqual(DataStreamState.CalculateStoredData, state,
"RequestImmediateStop() must not destroy CalculateStoredData state.");
Assert.IsTrue(stopQueueData, "Immediate stop should stop queueing.");
Assert.IsFalse(startDataProcessing, "Immediate stop should disable active processing.");
}
}
}
@@ -1,5 +1,11 @@
using System;
using System.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;
@@ -39,6 +45,18 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
return (T)field.GetValue(target);
}
private static void InvokePrepareCalculatedChannelData(GenesisSmartReader reader)
{
var method = typeof(GenesisSmartReader).GetMethod(
"PrepareCalculatedChannelData",
BindingFlags.Instance | BindingFlags.NonPublic);
if (method == null)
throw new MissingMethodException(typeof(GenesisSmartReader).FullName, "PrepareCalculatedChannelData");
method.Invoke(reader, null);
}
private static void InitializeThreeChannelState(GenesisSmartReader reader)
{
SetPrivateField(reader, "volumeRawExtLast", new double[ChannelCount]);
@@ -53,6 +71,13 @@ 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;
}
[TestMethod]
public void Start_ShouldOpenPort_AndEnableProcessing()
@@ -65,7 +90,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
Assert.IsTrue(fake.IsOpen);
Assert.IsTrue(fake.OpenCalls >= 1);
Assert.AreEqual(1, fake.DiscardInCalls);
Assert.AreEqual(2, fake.DiscardInCalls);
Assert.AreEqual(2, fake.DiscardOutCalls);
}
[TestMethod]
@@ -79,11 +105,36 @@ 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();
Assert.IsFalse(fake.IsOpen);
Assert.AreEqual(2, fake.CloseCalls);
}
[TestMethod]
public void Run_ShouldCaptureStartTelegramIndex_WhenTimeReached()
@@ -105,36 +156,39 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
}
[TestMethod]
public void ProcessOptoLine_ShouldIncreaseOptoDataCount()
public void ProcessOptoLine_ShouldInsertMultipleTelegrams_WithCorrectChannels()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
var line =
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331";
reader.optoSerialPort = fake;
fake.Open();
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave);
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(1, optoDataCount);
bool reset;
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(3, optoDataCount);
Assert.AreEqual(0, optoData[0].IChannel());
Assert.AreEqual(1, optoData[1].IChannel());
Assert.AreEqual(2, optoData[2].IChannel());
}
[DataTestMethod]
[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_ShouldUpdateExpectedChannel(string line, int expectedChannelIndex)
{
var fake = new FakeSerialDriver();
@@ -146,7 +200,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.optoSerialPort = fake;
InitializeThreeChannelState(reader);
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave);
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast");
@@ -185,7 +239,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var ev = reader.Run();
Assert.AreEqual(Event.ReadRegisterDone, ev);
Assert.AreEqual(4.0, reader.WMVolume, 1E-6); // average of (3, 4, 5)
Assert.AreEqual(4.0, reader.WMVolume, 1E-6);
Assert.AreEqual(4000, reader.WMPulses);
}
@@ -204,16 +258,16 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var line =
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD";
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave);
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
int currentTelegramIx = GetPrivateField<int>(reader, "currentTelegramIx");
Assert.AreEqual(0, currentTelegramIx);
}
[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();
@@ -223,7 +277,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.Initialize();
reader.optoSerialPort = fake;
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave);
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast");
@@ -267,30 +321,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
}
[TestMethod]
public void ProcessOptoLine_ShouldInsertMultipleTelegrams_WithCorrectChannels()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ProcessOptoLine( "@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave);
reader.ProcessOptoLine( "@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave);
reader.ProcessOptoLine( "@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave);
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(3, optoDataCount);
Assert.AreEqual(0, optoData[0].IChannel());
Assert.AreEqual(1, optoData[1].IChannel());
Assert.AreEqual(2, optoData[2].IChannel());
}
[TestMethod]
public void VolumeLtrStart_And_VolumeLtrEnd_ShouldUsePerChannelAverages()
public void VolumeLtrStart_And_VolumeLtrEnd_ShouldUsePreparedCachedChannelData()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
@@ -298,14 +329,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
// Build 18 records: 6 groups of channels 0,1,2
// Start group average = (10 + 20 + 30)/3 = 20
// Last 5 per channel:
// ch0: 11,12,13,14,15 => avg 13
// ch1: 21,22,23,24,25 => avg 23
// ch2: 31,32,33,34,35 => avg 33
// final average = (13 + 23 + 33)/3 = 23
int idx = 0;
for (int group = 0; group < 6; group++)
{
@@ -315,11 +338,19 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
}
SetPrivateField(reader, "optoDataCount", 18);
reader.TestStartTelegramIx = 2; // first full 3-channel group
reader.TestEndTelegramIx = 17; // last record
reader.TestStartTelegramIx = 1;
reader.TestEndTelegramIx = 17;
Assert.AreEqual(20.0, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(23.0, reader.VolumeLtrEnd, 1e-9);
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(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);
}
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
@@ -332,7 +363,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
iChannel = channel
};
}
[TestMethod]
public void VolumeLtrEnd_ShouldAverageOnlyAvailableChannels()
{
@@ -344,7 +375,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
int idx = 0;
// only channels 0 and 1
for (int i = 0; i < 5; i++)
{
optoData[idx++] = CreateOptoRecord(0, 10 + i, 100 + i);
@@ -355,8 +385,375 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
reader.TestStartTelegramIx = 1;
reader.TestEndTelegramIx = idx - 1;
// ch0 avg = 12, ch1 avg = 22 => total avg = 17
Assert.AreEqual(17.0, reader.VolumeLtrEnd, 1e-9);
InvokePrepareCalculatedChannelData(reader);
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
Assert.AreEqual(19.0, reader.VolumeLtrEnd, 1e-9);
}
[TestMethod]
public void ProcessOptoLine_Block_f_h1_h2_h3_f_ShouldResetBuffersOnlyAfterLastF()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 1;
bool reset;
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen on the first @f.");
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after @h 1.");
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after @h 2.");
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen immediately after @h 3.");
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
//now some cycles must be runing, so reset must happen.
//Assert.IsTrue(reset, "Reset must happen after trailing @f that closes the h1/h2/h3 block.");
}
[TestMethod]
public void ReadOptoData_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");
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);
Assert.IsNotNull(readMethod);
for (int i = 0; i < 5; i++)
{
readMethod.Invoke(reader, new object[] { DataStreamState.ProcessAndSave });
}
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.");
}
[TestMethod]
public void ProcessOptoLine_LastF_AfterH3_ShouldRequestBufferReset()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 1;
bool reset;
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Opening @f must not reset buffers.");
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
Assert.IsTrue(reset, "Closing @f after @h 3 must request reset.");
}
[TestMethod]
public void ProcessOptoLine_LastF_AfterH3_ShouldNotRequestReset_WhenRepetitionCountIsGreaterThanOne()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 3;
bool reset;
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset);
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
Assert.IsFalse(reset, "Reset must not happen after the first completed block when repetition count is 3.");
}
[TestMethod]
public void ProcessOptoLine_ShouldResetOnlyAfterThirdCompletedBlock()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 3;
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);
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 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 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("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
if (repetition < 3)
Assert.IsFalse(reset, "Reset must not happen before third full block.");
else
Assert.IsTrue(reset, "Reset must happen on third full block.");
}
}
[TestMethod]
public void ProcessOptoLine_ShouldResetOnlyAfterFifthCompletedBlock()
{
var fake = new FakeSerialDriver();
fake.Open();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.optoSerialPort = fake;
reader.ResetAfterBlockRepetitions = 5;
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);
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 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 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("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
if (repetition < 5)
Assert.IsFalse(reset, "Reset must not happen before fifth full block.");
else
Assert.IsTrue(reset, "Reset must happen on fifth full block.");
}
}
[TestMethod]
public void ProcessingLoop_Should_Keep_FIFO_Order_For_Enqueued_Items()
{
var reader = new GenesisSmartReader();
var expected = Enumerable.Range(1, 200)
.Select(i => $"LINE_{i:D4}")
.ToList();
var actual = new List<string>();
var sync = new object();
var allProcessed = new CountdownEvent(expected.Count);
reader.TestProcessLineOverride = line => true;
EnableProcessingLoopForTests(reader);
reader.TestLineProcessed = line =>
{
lock (sync)
{
actual.Add(line);
}
allProcessed.Signal();
};
reader.StartProcessingLoop();
try
{
foreach (var line in expected)
{
reader.TestEnqueueLine(line);
}
bool completed = allProcessed.Wait(TimeSpan.FromSeconds(10));
Assert.IsTrue(completed, "Timed out waiting for all queued items to be processed.");
CollectionAssert.AreEqual(expected, actual,
"Dequeued/processed order does not match enqueued order.");
}
finally
{
reader.StopProcessingLoop();
allProcessed.Dispose();
}
}
[TestMethod]
public void ProcessingLoop_Should_Keep_FIFO_Order_With_Concurrent_Producers()
{
var reader = new GenesisSmartReader();
const int producerCount = 4;
const int itemsPerProducer = 100;
int totalItems = producerCount * itemsPerProducer;
var expected = new List<string>();
var processed = new List<string>();
var expectedLock = new object();
var processedLock = new object();
int sequence = 0;
var allProcessed = new CountdownEvent(totalItems);
try
{
reader.TestProcessLineOverride = line => true;
EnableProcessingLoopForTests(reader);
reader.TestLineProcessed = line =>
{
lock (processedLock)
{
processed.Add(line);
}
allProcessed.Signal();
};
reader.StartProcessingLoop();
var tasks = new List<Task>();
for (int p = 0; p < producerCount; p++)
{
int producerId = p;
tasks.Add(Task.Run(() =>
{
for (int i = 0; i < itemsPerProducer; i++)
{
int seq = Interlocked.Increment(ref sequence);
string line = string.Format("{0:D6}|P{1}|I{2:D4}", seq, producerId, i);
lock (expectedLock)
{
expected.Add(line);
reader.TestEnqueueLine(line);
}
}
}));
}
Task.WaitAll(tasks.ToArray());
bool completed = allProcessed.Wait(TimeSpan.FromSeconds(15));
Assert.IsTrue(completed, "Timed out waiting for all concurrent items to be processed.");
CollectionAssert.AreEqual(expected, processed,
"Processed order does not match actual enqueue order under concurrent producers.");
}
finally
{
reader.StopProcessingLoop();
allProcessed.Dispose();
}
}
[TestMethod]
public void PrepareCalculatedChannelData_SimulateMode_ReturnsExpectedFakeValues()
{
var sut = new GenesisSmartReader();
sut.PrepareCalculatedChannelDataSimulationForTest();
Assert.IsFalse(sut.NoSamples);
Assert.AreEqual(100.0, sut.VolumeLtrStartCh1, 0.0001);
Assert.AreEqual(105.0, sut.VolumeLtrStartCh2, 0.0001);
Assert.AreEqual(110.0, sut.VolumeLtrStartCh3, 0.0001);
Assert.AreEqual(150.0, sut.VolumeLtrEndCh1, 0.0001);
Assert.AreEqual(157.0, sut.VolumeLtrEndCh2, 0.0001);
Assert.AreEqual(164.0, sut.VolumeLtrEndCh3, 0.0001);
Assert.AreEqual(10.0, sut.TimestampSecStartCh1, 0.0001);
Assert.AreEqual(10.0, sut.TimestampSecStartCh2, 0.0001);
Assert.AreEqual(10.0, sut.TimestampSecStartCh3, 0.0001);
Assert.AreEqual(20.0, sut.TimestampSecEndCh1, 0.0001);
Assert.AreEqual(20.0, sut.TimestampSecEndCh2, 0.0001);
Assert.AreEqual(20.0, sut.TimestampSecEndCh3, 0.0001);
Assert.AreEqual(105.0, sut.VolumeLtrStartRaw, 0.0001);
Assert.AreEqual(157.0, sut.VolumeLtrEndRaw, 0.0001);
Assert.AreEqual(10.0, sut.TimestampSecStart, 0.0001);
Assert.AreEqual(20.0, sut.TimestampSecEnd, 0.0001);
}
[TestMethod]
public void METHOD()
{
}
}
}
@@ -0,0 +1,389 @@
using System;
using System.Reflection;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class GenesisSmartReaderThreadedReadTests
{
private const int ChannelCount = 3;
private static GenesisCfg CreateCfg()
{
var cfg = new GenesisCfg(null);
cfg.OptoComPortNr = 7;
cfg.RfidComPortNr = 8;
return cfg;
}
private static void SetPrivateField(object target, string fieldName, object value)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
field.SetValue(target, value);
}
private static T GetPrivateField<T>(object target, string fieldName)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
return (T)field.GetValue(target);
}
private static bool WaitUntil(Func<bool> condition, int timeoutMs = 1500, int pollMs = 20)
{
var start = Environment.TickCount;
while (Environment.TickCount - start < timeoutMs)
{
if (condition())
return true;
Thread.Sleep(pollMs);
}
return condition();
}
private static void InitializeThreeChannelState(GenesisSmartReader reader)
{
SetPrivateField(reader, "volumeRawExtLast", new double[ChannelCount]);
SetPrivateField(reader, "timestampExtLast", new double[ChannelCount]);
SetPrivateField(reader, "lastVolumeRaw", new double[ChannelCount]);
SetPrivateField(reader, "lastTimestamp", new double[ChannelCount]);
SetPrivateField(reader, "volumeLtr", new[] { 0.0, 0.0, 0.0 });
SetPrivateField(reader, "volumeLtr0", new[] { 0.0, 0.0, 0.0 });
SetPrivateField(reader, "timestampSec", new[] { 0.0, 0.0, 0.0 });
SetPrivateField(reader, "timestampSec0", new[] { 0.0, 0.0, 0.0 });
}
private static FieldInfo GetAnyField(object target, string fieldName)
{
var field = target.GetType().GetField(
fieldName,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
return field;
}
private static void SetField(object target, string fieldName, object value)
{
GetAnyField(target, fieldName).SetValue(target, value);
}
private static T GetField<T>(object target, string fieldName)
{
return (T)GetAnyField(target, fieldName).GetValue(target);
}
private static void WaitForStartupFlushToFinish(GenesisSmartReader reader, int timeoutMs = 3000)
{
bool finished = WaitUntil(() =>
{
return !GetPrivateField<bool>(reader, "_startupFlushActive");
}, timeoutMs);
Assert.IsTrue(finished, "Startup flush did not finish in time.");
}
private static void ForceProcessingEnabled(GenesisSmartReader reader)
{
SetField(reader, "_startupFlushActive", false);
SetField(reader, "_stopQueueData", false);
SetField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
SetField(reader, "startDataProcessing", true);
var signal = GetField<AutoResetEvent>(reader, "_queueSignal");
signal.Set();
}
private static void EnqueueIncomingLine(GenesisSmartReader reader, string line)
{
var queue = GetField<System.Collections.Concurrent.ConcurrentQueue<(DateTime Timestamp, string Line)>>(
reader, "_receivedLines");
var signal = GetField<AutoResetEvent>(reader, "_queueSignal");
queue.Enqueue((DateTime.UtcNow, line));
signal.Set();
}
private static void InstallSuccessfulProcessingOverride(GenesisSmartReader reader)
{
Func<string, bool> handler = line =>
{
var optoData = GetField<OptoTelegramRaw[]>(reader, "optoData");
int count = GetField<int>(reader, "optoDataCount");
optoData[count].Counter = count;
optoData[count].SetFlags(OptoTelegramFlags.OK);
SetField(reader, "optoDataCount", count + 1);
return true;
};
SetField(reader, "TestProcessLineOverride", handler);
}
[TestMethod]
public void Start_ShouldOpenPort_AndStartBackgroundReadLoop()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.Start();
Assert.IsTrue(fake.IsOpen);
Assert.IsTrue(fake.OpenCalls >= 1);
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
Assert.IsNotNull(readLoopTask);
}
[TestMethod]
public void ProcessingLoop_ShouldConsumeQueuedLine()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
ForceProcessingEnabled(reader);
InstallSuccessfulProcessingOverride(reader);
EnqueueIncomingLine(reader, "TEST_LINE_1");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetField<int>(reader, "optoDataCount");
return optoDataCount == 1;
}, 3000);
Assert.IsTrue(processed, "Expected processing loop to consume queued line.");
int finalCount = GetField<int>(reader, "optoDataCount");
Assert.AreEqual(1, finalCount);
}
[TestMethod]
public void ProcessingLoop_ShouldConsumeQueuedLine_AndInsertTelegram()
{
var fake = new FakeSerialDriver();
fake.Encoding = System.Text.Encoding.ASCII;
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
ForceProcessingEnabled(reader);
EnqueueIncomingLine(reader,
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(processed, "Expected processing loop to consume queued line.");
int finalCount = GetPrivateField<int>(reader, "optoDataCount");
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
Assert.AreEqual(1, finalCount);
Assert.AreEqual(1, optoData[0].IChannel());
}
[TestMethod]
public void ProcessingLoop_ShouldConsumeAllQueuedLines()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
ForceProcessingEnabled(reader);
InstallSuccessfulProcessingOverride(reader);
EnqueueIncomingLine(reader, "TEST_LINE_1");
EnqueueIncomingLine(reader, "TEST_LINE_2");
EnqueueIncomingLine(reader, "TEST_LINE_3");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetField<int>(reader, "optoDataCount");
return optoDataCount == 3;
}, 3000);
Assert.IsTrue(processed, "Expected all queued lines to be processed.");
int finalCount = GetField<int>(reader, "optoDataCount");
var queueAfter = GetField<System.Collections.Concurrent.ConcurrentQueue<(DateTime Timestamp, string Line)>>(
reader, "_receivedLines");
Assert.AreEqual(3, finalCount);
Assert.IsTrue(queueAfter.IsEmpty);
}
[TestMethod]
public void IncomingLines_ShouldBeProcessedByBackgroundProcessingLoop()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
WaitForStartupFlushToFinish(reader);
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(processed, "Expected background processing loop to process line.");
}
[TestMethod]
public void StopDataStreamProcessing_ShouldStopReadAndProcessingLoops()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
reader.StopDataStreamProcessing();
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
var readLoopCts = GetPrivateField<CancellationTokenSource>(reader, "_readLoopCts");
var processLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_processLoopTask");
var processLoopCts = GetPrivateField<CancellationTokenSource>(reader, "_processLoopCts");
Assert.IsNull(readLoopTask);
Assert.IsNull(readLoopCts);
Assert.IsNull(processLoopTask);
Assert.IsNull(processLoopCts);
}
[TestMethod]
public void IncomingLine_AfterStop_ShouldNotBeProcessed()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
reader.StopDataStreamProcessing();
int before = GetPrivateField<int>(reader, "optoDataCount");
fake.EnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
Thread.Sleep(300);
int after = GetPrivateField<int>(reader, "optoDataCount");
Assert.AreEqual(before, after, "No new telegram should be processed after stop.");
}
[TestMethod]
public void InvalidLine_ShouldNotCrashProcessingLoop()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
WaitForStartupFlushToFinish(reader);
fake.EnqueueLine("INVALID_LINE_THAT_WILL_NOT_PARSE");
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(processed, "Processing loop should survive invalid input and continue.");
}
[TestMethod]
public void StartStop_StartAgain_ShouldStillProcessData()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
InitializeThreeChannelState(reader);
reader.Start();
reader.StopDataStreamProcessing();
reader.Start();
WaitForStartupFlushToFinish(reader);
fake.EnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
bool processed = WaitUntil(() =>
{
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
return optoDataCount >= 1;
}, 3000);
Assert.IsTrue(processed, "Reader should still process data after restart.");
}
[TestMethod]
public void Start_ShouldOpenPort_AndStartBothBackgroundLoops()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
reader.Start();
Assert.IsTrue(fake.IsOpen);
Assert.IsTrue(fake.OpenCalls >= 1);
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
var processLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_processLoopTask");
Assert.IsNotNull(readLoopTask);
Assert.IsNotNull(processLoopTask);
}
}
}
@@ -0,0 +1,195 @@
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
{
[TestMethod]
public void GetQ3Calibration_ShouldReturnInvalid_WhenRawDataIsNull()
{
var sut = new GenesisSmartReader();
InitializeReaderForQ3Test(sut);
SetPrivateField(sut, "_rawStartEndByChannel", null);
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 15625.0);
Assert.IsFalse(sut.Q3CalibValid);
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
}
[TestMethod]
public void GetQ3Calibration_ShouldReturnInvalid_WhenNoSamplesConditionIsTrue()
{
var sut = new GenesisSmartReader();
InitializeReaderForQ3Test(sut);
SetPrivateField(sut, "_rawStartEndByChannel", CreateMinimalStartEndData());
Assert.IsTrue(sut.NoSamples, "Expected NoSamples to be true for this test.");
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 15625.0);
Assert.IsFalse(sut.Q3CalibValid);
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
}
[TestMethod]
public void GetQ3Calibration_ShouldReturnInvalid_WhenRefVolumeIsZero()
{
var sut = new GenesisSmartReader();
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
sut.GetQ3Calibration(refVolume: 0.0, refTime: 120.0, initCalibFactor: 15625.0);
Assert.IsFalse(sut.Q3CalibValid);
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
}
[TestMethod]
public void GetQ3Calibration_ShouldReturnInvalid_WhenRefTimeIsZero()
{
var sut = new GenesisSmartReader();
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 0.0, initCalibFactor: 15625.0);
Assert.IsFalse(sut.Q3CalibValid);
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
}
[TestMethod]
public void GetQ3Calibration_ShouldReturnInvalid_WhenInitCalibFactorIsZero()
{
var sut = new GenesisSmartReader();
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 0.0);
Assert.IsFalse(sut.Q3CalibValid);
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
}
[TestMethod]
public void GetQ3Calibration_ShouldProduceNonNegativeResult_WithPreparedSimulationData()
{
var sut = new GenesisSmartReader();
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
sut.GetQ3Calibration(refVolume: 1.0, refTime: 120.0, initCalibFactor: 15625.0);
Assert.IsTrue(sut.Q3CalibValue >= 0.0);
}
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),
CreateRecord(15625.0, 120.0)
},
new OptoTelegramRaw[2],
new OptoTelegramRaw[2]
};
}
private static OptoTelegramRaw CreateRecord(double volumeRawExt, double timestampExt)
{
return new OptoTelegramRaw
{
VolumeRawExt = volumeRawExt,
TimestampExt = timestampExt
};
}
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.");
}
}
}
+5
View File
@@ -108,8 +108,13 @@
<Compile Include="Rig\Output\FileWriters\Enhanced\WriterTest.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\common\OptoTelegramRawTest.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\FakeSerialDriver.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisReaderTests.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderChannelAveragingTests.cs" />
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\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" />