tbf/TBFTests/GenesisRecoveryTests.cs
Michal Buzik 52bd0d01ce 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.
2026-09-09 15:04:49 +02:00

180 lines
9.1 KiB
C#

using System;
using System.Data.SQLite;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Results.Entities;
using Results.Entities.helpers;
using GenesisCalibrationFactors = TBF.Rig.TestMethods.GenesisCommunication.GenesisCalibrationFactors;
namespace TBFTests
{
[TestClass]
public class GenesisRecoveryTests
{
[TestMethod]
public void TextFieldsPreserveConfiguredFactors()
{
double[] factors; string error;
Assert.IsTrue(GenesisCalibrationFactors.TryParse("17969", "17969", "17969", out factors, out error));
CollectionAssert.AreEqual(new double[] { 17969, 17969, 17969 }, factors);
Assert.IsTrue(GenesisCalibrationFactors.TryParse(" 17969 ", "18000", "19000", out factors, out error));
CollectionAssert.AreEqual(new double[] { 17969, 18000, 19000 }, factors);
}
[TestMethod]
public void IncompleteOrInvalidFactorsAreRejectedBeforeWriting()
{
foreach (var invalid in new[] { null, "", "0", "-1", "65536", "NaN", "1.5", "bad" })
{
double[] factors; string error;
Assert.IsFalse(GenesisCalibrationFactors.TryParse("17969", invalid, "17969", out factors, out error));
Assert.IsNull(factors);
StringAssert.Contains(error, "Text2");
}
}
[TestMethod]
public void EmptyLegacyConfigurationKeepsDefault()
{
double[] factors; string error;
Assert.IsTrue(GenesisCalibrationFactors.TryParse(null, " ", "", out factors, out error));
CollectionAssert.AreEqual(new double[] { 15625, 15625, 15625 }, factors);
}
[TestMethod]
public void MultipleMetersHaveIndependentStableChannelRecords()
{
var test = new TestRslt();
var first = new WaterMeter { WMPosition = 1 };
var second = new WaterMeter { WMPosition = 2 };
var a = test.GetCalibrationFactors(first);
var b = test.GetCalibrationFactors(second);
a[0].BaseCalibFactor = 17969;
b[0].BaseCalibFactor = 19000;
Assert.AreEqual(6, test.CalibFactorResultsToSave.Count);
Assert.AreSame(a[0], test.GetCalibrationFactors(first)[0]);
Assert.AreEqual(17969, a[0].BaseCalibFactor);
Assert.AreEqual(19000, b[0].BaseCalibFactor);
for (int i = 0; i < 3; i++) Assert.AreEqual(i + 1, b[i].CalibFactorIndex);
}
[TestMethod]
public void GenesisFactoryAndProcedureActivitiesAreAvailable()
{
var factory = TBF.Rig.TbfComponents.CmpntFactoryFromClassName("TestMethods.GenesisCommunication");
Assert.IsNotNull(factory);
var config = factory.DefaultConfig() as TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfg;
Assert.IsNotNull(config);
Assert.AreEqual(10, config.NrThreads);
var parameters = new TBF.Rig.TestMethods.GenesisCommunication.iPerlCommunicationParams(true);
CollectionAssert.Contains(new System.Collections.Generic.List<string>(parameters.ParamValues(0)), "Prepare Q3 Calibration Slot");
Assert.IsNotNull(TBF.Rig.TbfComponents.CmpntFactoryFromClassName("TestMethods.iPerlCommunication"));
}
[TestMethod]
public void MappedCalibrationRecordsRoundTripForTwoMeters()
{
string path = Path.Combine(Path.GetTempPath(), "genesis-roundtrip-" + Guid.NewGuid() + ".sqlite");
var previousType = Results.DB.DbType;
var previousConnection = Results.DB.ConnectionString;
var previousFactory = Results.DB.SessionFactory;
try
{
Results.DB.DbType = Common.DBType.SQLite;
Results.DB.ConnectionString = path;
using (var factory = Results.DB.CreateSessionFactory(true))
{
int testId;
using (var session = factory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var test = new TestRslt();
session.Save(test);
testId = test.Id;
for (int meter = 1; meter <= 2; meter++)
foreach (var factor in test.GetCalibrationFactors(new WaterMeter { WMPosition = meter }))
{
factor.BaseCalibFactor = 17969 + meter;
factor.CalculatedCalibFactor = 18000 + meter;
factor.Stored = true;
factor.IsCalibFactorValid = true;
session.Save(factor);
}
transaction.Commit();
}
DatabaseMigrationHelper.EnsureSchema(Common.DBType.SQLite, path);
using (var session = factory.OpenSession())
{
var rows = TestRsltCalibFactorHelper.GetByTestRsltId(session, testId);
Assert.AreEqual(6, rows.Count);
foreach (var row in rows)
{
Assert.AreEqual(17969 + row.WaterMeterPosition, row.BaseCalibFactor);
Assert.IsTrue(row.Stored);
Assert.IsTrue(row.IsCalibFactorValid);
}
}
}
}
finally
{
Results.DB.DbType = previousType;
Results.DB.ConnectionString = previousConnection;
Results.DB.SessionFactory = previousFactory;
SQLiteConnection.ClearAllPools();
if (File.Exists(path)) File.Delete(path);
}
}
// This test creates a temporary schema at runtime; no IDE data source exists.
// ReSharper disable SqlResolve
[TestMethod]
public void SQLiteEnsureCreatesAndUpgradesWithoutLosingExistingData()
{
string path = Path.Combine(Path.GetTempPath(), "genesis-migration-" + Guid.NewGuid() + ".sqlite");
try
{
using (var connection = new SQLiteConnection("Data Source=" + path))
{
connection.Open();
Execute(connection, "CREATE TABLE WaterMeterData(Id INTEGER PRIMARY KEY)");
Execute(connection, "CREATE TABLE WaterMeter(Id INTEGER PRIMARY KEY, CalibFactorNominal REAL NOT NULL DEFAULT 4096)");
Execute(connection, "CREATE TABLE MeterTestRslt(Id INTEGER PRIMARY KEY, FlipMode INT NULL)");
Execute(connection, "INSERT INTO WaterMeter(Id, CalibFactorNominal) VALUES(1, 17969)");
Execute(connection, "INSERT INTO MeterTestRslt(Id, FlipMode) VALUES(1, 7)");
// Simulate a partially migrated database from a previous special build.
Execute(connection, "CREATE TABLE TestRsltCalibFactor(Id INTEGER PRIMARY KEY, TestRsltId INT)");
Execute(connection, "INSERT INTO TestRsltCalibFactor(Id, TestRsltId) VALUES(1, 12)");
}
DatabaseMigrationHelper.EnsureSchema(Common.DBType.SQLite, path);
DatabaseMigrationHelper.EnsureSchema(Common.DBType.SQLite, path);
using (var connection = new SQLiteConnection("Data Source=" + path))
{
connection.Open();
Assert.AreEqual(17969d, Convert.ToDouble(Scalar(connection, "SELECT CalibFactorNominal FROM WaterMeter WHERE Id=1")));
Assert.AreEqual(7L, Convert.ToInt64(Scalar(connection, "SELECT FlipMode FROM MeterTestRslt WHERE Id=1")));
Assert.AreEqual(0L, Convert.ToInt64(Scalar(connection, "SELECT Q3Channel FROM WaterMeter WHERE Id=1")));
Assert.AreEqual(12L, Convert.ToInt64(Scalar(connection, "SELECT TestRsltId FROM TestRsltCalibFactor WHERE Id=1")));
Execute(connection, "UPDATE TestRsltCalibFactor SET WaterMeterPosition=2, CalibFactorIndex=3, BaseCalibFactor=17969, IsCalibFactorValid=1, Stored=1 WHERE Id=1");
Assert.AreEqual(17969L, Convert.ToInt64(Scalar(connection, "SELECT BaseCalibFactor FROM TestRsltCalibFactor WHERE WaterMeterPosition=2 AND CalibFactorIndex=3")));
Assert.AreEqual(0L, Convert.ToInt64(Scalar(connection, "SELECT COUNT(*) FROM MeterTestCalibFactorRslt")));
}
}
finally { SQLiteConnection.ClearAllPools(); if (File.Exists(path)) File.Delete(path); }
}
// ReSharper restore SqlResolve
private static void Execute(SQLiteConnection connection, string sql)
{
using (var command = connection.CreateCommand()) { command.CommandText = sql; command.ExecuteNonQuery(); }
}
private static object Scalar(SQLiteConnection connection, string sql)
{
using (var command = connection.CreateCommand()) { command.CommandText = sql; return command.ExecuteScalar(); }
}
}
}