Poseidon integration fix, ReadMe_BugFix.md actualisation

This commit is contained in:
Michal Buzik 2026-09-03 10:49:44 +02:00
parent b8e54e1fc9
commit 0258e54b4e
9 changed files with 369 additions and 39 deletions

View File

@ -1,6 +1,7 @@
using System;
using MySql.Data.MySqlClient;
using Common;
using log4net;
namespace Results.Entities.helpers
{
@ -10,6 +11,8 @@ namespace Results.Entities.helpers
/// </summary>
public static class DatabaseMigrationHelper
{
private static readonly ILog log = LogManager.GetLogger(typeof(DatabaseMigrationHelper));
public static void EnsureSchema(DBType dbType, string connectionString)
{
switch (dbType)
@ -36,10 +39,32 @@ namespace Results.Entities.helpers
// EnsureColumnMySql(conn, "WaterMeter", "Q3Channel", "INT NOT NULL DEFAULT 0");
// EnsureColumnMySql(conn, "MeterTestRslt", "Q3Channel", "INT NOT NULL DEFAULT 0");
EnsureColumnMySql(conn, "MeterTestRslt", "PulsesPerKilogram", "DOUBLE NOT NULL DEFAULT 0");
EnsureMeterTestResultMassColumnsMySql(conn);
EnsureColumnMySql(conn, "MeterTestRslt", "FlipMode", "INT NULL");
EnsureColumnMySql(conn, "MeterTestRslt", "ExtraDataPath", "VARCHAR(255) NULL");
EnsureMeterTestResultExtraColumnsMySql(conn);
}
}
private static void EnsureMeterTestResultExtraColumnsMySql(MySqlConnection conn)
{
for (int index = 1; index <= 9; index++)
EnsureColumnMySql(conn, "MeterTestRslt", "X" + index, "FLOAT NOT NULL DEFAULT 0");
}
// These properties were added to the MeterTestRslt NHibernate mapping
// after older customer databases had already been created. Keep them in
// one migration group so a batch insert does not fail one column at a time.
private static void EnsureMeterTestResultMassColumnsMySql(MySqlConnection conn)
{
EnsureColumnMySql(conn, "MeterTestRslt", "MassMeter", "DOUBLE NOT NULL DEFAULT 0");
EnsureColumnMySql(conn, "MeterTestRslt", "MassRef", "DOUBLE NOT NULL DEFAULT 0");
EnsureColumnMySql(conn, "MeterTestRslt", "ErrorMass", "DOUBLE NOT NULL DEFAULT 0");
EnsureColumnMySql(conn, "MeterTestRslt", "PassedMass", "TINYINT(1) NOT NULL DEFAULT 0");
EnsureColumnMySql(conn, "MeterTestRslt", "QuantityUnits", "VARCHAR(32) NULL");
}
private static void EnsureColumnMySql(
MySqlConnection conn,
string tableName,
@ -75,6 +100,8 @@ namespace Results.Entities.helpers
alter.CommandText = "ALTER TABLE `" + tableName + "` ADD COLUMN `" +
columnName + "` " + columnDefinition;
alter.ExecuteNonQuery();
log.WarnFormat("Results DB migration: added {0}.{1} ({2}).",
tableName, columnName, columnDefinition);
}
}
@ -100,10 +127,29 @@ namespace Results.Entities.helpers
// EnsureColumnSQLite(conn, "WaterMeter", "Q3Channel", "INTEGER NOT NULL DEFAULT 0");
// EnsureColumnSQLite(conn, "MeterTestRslt", "Q3Channel", "INTEGER NOT NULL DEFAULT 0");
EnsureColumnSQLite(conn, "MeterTestRslt", "PulsesPerKilogram", "REAL NOT NULL DEFAULT 0");
EnsureMeterTestResultMassColumnsSQLite(conn);
EnsureColumnSQLite(conn, "MeterTestRslt", "FlipMode", "INTEGER NULL");
EnsureColumnSQLite(conn, "MeterTestRslt", "ExtraDataPath", "TEXT NULL");
EnsureMeterTestResultExtraColumnsSQLite(conn);
}
}
private static void EnsureMeterTestResultExtraColumnsSQLite(System.Data.SQLite.SQLiteConnection conn)
{
for (int index = 1; index <= 9; index++)
EnsureColumnSQLite(conn, "MeterTestRslt", "X" + index, "REAL NOT NULL DEFAULT 0");
}
private static void EnsureMeterTestResultMassColumnsSQLite(System.Data.SQLite.SQLiteConnection conn)
{
EnsureColumnSQLite(conn, "MeterTestRslt", "MassMeter", "REAL NOT NULL DEFAULT 0");
EnsureColumnSQLite(conn, "MeterTestRslt", "MassRef", "REAL NOT NULL DEFAULT 0");
EnsureColumnSQLite(conn, "MeterTestRslt", "ErrorMass", "REAL NOT NULL DEFAULT 0");
EnsureColumnSQLite(conn, "MeterTestRslt", "PassedMass", "INTEGER NOT NULL DEFAULT 0");
EnsureColumnSQLite(conn, "MeterTestRslt", "QuantityUnits", "TEXT NULL");
}
private static void EnsureColumnSQLite(
System.Data.SQLite.SQLiteConnection conn,
string tableName,
@ -137,6 +183,8 @@ namespace Results.Entities.helpers
alter.CommandText = "ALTER TABLE " + tableName + " ADD COLUMN " +
columnName + " " + columnDefinition;
alter.ExecuteNonQuery();
log.WarnFormat("Results DB migration: added {0}.{1} ({2}).",
tableName, columnName, columnDefinition);
}
}
}

