Restore Genesis communication and Q3 calibration from special branches

A – Registration and execution
- Register GenesisCommunication Factory in the component list.
- Separate the Genesis form and sequence from iPerl communication.

B – Communication activities
- Restore initialization, connection, PCB reading, password and login.
- Include grouped login, mode switching and disconnection.
- Support processing up to 10 slots.

C1 – Input calibration factors
- Read three factors from Water meters / Text1–Text3.
- Validate integer values in the range 1–65535.
- Preserve the default of 15625 when all three fields are empty.

D – Q3 calibration
- Connect the Prepare Q3 → measurement → Write Q3 workflow.
- Add channel processing to FlyingStart and FlyingStartMassCollection.
- Reset previous measurement data and validate calculated factors.
- Mark factors as stored only after StoreCalibration succeeds.

E – Results and database
- Store calibration factors separately for each meter and channel.
- Add result entities, mappings and Q3 data.
- Extend DB.cs / EnsureSchema to create and update the schema.
- Preserve compatibility with the existing binary format.

Validation:
- Debug build and 18 tests passed.
- Simulated communication runs follow the same activity sequence.
- The complete Q3 workflow has not yet been verified on hardware.

Known limitation:
- An inherited mismatch in simulated responses and error propagation
  can produce an incorrect OK result; this change does not fix it.