View File

@ -3,3 +3,14 @@
| Version | Source of change | Target Environment | Title | Description |
|------------|----------------------|--------------------------------------------------------------|-----------------------------------------------------|------------------------------------|
| 3.9.2149.0 | Michal databse error | HeatMeters, Heat meter sensors, Procedure Dilog, Tab Process | Excanged columns value 'Sensor' and 'Heat meter sensor' | Fix in code ProcedureDlg, row 1851 |
| 3.9.2149.1 | Main TBF | General release | Version iteration | Assembly and file version increment. |
| 3.9.2149.2 | Main TBF | Poseidon register reader | Poseidon pulse and timing handling | Added reference-pulse reading in `Run()` and improved Poseidon timing/task tracking. |
| 3.9.2149.4 | Main TBF | Mass collection / Poseidon start-stop | Mass collection update | Refactored standing-start mass collection, extended Poseidon start/end data-entry configuration and improved dialog/task handling. |
| 3.9.2200.1 | Main TBF | Test infrastructure | TBF test assembly access | Added `InternalsVisibleTo` support for `TBFTests`. |
| 3.9.2201.1 | Main TBF | Poseidon CLI | Poseidon CLI configuration | Added macro descriptions, refined serial-port/CLI argument configuration and extended CLI test coverage. |
| 3.9.2202.1 | Main TBF | Poseidon CLI | CLI release iteration | Assembly and file version increment for the Poseidon CLI workstream. |
| 3.9.2203.1 | Main TBF | Poseidon CLI / smart-meter sequence | CLI test and configuration update | Updated CLI executable test setup, serial-port default responses and smart-meter component-name handling. |
| 3.9.2204.1 | Morrisville | Morrisville / Poseidon CLI | Morrisville CLI diagnostics | Restored lost 2204 versioning, added detailed CLI command/response logging and used a fixed CLI directory for deployment. |
| 3.9.2205.1 | Morrisville | Morrisville / Poseidon CLI / Results DB | Poseidon read diagnostics and results DB migration | Improved Poseidon CLI execution and diagnostics: fixed CLI working directory, exit code/stdout/stderr capture, JSON/NFC/reading validation and culture-independent decimal parsing. Added reader-cycle and fake-CLI coverage. Results DB now creates missing compatibility columns (`FlipMode`, `ExtraDataPath`, `X1`-`X9`) automatically for MySQL and SQLite. |
| 3.9.2206.0 | Morrisville / Main TBF | Poseidon CLI / Results DB | Poseidon start/end rearm and simulation isolation | Re-armed a completed START reader once for END, preventing reused START values or skipped END CLI calls. Added CLI response, dialog prefill and confirmed-value diagnostics. Simulation always runs `C:\TBF\Cli\cmdSleepTest.exe` instead of the configured physical Hat CLI. Added customer-response, decimal separator, non-zero, dialog-transfer, reader-cycle and simulation-selection regression tests. Added compatibility migration for `PulsesPerKilogram`, `MassMeter`, `MassRef`, `ErrorMass`, `PassedMass` and `QuantityUnits`. |
| 3.9.3143.0 | Ally port | Ally / Poseidon CLI / Results DB | Morrisville Poseidon fixes transferred | Ported the applicable Morrisville Poseidon read, simulation, logging, regression-test and results-schema migration fixes to the Ally source branch while retaining its compatible legacy reader configuration and non-blocking UI flow. |

View File

@ -368,10 +368,16 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
{
dlg.WMStartState[item] = poseidonReader.BeginWMState;
dlg.WMStartStateStr[item] = poseidonReader.BeginWMState.ToString();
log.InfoFormat("Poseidon dialog prefill: phase=Start, WM{0}, reader={1}, value={2}",
item + 1, poseidonReader.Name, dlg.WMStartState[item]);
}
if (currentOp == CurrentOp.ReadDatastream_EndStates)
{
dlg.WMEndState[item] = poseidonReader.EndWMState;
log.InfoFormat("Poseidon dialog prefill: phase=End, WM{0}, reader={1}, value={2}",
item + 1, poseidonReader.Name, dlg.WMEndState[item]);
}
}
}
@ -426,30 +432,9 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
}
}
}
else if (modelessDlg is TestStartEndForm && currentOp == CurrentOp.ReadDatastream_StartStates)
else if (modelessDlg is TestStartEndForm)
{
/// Fixed start test - start
TestStartEndForm dlg = (modelessDlg as TestStartEndForm);
if (dlg != null)
{
for (int i = 0; i < dlg.WaterMetersCount; i++)
{
wmStartState[i] = dlg.WMStartState[i];
wmStartStateStr[i] = dlg.WMStartStateStr[i];
}
}
}
else if (modelessDlg is TestStartEndForm && currentOp == CurrentOp.ReadDatastream_EndStates)
{
/// Fixed start test - end
TestStartEndForm dlg = (modelessDlg as TestStartEndForm);
if (dlg != null)
{
for (int i = 0; i < dlg.WaterMetersCount; i++)
{
wmEndState[i] = dlg.WMEndState[i];
}
}
StoreAcceptedDialogValues((TestStartEndForm)modelessDlg);
}
resultSaved = true;
modelessDlg = null;
@ -459,6 +444,32 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
return Event.ModelessFormClosed; /// Form closed
}
private void StoreAcceptedDialogValues(TestStartEndForm dlg)
{
if (dlg == null)
return;
if (currentOp == CurrentOp.ReadDatastream_StartStates)
{
for (int i = 0; i < dlg.WaterMetersCount; i++)
{
wmStartState[i] = dlg.WMStartState[i];
wmStartStateStr[i] = dlg.WMStartStateStr[i];
log.InfoFormat("Poseidon dialog accepted: phase=Start, WM{0}, text='{1}', value={2}",
i + 1, wmStartStateStr[i], wmStartState[i]);
}
}
else if (currentOp == CurrentOp.ReadDatastream_EndStates)
{
for (int i = 0; i < dlg.WaterMetersCount; i++)
{
wmEndState[i] = dlg.WMEndState[i];
log.InfoFormat("Poseidon dialog accepted: phase=End, WM{0}, value={1}",
i + 1, wmEndState[i]);
}
}
}
/// <summary>Stop this operation</summary>
public void Stop()
{

View File

@ -147,6 +147,8 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
private void SetUiBusy(bool busy, long deltaTime = -1)
{
log.DebugFormat("Poseidon UI: SetUiBusy busy={0}, deltaTime={1}, enabledTextBoxes={2}",
busy, deltaTime, enabledTextBoxes.Count);
// show wait cursor for form and children
this.UseWaitCursor = busy;
@ -296,6 +298,8 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
BeginInvoke(new Action(() => UpdateValues(stratValue, enableEdit)));
return;
}
log.DebugFormat("Poseidon UI: UpdateValues on UI thread startValue={0}, enableEdit={1}, deltaTime={2}",
stratValue, enableEdit, deltaTime);
for (int i = 0; i < TextBoxesCount; i++)
{
@ -320,6 +324,7 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
if (enableEdit)
{
SetUiBusy(false, deltaTime);
log.Debug("Poseidon UI: values applied and dialog released from Loading...");
}
// if you also need to enable/disable editing, do it here,
// it's now safely on the UI thread.
@ -348,6 +353,8 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
if (endTextBoxes[i].Visible && endTextBoxes[i].Enabled)
{
WMEndState[i] = Utils.ParseUDouble(endTextBoxes[i].Text);
log.InfoFormat("Poseidon dialog OK: phase=End, WM{0}, enteredText='{1}', parsedValue={2}",
i + 1, endTextBoxes[i].Text, WMEndState[i]);
}
}
}
@ -359,6 +366,8 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
{
WMStartStateStr[i] = startTextBoxes[i].Text;
WMStartState[i] = Utils.ParseUDouble(startTextBoxes[i].Text);
log.InfoFormat("Poseidon dialog OK: phase=Start, WM{0}, enteredText='{1}', parsedValue={2}",
i + 1, startTextBoxes[i].Text, WMStartState[i]);
}
}
}