This commit is contained in:
2026-09-09 15:04:49 +02:00
parent 88b8bc5534
commit 52bd0d01ce
46 changed files with 12378 additions and 716 deletions
@@ -0,0 +1,40 @@
///
/// Copyright (c) 2015-2019 Sensus Metering Systems
/// Author: Milan Hanajík
///
using System;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBF.Rig.TestMethods.GenesisCommunication
{
public class CommCompletedEventArgs : EventArgs
{
public int ThreadId;
public int WMNr0; /// 0-based water meter position
public GenesisSmartReader Ihead;
public Results.Entities.WaterMeter Wm;
public string CommMessage;
public CommErr CommErr;
public CommCompletedEventArgs(int threadId, int wmNr0, GenesisSmartReader ihead, Results.Entities.WaterMeter wm, string commMessage, CommErr commErr)
{
this.ThreadId = threadId;
this.WMNr0 = wmNr0;
this.Ihead = ihead;
this.Wm = wm;
this.CommMessage = commMessage;
this.CommErr = commErr;
}
public override string ToString()
{
return string.Format("Thread={0} WMNr0={1} IHead={2} WM={3} CommMsg={4} CommErr={5}",
ThreadId,
WMNr0,
(Ihead != null) ? Ihead.Name : "null",
(Wm != null) ? Wm.WMPosition : -1,
(CommMessage != null) ? CommMessage : "null",
CommErr);
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Globalization;
namespace TBF.Rig.TestMethods.GenesisCommunication
{
public static class GenesisCalibrationFactors
{
// Preserve the special branch default only when no channel has been configured.
public const ushort DefaultFactor = 15625;
public static bool TryParse(string text1, string text2, string text3,
out double[] factors, out string error)
{
factors = null;
error = null;
var text = new[] { text1, text2, text3 };
if (Array.TrueForAll(text, string.IsNullOrWhiteSpace))
{
factors = new double[] { DefaultFactor, DefaultFactor, DefaultFactor };
return true;
}
var parsed = new double[3];
for (int channel = 0; channel < parsed.Length; channel++)
{
ushort value;
if (!ushort.TryParse(text[channel]?.Trim(), NumberStyles.None,
CultureInfo.InvariantCulture, out value) || value == 0)
{
error = "Genesis calibration Text" + (channel + 1) + " must be an integer from 1 to 65535. Set all three channels.";
return false;
}
parsed[channel] = value;
}
factors = parsed;
return true;
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -32,15 +32,15 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
System.Windows.Forms.Form modelessDlg;
///
delegate void iPerlCommFormDlgt(GenesisCommunicationSeq myRef, iPerlCommunication.TestMethod method, Test test, iPerlCommunicationParams testParams);
delegate void iPerlCommFormDlgt(GenesisCommunicationSeq myRef, TestMethod method, Test test, iPerlCommunicationParams testParams);
///
void OpenIPerlCommForm(GenesisCommunicationSeq myRef, iPerlCommunication.TestMethod method, Test test, iPerlCommunicationParams testParams)
void OpenIPerlCommForm(GenesisCommunicationSeq myRef, TestMethod method, Test test, iPerlCommunicationParams testParams)
{
//TODO solve this wia SmartCommunicationForm
throw new NotImplementedException();
//myRef.modelessDlg = new iPerlCommunicationForm(method, test, testParams);
//myRef.modelessDlg.Show();
myRef.modelessDlg = new GenesisCommunicationForm(method, test, testParams);
myRef.modelessDlg.Show();
}
@@ -72,7 +72,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
processDataLoggingOp = new TBF.Rig.Operations.ProcessDataLoggingOp(processDataLogger, this, false);
string cmd;
if (testParams.Activity.ToLower().Equals(cmd = iPerlCommunicationForm.GetDefaultQ2CorrectionsStr.ToLower()))
if (testParams.Activity.ToLower().Equals(cmd = GenesisCommunicationForm.GetDefaultQ2CorrectionsStr.ToLower()))
{
TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 0, 30, 0, 30, 0, 0 });
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.JustStarted));
@@ -33,13 +33,13 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
/// Private parameterless constructor invoked by all other (public) constructors
TestMethodCfg()
{
Name = "SmartCommunication";
Name = "SmartCommunicationGenesis";
ParentName = string.Empty;
CommTimeout = 1800; /// ms
MaxCommRetries = 4;
WaitTimeAfterFailure = 2200;
PassThroughWaitTime = 1500;
NrThreads = 2; /// 1, 2 or 4 threads
NrThreads = 10; /// 1, 2 or 4 threads
IperlCheckErrorsToStop = 10;
MciTimeoutMs = 4000; // ms, NFC interface
BaudRate = 57600; // NFC Interface
@@ -28,6 +28,14 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
public TestMethodCfgCtrl()
{
InitializeComponent();
dfltQ2corrFactorsGroupBox.Visible = false;
useWebServiceGroupBox.Visible = false;
var factorHint = new System.Windows.Forms.Label
{
AutoSize = true, Location = dfltQ2corrFactorsGroupBox.Location,
Text = "Q3 factors: Water meters / Text1, Text2, Text3 (CH1, CH2, CH3)."
};
Controls.Add(factorHint);
}
private void EntryFormCfgCtrl_Load(object sender, EventArgs e)
@@ -116,10 +124,10 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Comm. timeout' should be in range 0 .. 5000";
}
if (!int.TryParse(nrThreadsTextBox.Text, out dummy) || (dummy != 1 && dummy != 2 && dummy != 4))
if (!int.TryParse(nrThreadsTextBox.Text, out dummy) || (dummy < 1 || dummy > 10))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Nr. threads' should be 1, 2 or 4";
message += Environment.NewLine + "'Nr. threads' should be in range 1 .. 10";
}
if (!int.TryParse(iperlCheckErrorsToStopTextBox.Text, out dummy) || ((dummy < 1) && (dummy > 40)))
{
@@ -10,7 +10,6 @@ using Config.Entities;
using TBF.Resources;
using TBF.Rig.Generic;
using TBF.Rig.TestMethods.iPerlCommunication;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
namespace TBF.Rig.TestMethods.GenesisCommunication
{
@@ -31,7 +30,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
public override void InitializeAll()
{
Activity = iPerlCommunicationForm.ReadConfigurationStr;
Activity = GenesisCommunicationForm.SlotInitializeStr;
SimultWithPrevious = false;
SimultWithNext = false;
}
@@ -51,62 +50,79 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
if (i == 0)
{
var retVal = new List<string>();
retVal.Add(iPerlCommunicationForm.ReadConfigurationStr);
retVal.Add(iPerlCommunicationConstants.ReadAdditionalCommonParametersStr);
retVal.Add(iPerlCommunicationForm.ReadSerialNrStr);
retVal.Add(string.Format("{0} A0", iPerlCommunicationForm.SetTestModeStr));
retVal.Add(string.Format("{0} A4", iPerlCommunicationForm.SetTestModeStr));
retVal.Add(iPerlCommunicationForm.ReadCalibrationStr);
retVal.Add(iPerlCommunicationForm.ReadCalibrationV4Str);
retVal.Add(iPerlCommunicationForm.NormalizeCalibrationFactorStr);
retVal.Add(iPerlCommunicationForm.NormalizeCalibrationV4FactorsStr);
retVal.Add(iPerlCommunicationForm.GetDefaultQ2CorrectionsStr);
retVal.Add(iPerlCommunicationForm.ReadQ2CorrectionStr);
retVal.Add(iPerlCommunicationForm.ResetQ2CorrectionStr);
retVal.Add(iPerlCommunicationForm.WriteDefaultQ2CorrectionsStr);
retVal.Add(iPerlCommunicationForm.InitOrReadQ2CorrectionsStr);
retVal.Add(iPerlCommunicationForm.WriteCalibrationFactorStr);
retVal.Add(iPerlCommunicationForm.WriteCalibrationV4FactorsStr);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionStr);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionAltStr);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionGreeceStr);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionRLStr);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionLRStr);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionIncl05Str);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionAltIncl05Str);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionPlusIncl05Str);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionPlusAltIncl05Str);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionGreeceIncl05Str);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionRLIncl05Str);
retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionLRIncl05Str);
retVal.Add(iPerlCommunicationSeq.Q2correctedFromCmd + "Qx");
retVal.Add(iPerlCommunicationSeq.StrictQ2ErrorCheckStr + "Qx");
retVal.Add(iPerlCommunicationSeq.Q2correctionCheckCmd);
retVal.Add(iPerlCommunicationSeq.IperlCheckCmd);
retVal.Add(iPerlCommunicationForm.UpdateBothQ2FactorsTestRLOnlyStr);
retVal.Add(iPerlCommunicationForm.UpdateBothQ2FactorsTestLROnlyStr);
retVal.Add(iPerlCommunicationForm.UpdateQ2CorrectionsStr);
retVal.Add(iPerlCommunicationForm.ConditnlUpdateQ2CorrectionsStr);
retVal.Add(iPerlCommunicationForm.ConditnlUpdateQ2CorrRLStr);
retVal.Add(iPerlCommunicationForm.ConditnlUpdateQ2CorrLRStr);
retVal.Add("Q2 corrected from Q2adj");
retVal.Add("Q2 correction check Q2bc Q2ac");
retVal.Add(iPerlCommunicationForm.SetActiveModeStr);
retVal.Add(iPerlCommunicationForm.SetIdleModeStr);
retVal.Add("---");
retVal.Add(iPerlCommunicationForm.Reset2HzCorrectionStr);
retVal.Add(iPerlCommunicationForm.Write2HzCorrectionStr);
retVal.Add(iPerlCommunicationForm.DewaReworkRLStr);
retVal.Add(iPerlCommunicationForm.DewaReworkLRStr);
retVal.Add(iPerlCommunicationForm.StartTestingSealedMetersStr);
retVal.Add(iPerlCommunicationForm.EndTestingSealedMetersStr);
retVal.Add(string.Format("{0} if enabled", iPerlCommunicationForm.ReadConfigurationStr));
retVal.Add(string.Format("{0} 80", iPerlCommunicationForm.SetTestModeStr));
retVal.Add("iPerl_check prevWorkStep direction q2factors");
for (iPerlCommunication.ConditionID id = iPerlCommunication.ConditionID.A; id < iPerlCommunication.ConditionID.Count; id++)
{
retVal.Add(string.Format(iPerlCommunication.SequenceConditionOp.ConditionNameFmt, id));
}
retVal.Add(GenesisCommunicationForm.SlotInitializeStr);
retVal.Add(GenesisCommunicationForm.SlotUpdateStr);
retVal.Add(GenesisCommunicationForm.SlotConnectStr);
retVal.Add(GenesisCommunicationForm.SlotPCBSlotStr);
retVal.Add(GenesisCommunicationForm.SlotSetPasswordStr);
retVal.Add(GenesisCommunicationForm.SlotLoginStr);
retVal.Add(GenesisCommunicationForm.SlotGroupedLoginStr);
retVal.Add(GenesisCommunicationForm.SlotSetTestModeStr);
retVal.Add(GenesisCommunicationForm.SlotSetActiveModeStr);
retVal.Add(GenesisCommunicationForm.SlotDisconnectStr);
retVal.Add(GenesisCommunicationForm.PrepareSlotQ3CalibrationStr);
retVal.Add(GenesisCommunicationForm.WriteSlotQ3CalibrationStr);
retVal.Add(GenesisCommunicationForm.CheckMeterPrepareStr);
retVal.Add(GenesisCommunicationForm.HoldSlotStr);
// retVal.Add(GenesisCommunicationForm.ReadConfigurationStr);
// retVal.Add(GenesisCommunicationForm.ReadSerialNrStr);
// retVal.Add(string.Format("{0} A0", GenesisCommunicationForm.SetTestModeStr));
// retVal.Add(string.Format("{0} A4", GenesisCommunicationForm.SetTestModeStr));
// retVal.Add(GenesisCommunicationForm.ReadCalibrationStr);
// retVal.Add(GenesisCommunicationForm.ReadCalibrationV4Str);
// retVal.Add(GenesisCommunicationForm.NormalizeCalibrationFactorStr);
// retVal.Add(GenesisCommunicationForm.NormalizeCalibrationV4FactorsStr);
// retVal.Add(GenesisCommunicationForm.GetDefaultQ2CorrectionsStr);
// retVal.Add(GenesisCommunicationForm.ReadQ2CorrectionStr);
// retVal.Add(GenesisCommunicationForm.ResetQ2CorrectionStr);
// retVal.Add(GenesisCommunicationForm.WriteDefaultQ2CorrectionsStr);
// retVal.Add(GenesisCommunicationForm.InitOrReadQ2CorrectionsStr);
// retVal.Add(GenesisCommunicationForm.WriteCalibrationFactorStr);
// retVal.Add(GenesisCommunicationForm.WriteCalibrationV4FactorsStr);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionStr);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionAltStr);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionGreeceStr);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionRLStr);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionLRStr);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionIncl05Str);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionAltIncl05Str);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionPlusIncl05Str);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionPlusAltIncl05Str);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionGreeceIncl05Str);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionRLIncl05Str);
// retVal.Add(GenesisCommunicationForm.WriteQ2CorrectionLRIncl05Str);
// retVal.Add(iPerlCommunicationSeq.Q2correctedFromCmd + "Qx");
// retVal.Add(iPerlCommunicationSeq.StrictQ2ErrorCheckStr + "Qx");
// retVal.Add(iPerlCommunicationSeq.Q2correctionCheckCmd);
// retVal.Add(iPerlCommunicationSeq.IperlCheckCmd);
// retVal.Add(GenesisCommunicationForm.UpdateBothQ2FactorsTestRLOnlyStr);
// retVal.Add(GenesisCommunicationForm.UpdateBothQ2FactorsTestLROnlyStr);
// retVal.Add(GenesisCommunicationForm.UpdateQ2CorrectionsStr);
// retVal.Add(GenesisCommunicationForm.ConditnlUpdateQ2CorrectionsStr);
// retVal.Add(GenesisCommunicationForm.ConditnlUpdateQ2CorrRLStr);
// retVal.Add(GenesisCommunicationForm.ConditnlUpdateQ2CorrLRStr);
// retVal.Add("Q2 corrected from Q2adj");
// retVal.Add("Q2 correction check Q2bc Q2ac");
// retVal.Add(GenesisCommunicationForm.SetActiveModeStr);
// retVal.Add(GenesisCommunicationForm.SetIdleModeStr);
// retVal.Add("---");
// retVal.Add(GenesisCommunicationForm.Reset2HzCorrectionStr);
// retVal.Add(GenesisCommunicationForm.Write2HzCorrectionStr);
// retVal.Add(GenesisCommunicationForm.DewaReworkRLStr);
// retVal.Add(GenesisCommunicationForm.DewaReworkLRStr);
// retVal.Add(GenesisCommunicationForm.StartTestingSealedMetersStr);
// retVal.Add(GenesisCommunicationForm.EndTestingSealedMetersStr);
// retVal.Add(string.Format("{0} if enabled", GenesisCommunicationForm.ReadConfigurationStr));
// retVal.Add(string.Format("{0} 80", GenesisCommunicationForm.SetTestModeStr));
// retVal.Add("iPerl_check prevWorkStep direction q2factors");
// for (ConditionID id = ConditionID.A; id < ConditionID.Count; id++)
// {
// retVal.Add(string.Format(SequenceConditionOp.ConditionNameFmt, id));
// }
return retVal;
}