View File

@ -25,6 +25,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
public class PoseidonReader : ComponentBase, IDevice, IRegReaderDatastream, ISessionDataMngmnt, IOperation, ICommonRegReader
{
private static readonly ILog log = LogManager.GetLogger(typeof(PoseidonReader));
// Simulation must never call the CLI configured for the physical Hat.
internal const string SimulatedCliFileName = "cmdSleepTest.exe";
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
readonly PoseidonCfg registerReaderCfg;
@ -302,10 +304,18 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
registerReaderCfg.MeterType,
SerialPortData.DefaultPoseidonMeterType);
}
string cliFileName = GetCliFileNameForMode(DebugLevel, registerReaderCfg.CliFileName);
serialPort = new SerialPortData(string.Format("COM{0}", registerReaderCfg.ComPortNr),
registerReaderCfg.CliFileName,
cliFileName,
registerReaderCfg.MeterType);
if (DebugLevel == DebugMode.Simulate)
{
log.WarnFormat(
"{0}: simulation mode enabled; overriding configured CLI '{1}' with '{2}'.",
Name, registerReaderCfg.CliFileName, serialPort.SerialPortCmdClientPath);
}
//TODO BUMI prepare serial port - for us do nothing
//serialPort.Open();
@ -327,6 +337,13 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
}
}
internal static string GetCliFileNameForMode(DebugMode debugMode, string configuredCliFileName)
{
return debugMode == DebugMode.Simulate
? SimulatedCliFileName
: configuredCliFileName;
}
public void Clear()
{
log.DebugFormat("{0}:Clear()", Name);
@ -518,12 +535,14 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
}
}
double volume;
if (TryParseCliReading(data.Reading, out volume))
double volumeLi;
string dialogValueFailureReason;
if (TryGetDialogValue(data, out volumeLi, out dialogValueFailureReason))
{
lastCliReadingParsed = true;
lastCliReadSucceeded = true;
double volumeLi = Units.ConvertFrom(Unit.USgal, volume);
double volume;
TryParseCliReading(data.Reading, out volume);
if (_isReadingStart)
beginWMState = volumeLi;
@ -533,7 +552,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
Name, _isReadingStart ? "Begin" : "End", data.DeviceId, data.Reading, volume, volumeLi, beginWMState, endWMState);
}
else
lastCliReadFailureReason = "Reading could not be parsed: '" + data.Reading + "'.";
{
lastCliReadFailureReason = dialogValueFailureReason;
log.ErrorFormat("{0}: cannot parse CLI reading '{1}' using invariant or current culture.",
Name, data.Reading);
}
}
}
@ -554,6 +577,29 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
CultureInfo.InvariantCulture, out value);
}
/// <summary>
/// Validates a CLI response and converts its US-gallon reading to the
/// litre value assigned to the START/END dialog.
/// </summary>
public static bool TryGetDialogValue(JsonDataFromPoseidon data, out double valueLitres,
out string failureReason)
{
valueLitres = 0;
if (!TryValidateCliReadResponse(data, out failureReason))
return false;
double valueGallons;
if (!TryParseCliReading(data.Reading, out valueGallons))
{
failureReason = "Reading could not be parsed: '" + data.Reading + "'.";
return false;
}
valueLitres = Units.ConvertFrom(Unit.USgal, valueGallons);
failureReason = null;
return true;
}
public static bool TryValidateCliReadResponse(JsonDataFromPoseidon data, out string failureReason)
{
if (data == null) { failureReason = "CLI returned no JSON data."; return false; }

View File

@ -0,0 +1,54 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
using CmdPoseidonReader = TBF.Rig.RegisterReaders.PoseidonCmdStartStop.PoseidonReader;
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
{
/// <summary>
/// Regression fixtures extracted from PT50 customer CliRunner responses.
/// </summary>
[TestClass]
public class PoseidonCustomerCliResponseTest
{
// Independently calculated fixtures can differ by insignificant
// IEEE-754 rounding during gallons-to-litres conversion.
private const double DialogValueToleranceLitres = 0.0001;
private const string Com43InitialResponse =
"{\"Reading\":\"002780.99\",\"DeviceId\":\"1000000219\",\"ProductType\":74,\"ReadingComplete\":true,\"NfcTagDetected\":true,\"CliVersion\":\"2.0.0.2\"}";
private const string Com45InitialResponse =
"{\"Reading\":\"002316.63\",\"DeviceId\":\"1000000279\",\"ProductType\":74,\"ReadingComplete\":true,\"NfcTagDetected\":true,\"CliVersion\":\"2.0.0.2\"}";
private const string Com43LaterResponse =
"{\"Reading\":\"002898.60\",\"DeviceId\":\"1000000219\",\"ProductType\":74,\"ReadingComplete\":true,\"NfcTagDetected\":true,\"CliVersion\":\"2.0.0.2\"}";
private const string Com45LaterResponse =
"{\"Reading\":\"002433.50\",\"DeviceId\":\"1000000279\",\"ProductType\":74,\"ReadingComplete\":true,\"NfcTagDetected\":true,\"CliVersion\":\"2.0.0.2\"}";
[TestMethod]
public void CustomerCliResponses_AreDeserializedAndConvertedToDialogValues()
{
AssertDialogValue(Com43InitialResponse, "1000000219", "002780.99", 10527.1923171862);
AssertDialogValue(Com45InitialResponse, "1000000279", "002316.63", 8769.39850116792);
AssertDialogValue(Com43LaterResponse, "1000000219", "002898.60", 10972.3945971024);
AssertDialogValue(Com45LaterResponse, "1000000279", "002433.50", 9211.799576364);
}
private static void AssertDialogValue(string json, string expectedDeviceId,
string expectedReading, double expectedLitres)
{
var cliRunner = new CliRunner(false);
JsonDataFromPoseidon response;
Assert.IsTrue(cliRunner.TryJsonStringDeserialize(json, out response));
Assert.IsNotNull(response);
Assert.AreEqual(expectedDeviceId, response.DeviceId);
Assert.AreEqual(expectedReading, response.Reading);
double dialogValue;
string failureReason;
Assert.IsTrue(CmdPoseidonReader.TryGetDialogValue(response, out dialogValue, out failureReason), failureReason);
Assert.AreEqual(expectedLitres, dialogValue, DialogValueToleranceLitres,
"The value assigned to the START/END dialog must come from the CLI response.");
Assert.AreNotEqual(0d, dialogValue);
}
}
}

View File

@ -0,0 +1,129 @@
using System;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.DataEntry.PoseidonCmd;
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
using CmdPoseidonReader = TBF.Rig.RegisterReaders.PoseidonCmdStartStop.PoseidonReader;
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
{
[TestClass]
public class PoseidonDialogTransferTest
{
private const string CustomerCliResponse =
"{\"Reading\":\"002433.50\",\"DeviceId\":\"1000000279\",\"ProductType\":74,\"ReadingComplete\":true,\"NfcTagDetected\":true,\"CliVersion\":\"2.0.0.2\"}";
[TestMethod]
public void CustomerCliValue_IsPrefilledConfirmedAndTransferred_ForStartAndEnd()
{
Exception failure = null;
var staThread = new Thread(() =>
{
try { RunDialogTransferScenario(); }
catch (Exception exception) { failure = exception; }
});
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join(TimeSpan.FromSeconds(15));
Assert.IsFalse(staThread.IsAlive, "The Poseidon dialog test did not finish.");
if (failure != null) throw failure;
}
private static void RunDialogTransferScenario()
{
var cliRunner = new CliRunner(false);
JsonDataFromPoseidon response;
Assert.IsTrue(cliRunner.TryJsonStringDeserialize(CustomerCliResponse, out response));
double cliValueLitres;
string failureReason;
Assert.IsTrue(CmdPoseidonReader.TryGetDialogValue(response, out cliValueLitres, out failureReason), failureReason);
VerifyStartDialogTransfer(cliValueLitres);
VerifyEndDialogTransfer(cliValueLitres);
}
private static void VerifyStartDialogTransfer(double expectedValue)
{
using (var dialog = new TestStartEndForm(1, null, new bool[1]))
{
dialog.CreateControl();
dialog.WMStartState[0] = expectedValue;
dialog.WMStartStateStr[0] = expectedValue.ToString();
dialog.UpdateValues(true, true);
Assert.AreEqual(dialog.WMStartStateStr[0], FindTextBox(dialog, "startTextBox1").Text);
ConfirmDialog(dialog);
Assert.IsTrue(dialog.Completed);
Assert.AreEqual(expectedValue, dialog.WMStartState[0], 0.0001);
var entryForm = CreateEntryFormForTransfer(EntryForm.CurrentOp.ReadDatastream_StartStates);
TransferAcceptedDialogValues(entryForm, dialog);
Assert.AreEqual(expectedValue, entryForm.WMStartState(0), 0.0001);
}
}
private static void VerifyEndDialogTransfer(double expectedValue)
{
using (var dialog = new TestStartEndForm(1, null, new[] { expectedValue.ToString() }, new bool[1], 0, 0, 0))
{
dialog.CreateControl();
dialog.WMEndState[0] = expectedValue;
dialog.UpdateValues(false, true);
Assert.AreEqual(expectedValue.ToString(), FindTextBox(dialog, "endTextBox1").Text);
ConfirmDialog(dialog);
Assert.IsTrue(dialog.Completed);
Assert.AreEqual(expectedValue, dialog.WMEndState[0], 0.0001);
var entryForm = CreateEntryFormForTransfer(EntryForm.CurrentOp.ReadDatastream_EndStates);
TransferAcceptedDialogValues(entryForm, dialog);
Assert.AreEqual(expectedValue, entryForm.WMEndState(0), 0.0001);
}
}
private static TextBox FindTextBox(Control dialog, string name)
{
TextBox textBox = dialog.Controls.Find(name, true).OfType<TextBox>().FirstOrDefault();
Assert.IsNotNull(textBox, "Expected dialog control was not found: " + name);
return textBox;
}
private static void ConfirmDialog(TestStartEndForm dialog)
{
MethodInfo okHandler = typeof(TestStartEndForm).GetMethod("okButton_Click",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.IsNotNull(okHandler);
okHandler.Invoke(dialog, new object[] { dialog, EventArgs.Empty });
}
private static EntryForm CreateEntryFormForTransfer(EntryForm.CurrentOp currentOp)
{
var entryForm = new EntryForm();
SetPrivateField(entryForm, "currentOp", currentOp);
SetPrivateField(entryForm, "wmStartState", new double[1]);
SetPrivateField(entryForm, "wmStartStateStr", new string[1]);
SetPrivateField(entryForm, "wmEndState", new double[1]);
return entryForm;
}
private static void TransferAcceptedDialogValues(EntryForm entryForm, TestStartEndForm dialog)
{
MethodInfo method = typeof(EntryForm).GetMethod("StoreAcceptedDialogValues",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.IsNotNull(method);
method.Invoke(entryForm, new object[] { dialog });
}
private static void SetPrivateField(object instance, string name, object value)
{
FieldInfo field = instance.GetType().GetField(name,
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.IsNotNull(field, "Expected field was not found: " + name);
field.SetValue(instance, value);
}
}
}

View File

@ -82,6 +82,26 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
}
}
[TestMethod]
public void InputJson_NonZeroReading_WithDecimalCommaOrDot_IsParsedCorrectly()
{
foreach (string reading in new[] { "002433,50", "002433.50", "002898,60", "002898.60" })
{
double value;
Assert.IsTrue(CmdPoseidonReader.TryParseCliReading(reading, out value));
Assert.IsTrue(value > 0, "The non-zero CLI reading must remain non-zero: " + reading);
}
}
[TestMethod]
public void SimulationMode_AlwaysUsesCmdSleepTestInsteadOfConfiguredHatCli()
{
Assert.AreEqual("cmdSleepTest.exe", CmdPoseidonReader.GetCliFileNameForMode(
Common.DebugMode.Simulate, "HatCliDemo.exe"));
Assert.AreEqual("HatCliDemo.exe", CmdPoseidonReader.GetCliFileNameForMode(
Common.DebugMode.Normal, "HatCliDemo.exe"));
}
[TestMethod]
public void CliReadResponse_RequiresNfcAndCompletedReading()
{

View File

@ -136,6 +136,8 @@
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutUnitTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReadCycleTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonCustomerCliResponseTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonDialogTransferTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonSingleMeterIntegrationTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />