Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5936c8f19 | ||
|
|
2842425673 |
@@ -6,10 +6,6 @@
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<WarningLevel>0</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
|
||||
+1
-6
@@ -11,7 +11,6 @@ using NHibernate.Cfg;
|
||||
using NHibernate.Tool.hbm2ddl;
|
||||
using Common;
|
||||
using Results.Entities;
|
||||
using Results.Entities.helpers;
|
||||
|
||||
namespace Results
|
||||
{
|
||||
@@ -108,11 +107,7 @@ namespace Results
|
||||
throw new Exception("Connection string was not specified");
|
||||
}
|
||||
|
||||
if (SessionFactory == null)
|
||||
{
|
||||
DatabaseMigrationHelper.EnsureSchema(dbType, connectionString);
|
||||
SessionFactory = CreateSessionFactory();
|
||||
}
|
||||
if (SessionFactory == null) SessionFactory = CreateSessionFactory();
|
||||
|
||||
return SessionFactory.OpenSession();
|
||||
}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
using System;
|
||||
using Common;
|
||||
using log4net;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Results.Entities.helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies explicit, backwards-compatible results DB migrations before the
|
||||
/// first NHibernate session factory is created.
|
||||
/// </summary>
|
||||
public static class DatabaseMigrationHelper
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(DatabaseMigrationHelper));
|
||||
|
||||
public static void EnsureSchema(DBType dbType, string connectionString)
|
||||
{
|
||||
switch (dbType)
|
||||
{
|
||||
case DBType.MySql:
|
||||
EnsureMySqlSchema(connectionString);
|
||||
break;
|
||||
case DBType.SQLite:
|
||||
EnsureSQLiteSchema(connectionString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureMySqlSchema(string connectionString)
|
||||
{
|
||||
using (var conn = new MySqlConnection(connectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
// Added by newer MeterTestRslt mapping; older customer DBs do
|
||||
// not contain it and otherwise reject the entire batch insert.
|
||||
EnsureColumnMySql(conn, "MeterTestRslt", "PulsesPerKilogram", "DOUBLE NOT NULL DEFAULT 0");
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureColumnMySql(
|
||||
MySqlConnection conn,
|
||||
string tableName,
|
||||
string columnName,
|
||||
string columnDefinition)
|
||||
{
|
||||
using (var transaction = conn.BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
bool exists;
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.Transaction = transaction;
|
||||
cmd.CommandText = @"
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = @tableName
|
||||
AND COLUMN_NAME = @columnName";
|
||||
cmd.Parameters.AddWithValue("@tableName", tableName);
|
||||
cmd.Parameters.AddWithValue("@columnName", columnName);
|
||||
exists = Convert.ToInt32(cmd.ExecuteScalar()) > 0;
|
||||
}
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
using (var alter = conn.CreateCommand())
|
||||
{
|
||||
alter.Transaction = transaction;
|
||||
alter.CommandText = "ALTER TABLE `" + tableName + "` ADD COLUMN `" +
|
||||
columnName + "` " + columnDefinition;
|
||||
alter.ExecuteNonQuery();
|
||||
log.WarnFormat("Results DB migration: added {0}.{1} ({2}).",
|
||||
tableName, columnName, columnDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureSQLiteSchema(string databaseFile)
|
||||
{
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection("Data Source=" + databaseFile))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
EnsureColumnSQLite(conn, "MeterTestRslt", "PulsesPerKilogram", "REAL NOT NULL DEFAULT 0");
|
||||
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 EnsureColumnSQLite(
|
||||
System.Data.SQLite.SQLiteConnection conn,
|
||||
string tableName,
|
||||
string columnName,
|
||||
string columnDefinition)
|
||||
{
|
||||
bool exists = false;
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "PRAGMA table_info(" + tableName + ")";
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
if (string.Equals(reader["name"].ToString(), columnName,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
using (var alter = conn.CreateCommand())
|
||||
{
|
||||
alter.CommandText = "ALTER TABLE " + tableName + " ADD COLUMN " +
|
||||
columnName + " " + columnDefinition;
|
||||
alter.ExecuteNonQuery();
|
||||
log.WarnFormat("Results DB migration: added {0}.{1} ({2}).",
|
||||
tableName, columnName, columnDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,9 +53,6 @@
|
||||
<Reference Include="NHibernate">
|
||||
<HintPath>..\packages\NHibernate.4.0.4.4000\lib\net40\NHibernate.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.SQLite, Version=2.0.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Data.SQLite.2.0.3\lib\net471\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
@@ -72,7 +69,6 @@
|
||||
<Compile Include="DBase.cs" />
|
||||
<Compile Include="Entities\Batch.cs" />
|
||||
<Compile Include="Entities\Components.cs" />
|
||||
<Compile Include="Entities\helpers\DatabaseMigrationHelper.cs" />
|
||||
<Compile Include="Entities\MeterTestRslt.cs" />
|
||||
<Compile Include="Entities\PurchaseBox.cs" />
|
||||
<Compile Include="Entities\PurchaseOrder.cs" />
|
||||
@@ -269,4 +265,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -106,6 +106,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LabelPrinting", "LabelPrint
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TBFTests", "TBFTests\TBFTests.csproj", "{77EB589F-C670-4489-AAD6-2A3C02061FD1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppDiagnostic", "AppDiagnostic\AppDiagnostic.csproj", "{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3} = {8F942729-F454-4C99-BA6C-746962065AE3}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedComponents", "SharedComponents\SharedComponents.csproj", "{8F942729-F454-4C99-BA6C-746962065AE3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sensus.iPerl.RfidCom", "..\iPerlHead\Sensus.iPerl.RfidCom\Sensus.iPerl.RfidCom.csproj", "{A3E14180-7F00-42E5-94A9-8DB62EAD6EE8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sensus.iPerl.TestConsole", "..\iPerlHead\Sensus.iPerl.TestConsole\Sensus.iPerl.TestConsole.csproj", "{5025ED8B-A94F-4E58-8BE0-68481B061609}"
|
||||
@@ -114,6 +121,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NfcS5_DLL", "..\NfcS5_DLL\N
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NfcC7_DLL", "NfcC7_DLL\NfcC7_DLL.csproj", "{53E75979-B530-4805-8FC9-B314F14A62BE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NfcC7_DLL.Tests", "NfcC7_DLL.Tests\NfcC7_DLL.Tests.csproj", "{715605C5-568B-48D6-9C09-2DC5F2E81F66}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -474,6 +483,30 @@ Global
|
||||
{77EB589F-C670-4489-AAD6-2A3C02061FD1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{77EB589F-C670-4489-AAD6-2A3C02061FD1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{77EB589F-C670-4489-AAD6-2A3C02061FD1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}.Release|x86.Build.0 = Release|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8F942729-F454-4C99-BA6C-746962065AE3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A3E14180-7F00-42E5-94A9-8DB62EAD6EE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A3E14180-7F00-42E5-94A9-8DB62EAD6EE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A3E14180-7F00-42E5-94A9-8DB62EAD6EE8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
@@ -522,6 +555,18 @@ Global
|
||||
{53E75979-B530-4805-8FC9-B314F14A62BE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{53E75979-B530-4805-8FC9-B314F14A62BE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{53E75979-B530-4805-8FC9-B314F14A62BE}.Release|x86.Build.0 = Release|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{715605C5-568B-48D6-9C09-2DC5F2E81F66}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -19,9 +19,6 @@ 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")]
|
||||
|
||||
@@ -32,5 +29,5 @@ using System.Runtime.InteropServices;
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("3.9.2206.0")]
|
||||
[assembly: AssemblyFileVersion("3.9.2206.0")]
|
||||
[assembly: AssemblyVersion("3.9.2149.4")]
|
||||
[assembly: AssemblyFileVersion("3.9.2149.4")]
|
||||
|
||||
@@ -3,13 +3,3 @@
|
||||
| Version | Target Environment | Title | Description |
|
||||
|------------|--------------------------------------------------------------|-----------------------------------------------------|------------------------------------|
|
||||
| 3.9.2149.0 | 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 | General release | Version iteration | Assembly and file version increment. |
|
||||
| 3.9.2149.2 | Poseidon register reader | Poseidon pulse and timing handling | Added reference-pulse reading in `Run()` and improved Poseidon timing/task tracking. |
|
||||
| 3.9.2149.4 | 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 | Test infrastructure | TBF test assembly access | Added `InternalsVisibleTo` support for `TBFTests`. |
|
||||
| 3.9.2201.1 | Poseidon CLI | Poseidon CLI configuration | Added macro descriptions, refined serial-port/CLI argument configuration and extended CLI test coverage. |
|
||||
| 3.9.2202.1 | Poseidon CLI | CLI release iteration | Assembly and file version increment for the Poseidon CLI workstream. |
|
||||
| 3.9.2203.1 | 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 / 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 / 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 test coverage. Prevented a device/CLI error while changing the legacy optical-head mode from crashing the UI. Results DB now creates missing `MeterTestRslt` compatibility columns (`FlipMode`, `ExtraDataPath`, `X1`-`X9`) automatically for MySQL and SQLite before results are saved. |
|
||||
| 3.9.2206.0 | Morrisville / Poseidon CLI / Results DB | Poseidon start/end rearm and simulation isolation | Fixed the Poseidon start/end read cycle so a completed START reader is armed again once for END, preventing reused START values or skipped END CLI calls. Added trace logging for CLI response, dialog prefill and confirmed dialog values. In `Simulate` mode, `PoseidonCmdStartStop` now always runs the fixed `C:\TBF\Cli\cmdSleepTest.exe` instead of the configured physical Hat CLI. Added regression tests for customer CLI JSON responses, decimal comma/dot and non-zero readings, start/end dialog transfer, full/reduced reader cycles and simulation CLI selection. Extended results-schema migration with `MeterTestRslt.PulsesPerKilogram`. |
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/// Copyright (c) 2021-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using Common;
|
||||
using log4net;
|
||||
using TBF.Rig.Sequences;
|
||||
|
||||
@@ -11,6 +12,10 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(FlyingStartStopTestOp));
|
||||
public override string ToString() { return string.Format("FlyingStartStopTestOp()"); }
|
||||
|
||||
private DateTime startTimeForSimulation;
|
||||
private bool simulationTimerStarted = false;
|
||||
|
||||
|
||||
/// Arguments of the constructor
|
||||
readonly UniCB uniCB;
|
||||
@@ -123,6 +128,28 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
{
|
||||
log.DebugFormat("Op.Run() opState={0}", opState);
|
||||
|
||||
//Simulation of processing time 25 seconds
|
||||
if (uniCB.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
if (opState == OpState.StartingTest)
|
||||
{
|
||||
startTimeForSimulation = DateTime.Now;
|
||||
simulationTimerStarted = false;
|
||||
}
|
||||
if (opState == OpState.TestInProgress)
|
||||
{
|
||||
if (!simulationTimerStarted)
|
||||
{
|
||||
startTimeForSimulation = DateTime.Now;
|
||||
simulationTimerStarted = true;
|
||||
}
|
||||
else if (DateTime.Now - startTimeForSimulation > TimeSpan.FromSeconds(25))
|
||||
{
|
||||
return Event.TestCompleted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (opState)
|
||||
{
|
||||
case OpState.StartingTest:
|
||||
|
||||
@@ -17,7 +17,8 @@ using TBF.Boxes;
|
||||
using TBF.Rig.GenericDevices;
|
||||
using TBF.Rig.Sequences;
|
||||
using TBF.UiBridge;
|
||||
|
||||
using AppDiagnostic;
|
||||
using SharedComponents;
|
||||
|
||||
namespace TBF.Rig.ControlBoard.Uni
|
||||
{
|
||||
|
||||
@@ -268,16 +268,10 @@ 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,10 +326,31 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (modelessDlg is TestStartEndForm)
|
||||
else if (modelessDlg is TestStartEndForm && currentOp == CurrentOp.ReadDatastream_StartStates)
|
||||
{
|
||||
StoreAcceptedDialogValues((TestStartEndForm)modelessDlg);
|
||||
}
|
||||
/// 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
resultSaved = true;
|
||||
modelessDlg = null;
|
||||
}
|
||||
@@ -343,36 +358,6 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
|
||||
return Event.ModelessFormClosed; /// Form closed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies values confirmed by the Poseidon start/end dialog into the
|
||||
/// data-entry state used later by the result calculation.
|
||||
/// </summary>
|
||||
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()
|
||||
{
|
||||
@@ -448,45 +433,35 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
|
||||
|| (currentOp == CurrentOp.ReadDatastream_EndStates))
|
||||
{
|
||||
bool finishedReading = regReaders == null; // we can work only with register readers
|
||||
List<IPoseidonReadOperation> poseidonReaders = new List<IPoseidonReadOperation>();
|
||||
if (regReaders != null)
|
||||
{
|
||||
foreach (var iRegReader in regReaders)
|
||||
{
|
||||
PoseidonReader poseidonReader = iRegReader as PoseidonReader;
|
||||
if (poseidonReader != null)
|
||||
{
|
||||
poseidonReader.SetCliLogging(CliLogging);
|
||||
poseidonReaders.Add(new PoseidonReaderOperation(poseidonReader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var phaseRunner = new PoseidonReadPhaseRunner(
|
||||
currentOp == CurrentOp.ReadDatastream_StartStates);
|
||||
while (!finishedReading) //TODO BUMI lock - fuck ?
|
||||
{
|
||||
foreach (IPoseidonReadOperation poseidonReader in poseidonReaders)
|
||||
bool bAllReadersFinished = true;
|
||||
foreach (var iRegReader in regReaders )
|
||||
{
|
||||
if (poseidonReader.IsNotStarted || poseidonReader.IsFinished)
|
||||
log.DebugFormat("Poseidon read: arming {0} for {1}, previous state finished={2}",
|
||||
currentOp, poseidonReader.Name, poseidonReader.IsFinished);
|
||||
}
|
||||
|
||||
bool bAllReadersFinished = phaseRunner.RunIteration(poseidonReaders);
|
||||
foreach (IPoseidonReadOperation poseidonReader in poseidonReaders)
|
||||
{
|
||||
if (poseidonReader.IsFinished)
|
||||
if (iRegReader is PoseidonReader)
|
||||
{
|
||||
if (poseidonReader.HasError)
|
||||
log.ErrorFormat("Poseidon read: {0} completed with Error during {1}", poseidonReader.Name, currentOp);
|
||||
else
|
||||
log.DebugFormat("Poseidon read: {0} completed during {1}", poseidonReader.Name, currentOp);
|
||||
PoseidonReader poseidonReader = (iRegReader as PoseidonReader);
|
||||
if(poseidonReader == null)
|
||||
continue;
|
||||
poseidonReader.SetCliLogging(CliLogging);
|
||||
if (!(poseidonReader.CurrentOp == PoseidonReader.CurrentPoseidonOp.ReadDatastream_Done
|
||||
|| poseidonReader.CurrentOp == PoseidonReader.CurrentPoseidonOp.ReadDatastream_Running))
|
||||
{
|
||||
poseidonReader.SetCurrentOp((currentOp == CurrentOp.ReadDatastream_StartStates)?
|
||||
PoseidonReader.CurrentPoseidonOp.ReadDataStream_Start :
|
||||
PoseidonReader.CurrentPoseidonOp.ReadDataStream_End);
|
||||
}
|
||||
/// Send start data stream
|
||||
poseidonReader.Run();
|
||||
if (!(poseidonReader.CurrentOp == PoseidonReader.CurrentPoseidonOp.Done
|
||||
|| poseidonReader.CurrentOp == PoseidonReader.CurrentPoseidonOp.Error))
|
||||
{
|
||||
bAllReadersFinished = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bAllReadersFinished)
|
||||
{
|
||||
log.DebugFormat("Poseidon read: all readers finished for {0}", currentOp);
|
||||
finishedReading = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -144,9 +144,6 @@ 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;
|
||||
|
||||
@@ -293,15 +290,10 @@ namespace TBF.Rig.DataEntry.PoseidonCmd
|
||||
// Ensure we are on the UI thread
|
||||
if (InvokeRequired)
|
||||
{
|
||||
log.DebugFormat("Poseidon UI: queue UpdateValues startValue={0}, enableEdit={1}, deltaTime={2}",
|
||||
stratValue, enableEdit, deltaTime);
|
||||
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++)
|
||||
{
|
||||
if (stratValue)
|
||||
@@ -325,7 +317,6 @@ 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.
|
||||
@@ -354,8 +345,6 @@ 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,8 +356,6 @@ 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,42 @@
|
||||
///
|
||||
|
||||
using System;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.CommonRR.IPerl.communication
|
||||
{
|
||||
public class OptoReceivedEventArgs : EventArgs
|
||||
{
|
||||
public string Data;
|
||||
public WaterMetrologyData WaterMetrologyData;
|
||||
public byte[] RawData;
|
||||
|
||||
public OptoReceivedEventArgs(string data)
|
||||
{
|
||||
this.Data = data;
|
||||
WaterMetrologyData = null;
|
||||
RawData = null;
|
||||
}
|
||||
|
||||
public OptoReceivedEventArgs(string data, WaterMetrologyData waterMetrologyData)
|
||||
{
|
||||
this.Data = data;
|
||||
this.WaterMetrologyData = waterMetrologyData;
|
||||
RawData = null;
|
||||
}
|
||||
|
||||
public OptoReceivedEventArgs(byte[] data)
|
||||
{
|
||||
this.RawData = data;
|
||||
Data = null;
|
||||
WaterMetrologyData = null;
|
||||
}
|
||||
|
||||
public OptoReceivedEventArgs(WaterMetrologyData data)
|
||||
{
|
||||
WaterMetrologyData = data;
|
||||
Data = null;
|
||||
RawData = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,29 +13,46 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
public class CliRunner
|
||||
{
|
||||
//static readonly ILog log = LogManager.GetLogger(typeof(CliRunner));
|
||||
private readonly ILog log;
|
||||
private readonly List<CliTaskInfo> taskPool = new List<CliTaskInfo>();
|
||||
private long startTimeMs;
|
||||
private long incommingTimeMs;
|
||||
private List<Task> taskPool = new List<Task>();
|
||||
private long startTime;
|
||||
private long incommingTime;
|
||||
|
||||
public List<Task> TaskPool { get { return taskPool; } }
|
||||
public void AddTask(Task task) { taskPool.Add(task); }
|
||||
public void WaitAll() { Task.WaitAll(taskPool.ToArray()); }
|
||||
public void Clear() { taskPool.Clear(); }
|
||||
public void CancelAll() { Task.WhenAll(taskPool).ContinueWith(t => { }); }
|
||||
|
||||
public List<CliTaskInfo> TaskPool
|
||||
public void StartAll()
|
||||
{
|
||||
get { return taskPool; }
|
||||
taskPool.ForEach(t => t.Start());
|
||||
}
|
||||
|
||||
public bool AreTasksDone()
|
||||
{
|
||||
return taskPool.All(task => task.IsCompleted);
|
||||
}
|
||||
|
||||
public long StartTime
|
||||
{
|
||||
get { return startTimeMs; }
|
||||
get => startTime;
|
||||
}
|
||||
|
||||
public long IncommingTime
|
||||
{
|
||||
get { return incommingTimeMs; }
|
||||
get => incommingTime;
|
||||
}
|
||||
|
||||
public bool TimeOutReceived(long timeout)
|
||||
{
|
||||
return (DateTime.Now.Ticks - startTime) > timeout;
|
||||
}
|
||||
|
||||
public CliRunner(bool isCliLogging)
|
||||
{
|
||||
ResetStartTime();
|
||||
startTime = DateTime.Now.Ticks;
|
||||
|
||||
if (isCliLogging)
|
||||
{
|
||||
@@ -43,432 +60,173 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
log = new ScopedLoggerFactory().CreateLogger<CliRunner>(
|
||||
@"C:\TBF\Logs\CliRunner.txt",
|
||||
10,
|
||||
7,
|
||||
10, // maxFileSizeMB
|
||||
7, // maxBackups
|
||||
log4net.Core.Level.Debug,
|
||||
true,
|
||||
true,
|
||||
TimeSpan.FromMinutes(2)
|
||||
true, // zipRolledFiles
|
||||
true, // singleZipPerDay
|
||||
TimeSpan.FromMinutes(2) // zipScanInterval
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fallback to console or handle gracefully
|
||||
Console.WriteLine($"Failed to initialize logger: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetStartTime()
|
||||
{
|
||||
startTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var item in taskPool)
|
||||
{
|
||||
try
|
||||
{
|
||||
item?.Cts?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (item?.Process != null)
|
||||
{
|
||||
item.Process.Dispose();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
taskPool.Clear();
|
||||
}
|
||||
|
||||
public void WaitAll()
|
||||
{
|
||||
var tasks = taskPool
|
||||
.Where(t => t != null && t.Task != null)
|
||||
.Select(t => t.Task)
|
||||
.ToArray();
|
||||
|
||||
if (tasks.Length == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
catch (AggregateException)
|
||||
{
|
||||
RefreshTaskStates();
|
||||
}
|
||||
}
|
||||
|
||||
public bool AreTasksDone()
|
||||
{
|
||||
RefreshTaskStates();
|
||||
return taskPool.Count > 0 && taskPool.All(t => t.State != CliTaskState.Running);
|
||||
}
|
||||
|
||||
public bool TimeOutReceived(long timeoutMs)
|
||||
{
|
||||
long nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
return (nowMs - startTimeMs) > timeoutMs;
|
||||
}
|
||||
|
||||
public void RefreshTaskStates()
|
||||
{
|
||||
foreach (var item in taskPool)
|
||||
{
|
||||
if (item == null || item.Task == null)
|
||||
continue;
|
||||
|
||||
if (item.State == CliTaskState.TimedOut)
|
||||
continue;
|
||||
|
||||
if (!item.Task.IsCompleted)
|
||||
{
|
||||
item.State = CliTaskState.Running;
|
||||
}
|
||||
else if (item.Task.IsCanceled)
|
||||
{
|
||||
item.State = CliTaskState.Canceled;
|
||||
}
|
||||
else if (item.Task.IsFaulted)
|
||||
{
|
||||
item.State = CliTaskState.Faulted;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.State = CliTaskState.Completed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelUndoneTasksAsTimedOut()
|
||||
{
|
||||
foreach (var item in taskPool)
|
||||
{
|
||||
if (item == null || item.Task == null)
|
||||
continue;
|
||||
|
||||
if (item.Task.IsCompleted)
|
||||
{
|
||||
if (item.Task.IsCanceled)
|
||||
item.State = CliTaskState.Canceled;
|
||||
else if (item.Task.IsFaulted)
|
||||
item.State = CliTaskState.Faulted;
|
||||
else
|
||||
item.State = CliTaskState.Completed;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
item.State = CliTaskState.TimedOut;
|
||||
|
||||
try
|
||||
{
|
||||
item.Cts?.Cancel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log?.Warn("Cancel token failed", ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (item.Process != null && !item.Process.HasExited)
|
||||
{
|
||||
item.Process.Kill();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log?.Warn("Kill process failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public void AddSendAsync(SerialPortData data, SerialPortData.EMeterArg eMeterArg)
|
||||
{
|
||||
AddSendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
|
||||
var task = SendAsync(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
|
||||
taskPool.Add(task);
|
||||
}
|
||||
|
||||
|
||||
public void AddSendAsync(string fileName, string args)
|
||||
{
|
||||
ResetStartTime();
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var info = new CliTaskInfo
|
||||
{
|
||||
Cts = cts,
|
||||
Name = "SendAsync"
|
||||
};
|
||||
|
||||
var task = SendAsync(fileName, args, info, cts.Token);
|
||||
info.Task = task;
|
||||
taskPool.Add(info);
|
||||
var task = SendAsync(fileName, args);
|
||||
taskPool.Add(task);
|
||||
}
|
||||
|
||||
public async Task<string> SendAsync(string fileName, string args, CliTaskInfo info, CancellationToken ct = default)
|
||||
|
||||
|
||||
public async Task<string> SendAsync(string fileName, string args, CancellationToken ct = default)
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = args,
|
||||
WorkingDirectory = System.IO.Path.GetDirectoryName(System.IO.Path.GetFullPath(fileName)),
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
|
||||
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
||||
process.Start();
|
||||
|
||||
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
|
||||
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
|
||||
|
||||
try
|
||||
{
|
||||
info.Process = process;
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
|
||||
Task<string> stdOutTask = process.StandardOutput.ReadToEndAsync();
|
||||
Task<string> stdErrTask = process.StandardError.ReadToEndAsync();
|
||||
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (info.State != CliTaskState.TimedOut)
|
||||
info.State = CliTaskState.Canceled;
|
||||
|
||||
if (!process.HasExited)
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
incommingTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
|
||||
info.ExitCode = process.ExitCode;
|
||||
info.StandardOutput = stdOutTask.Result ?? "";
|
||||
info.StandardError = stdErrTask.Result ?? "";
|
||||
string allOutput = info.StandardOutput + info.StandardError;
|
||||
log?.Debug(allOutput);
|
||||
|
||||
if (info.ExitCode != 0)
|
||||
{
|
||||
info.FailureReason = $"CLI exited with exit code {info.ExitCode}.";
|
||||
log?.Error($"{info.Name}: {info.FailureReason} stderr='{info.StandardError}'");
|
||||
}
|
||||
|
||||
info.State = CliTaskState.Completed;
|
||||
return allOutput;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
info.State = CliTaskState.Faulted;
|
||||
log?.Error("SendAsync failed", ex);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
info.Process = null;
|
||||
}
|
||||
await Task.WhenAll(stdOutTask, stdErrTask, process.WaitForExitAsync(ct));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (!process.HasExited)
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
//comming answer from serial port - good place for time stamp
|
||||
incommingTime = DateTime.Now.Ticks;
|
||||
string allOutput = (stdOutTask.Result ?? "") + (stdErrTask.Result ?? "");
|
||||
log?.Debug(allOutput);
|
||||
return allOutput;
|
||||
}
|
||||
|
||||
|
||||
public void AddRunAndCaptureJsonAsync<T>(SerialPortData data, SerialPortData.EMeterArg eMeterArg) where T : new()
|
||||
{
|
||||
AddRunAndCaptureJsonAsync<T>(
|
||||
data.SerialPortCmdClientPath,
|
||||
data.DefaultArgSettings(eMeterArg));
|
||||
var task = RunAndCaptureJsonAsync<T>(data.SerialPortCmdClientPath, data.DefaultArgSettings(eMeterArg));
|
||||
taskPool.Add(task);
|
||||
}
|
||||
|
||||
public void AddRunAndCaptureJsonAsync<T>(string fileName, string args) where T : new()
|
||||
{
|
||||
// Tests deliberately create CliRunner without a logger. Starting a CLI task
|
||||
// must not depend on diagnostics being configured.
|
||||
log?.Debug($"CLI: {fileName}");
|
||||
log?.Debug($"ARGS: {args}");
|
||||
|
||||
ResetStartTime();
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var info = new CliTaskInfo
|
||||
{
|
||||
Cts = cts,
|
||||
Name = $"RunAndCaptureJsonAsync<{typeof(T).Name}>"
|
||||
};
|
||||
|
||||
var task = RunAndCaptureJsonAsync<T>(fileName, args, info, cts.Token);
|
||||
|
||||
info.Task = task;
|
||||
taskPool.Add(info);
|
||||
}
|
||||
|
||||
public async Task<T> RunAndCaptureJsonAsync<T>(string fileName, string args, CliTaskInfo info, CancellationToken ct = default) where T : new()
|
||||
|
||||
public async Task<T> RunAndCaptureJsonAsync<T>(string fileName, string args, CancellationToken ct = default) where T : new()
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = args,
|
||||
WorkingDirectory = System.IO.Path.GetDirectoryName(System.IO.Path.GetFullPath(fileName)),
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true })
|
||||
{
|
||||
info.Process = process;
|
||||
using var process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
||||
process.Start();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
log?.Debug($"FileName = '{psi.FileName}'");
|
||||
log?.Debug($"Arguments = '{psi.Arguments}'");
|
||||
log?.Debug($"WorkingDirectory = '{psi.WorkingDirectory}'");
|
||||
log?.Debug($"Exists = {System.IO.File.Exists(psi.FileName)}");
|
||||
|
||||
process.Start();
|
||||
var stdoutTask = process.StandardOutput.ReadToEndAsync();
|
||||
var stderrTask = process.StandardError.ReadToEndAsync();
|
||||
|
||||
var stdoutTask = process.StandardOutput.ReadToEndAsync();
|
||||
var stderrTask = process.StandardError.ReadToEndAsync();
|
||||
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct));
|
||||
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(stdoutTask, stderrTask, process.WaitForExitAsync(ct));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (info.State != CliTaskState.TimedOut)
|
||||
info.State = CliTaskState.Canceled;
|
||||
string allOutput = (stdoutTask.Result ?? "") + (stderrTask.Result ?? "");
|
||||
log?.Debug(allOutput);
|
||||
|
||||
if (!process.HasExited)
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
incommingTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
|
||||
info.ExitCode = process.ExitCode;
|
||||
info.StandardOutput = stdoutTask.Result ?? "";
|
||||
info.StandardError = stderrTask.Result ?? "";
|
||||
string allOutput = info.StandardOutput + info.StandardError;
|
||||
log?.Debug(allOutput);
|
||||
|
||||
if (info.ExitCode != 0)
|
||||
{
|
||||
info.FailureReason = $"CLI exited with exit code {info.ExitCode}.";
|
||||
log?.Error($"{info.Name}: {info.FailureReason} stderr='{info.StandardError}'");
|
||||
info.State = CliTaskState.Completed;
|
||||
return default(T);
|
||||
}
|
||||
|
||||
string json = ExtractJson(allOutput);
|
||||
T result;
|
||||
if (TryJsonStringDeserialize(json, out result))
|
||||
{
|
||||
info.State = CliTaskState.Completed;
|
||||
return result;
|
||||
}
|
||||
|
||||
info.FailureReason = "CLI completed without a valid JSON response.";
|
||||
log?.Error($"{info.Name}: {info.FailureReason} stdout='{info.StandardOutput}' stderr='{info.StandardError}'");
|
||||
info.State = CliTaskState.Completed;
|
||||
return default(T);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
info.State = CliTaskState.Faulted;
|
||||
log?.Error("RunAndCaptureJsonAsync failed", ex);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
info.Process = null;
|
||||
}
|
||||
}
|
||||
string json = ExtractJson(allOutput);
|
||||
if (TryJsonStringDeserialize(json, out T result)) return result;
|
||||
return default;
|
||||
}
|
||||
|
||||
public bool TryJsonStringDeserialize<T>(string json, out T value) where T : new()
|
||||
public bool TryJsonStringDeserialize<T>(string json, out T runAndCaptureJsonAsync) where T : new()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(json))
|
||||
if (json != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = JsonConvert.DeserializeObject<T>(json);
|
||||
return value != null;
|
||||
runAndCaptureJsonAsync = JsonConvert.DeserializeObject<T>(json);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log?.Debug(ex.Message);
|
||||
return TryConvert(json, out value);
|
||||
log.Debug(ex.Message);
|
||||
runAndCaptureJsonAsync = TryConvert<T>(json);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = default(T);
|
||||
runAndCaptureJsonAsync = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryConvert<T>(string json, out T value) where T : new()
|
||||
|
||||
private static T TryConvert<T>(string json) where T : new()
|
||||
{
|
||||
T obj = new T();
|
||||
|
||||
T obj = new T();
|
||||
|
||||
try
|
||||
{
|
||||
JObject jObject = JObject.Parse(json);
|
||||
|
||||
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
try
|
||||
{
|
||||
if (!prop.CanWrite)
|
||||
continue;
|
||||
JObject jObject = JObject.Parse(json);
|
||||
|
||||
JToken token;
|
||||
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
|
||||
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
try
|
||||
{
|
||||
object propertyValue = token.ToObject(prop.PropertyType);
|
||||
prop.SetValue(obj, propertyValue);
|
||||
}
|
||||
catch
|
||||
if (!prop.CanWrite) continue;
|
||||
|
||||
JToken token;
|
||||
if (jObject.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out token))
|
||||
{
|
||||
try
|
||||
{
|
||||
object value = token.ToObject(prop.PropertyType);
|
||||
prop.SetValue(obj, value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// leave default if conversion fails
|
||||
}
|
||||
}
|
||||
// else → keep default value
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"TryConvert failed: {ex.Message}");
|
||||
}
|
||||
|
||||
value = obj;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"TryConvert failed: {ex.Message}");
|
||||
value = default(T);
|
||||
return false;
|
||||
}
|
||||
return obj;
|
||||
|
||||
}
|
||||
|
||||
public string ExtractJson(string text)
|
||||
@@ -483,20 +241,6 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal void AddTaskForTest(Task task, string name = "TestTask", CancellationTokenSource cts = null)
|
||||
{
|
||||
taskPool.Add(new CliTaskInfo
|
||||
{
|
||||
Task = task,
|
||||
Cts = cts,
|
||||
Name = name,
|
||||
State = task.IsCompleted
|
||||
? (task.IsCanceled ? CliTaskState.Canceled :
|
||||
task.IsFaulted ? CliTaskState.Faulted :
|
||||
CliTaskState.Completed)
|
||||
: CliTaskState.Running
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
public class CliTaskInfo
|
||||
{
|
||||
public Task Task { get; set; }
|
||||
public CancellationTokenSource Cts { get; set; }
|
||||
public Process Process { get; set; }
|
||||
public CliTaskState State { get; set; } = CliTaskState.Running;
|
||||
public string Name { get; set; }
|
||||
public int? ExitCode { get; set; }
|
||||
public string StandardOutput { get; set; }
|
||||
public string StandardError { get; set; }
|
||||
public string FailureReason { get; set; }
|
||||
|
||||
public bool UseResult
|
||||
{
|
||||
get
|
||||
{
|
||||
return State == CliTaskState.Completed
|
||||
&& Task != null
|
||||
&& Task.Status == TaskStatus.RanToCompletion;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
public enum CliTaskState
|
||||
{
|
||||
Running,
|
||||
Completed,
|
||||
TimedOut,
|
||||
Canceled,
|
||||
Faulted
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
public static class GetDescription
|
||||
{
|
||||
public static string ToDescription(this Enum en)
|
||||
{
|
||||
Type type = en.GetType();
|
||||
MemberInfo[] memInfo = type.GetMember(en.ToString());
|
||||
|
||||
if (memInfo != null && memInfo.Length > 0)
|
||||
{
|
||||
object[] attrs = memInfo[0].GetCustomAttributes(
|
||||
typeof(DescriptionAttribute),
|
||||
false);
|
||||
|
||||
if (attrs != null && attrs.Length > 0)
|
||||
return ((DescriptionAttribute)attrs[0]).Description;
|
||||
}
|
||||
|
||||
return en.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,22 @@
|
||||
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
public class JsonDataFromPoseidon
|
||||
{
|
||||
public bool NfcTagDetected { get; set; }
|
||||
public bool ReadingComplete { get; set; }
|
||||
|
||||
public string DeviceId { get; set; }
|
||||
public string ProductType { get; set; }
|
||||
public bool? NfcTagDetected { get; set; }
|
||||
public int? ProductType { get; set; }
|
||||
public string ProductTypeVersion { get; set; }
|
||||
public string CliVersion { get; set; }
|
||||
|
||||
public string DeviceId { get; set; }
|
||||
public string Reading { get; set; }
|
||||
|
||||
public object Reading_Totalizer { get; set; }
|
||||
public object Reading_FlowRate { get; set; }
|
||||
public object Reading_FlowDirection { get; set; }
|
||||
public object Reading_Digits { get; set; }
|
||||
public object Reading_Shift { get; set; }
|
||||
public object Reading_Resolution { get; set; }
|
||||
public string Reading_Totalizer { get; set; }
|
||||
public string Reading_Digits { get; set; }
|
||||
public string Reading_Shift { get; set; }
|
||||
public string Reading_Resolution { get; set; }
|
||||
public string Reading_Units { get; set; }
|
||||
|
||||
public string MeterState { get; set; }
|
||||
public string Reading_FlowDirection { get; set; }
|
||||
public string Reading_FlowRate { get; set; }
|
||||
public string CalibrationFactor { get; set; }
|
||||
public string OpticalDataMode { get; set; }
|
||||
|
||||
public string BuildInformation { get; set; }
|
||||
public string ManufactureDate { get; set; }
|
||||
public string MeterSize { get; set; }
|
||||
public string FactoryId { get; set; }
|
||||
public string CustomerText { get; set; }
|
||||
|
||||
public string TestTotalizer { get; set; }
|
||||
public string FlowUnits { get; set; }
|
||||
public string ReadingUnits { get; set; }
|
||||
|
||||
public string SpreadSpectrumParameters { get; set; }
|
||||
|
||||
public string SpreadSpect_DisableSpreadSpectrum { get; set; }
|
||||
public string SpreadSpect_AlwaysUseFullRateBags { get; set; }
|
||||
public string SpreadSpect_PowerUp { get; set; }
|
||||
public string SpreadSpect_HighNoise { get; set; }
|
||||
public string SpreadSpect_EmptyPipe { get; set; }
|
||||
public string SpreadSpect_BadAdc { get; set; }
|
||||
public string SpreadSpect_InitLearningCompleteCount { get; set; }
|
||||
public string SpreadSpect_MinOffsetLearningSampleCount { get; set; }
|
||||
public string SpreadSpect_AdcShiftUpdateInterval { get; set; }
|
||||
public string SpreadSpect_MaxPreviousAdcAge { get; set; }
|
||||
public string SpreadSpect_AdcOffsetLearningGuard { get; set; }
|
||||
public string SpreadSpect_DisplacementCorrectionCount { get; set; }
|
||||
public bool? ReadingComplete { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,10 @@
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO.Ports;
|
||||
using System.Xml.Serialization;
|
||||
using TBF.Rig.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Common;
|
||||
using TBF.Rig.Generic;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
@@ -24,22 +22,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
///
|
||||
public int ComPortNr;
|
||||
public string CliFileName;
|
||||
|
||||
[XmlIgnore]
|
||||
public SerialPortData.MeterProduct MeterType = SerialPortData.MeterProduct.Poseidon;
|
||||
|
||||
[XmlElement("MeterProduct")]
|
||||
public string MeterProductXml
|
||||
{
|
||||
get { return SerialPortData.MeterToString(MeterType); }
|
||||
set { MeterType = SerialPortData.StringToMeter(value); }
|
||||
}
|
||||
|
||||
public SerialPortData.HatType HatType = SerialPortData.HatType.Mth;
|
||||
public int TimeOut = 30;
|
||||
public string MainMacro = SerialPortData.MacroTypes.readall.ToDescription();
|
||||
|
||||
|
||||
public int MeterType;
|
||||
|
||||
|
||||
/// <summary> Procedure parameters </summary>
|
||||
@@ -48,68 +31,14 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
|
||||
public override IParamsProvider CreateProcParamsProvider() { return new PoseidonProcParams(true); }
|
||||
|
||||
public static bool TryParseMeter(
|
||||
string description,
|
||||
out SerialPortData.MeterProduct meter)
|
||||
{
|
||||
var found = Enum.GetValues(typeof(SerialPortData.MeterProduct))
|
||||
.Cast<SerialPortData.MeterProduct>()
|
||||
.FirstOrDefault(e =>
|
||||
string.Equals(
|
||||
e.GetType()
|
||||
.GetField(e.ToString())
|
||||
.GetCustomAttribute<DescriptionAttribute>()
|
||||
?.Description,
|
||||
description,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!Equals(found, default(SerialPortData.MeterProduct)) ||
|
||||
string.Equals(description, "Poseidon", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
meter = found;
|
||||
return true;
|
||||
}
|
||||
|
||||
meter = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryParseMacro(
|
||||
string description,
|
||||
out string macro)
|
||||
{
|
||||
var found = Enum.GetValues(typeof(SerialPortData.MacroTypes))
|
||||
.Cast<SerialPortData.MacroTypes>()
|
||||
.FirstOrDefault(e =>
|
||||
string.Equals(
|
||||
e.GetType()
|
||||
.GetField(e.ToString())
|
||||
.GetCustomAttribute<DescriptionAttribute>()
|
||||
?.Description,
|
||||
description,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!Equals(found, default(SerialPortData.MacroTypes)) ||
|
||||
string.Equals(description, "readall", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
macro = found.ToDescription();
|
||||
return true;
|
||||
}
|
||||
|
||||
macro = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
string[] paramNames = new string[]
|
||||
{
|
||||
"Serial port number", /// 0
|
||||
"CLI program name", /// 1
|
||||
"Meter Type", /// 2
|
||||
"Hat Type", /// 3
|
||||
"TimeOut [s]", /// 4
|
||||
"Main Macro" /// 5
|
||||
};
|
||||
|
||||
public string ParamName(int i) { return paramNames[i]; }
|
||||
public int ParamsCount() { return paramNames.Length; }
|
||||
|
||||
@@ -122,27 +51,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
case 1:
|
||||
return new string[] { "HatCLI.exe"};
|
||||
case 2:
|
||||
return new string[] {
|
||||
SerialPortData.MeterProduct.Poseidon.ToDescription() ,
|
||||
SerialPortData.MeterProduct.Ally.ToDescription(),
|
||||
SerialPortData.MeterProduct.IperlPlus.ToDescription(),
|
||||
SerialPortData.MeterProduct.IperlLegacy.ToDescription()
|
||||
};
|
||||
case 3:
|
||||
return new string[]
|
||||
{
|
||||
SerialPortData.HatType.Mth.ToDescription(),
|
||||
SerialPortData.HatType.Harry.ToDescription()
|
||||
};
|
||||
case 4:
|
||||
return new string[] { "30", "60", "90"};
|
||||
case 5:
|
||||
string macro0 = SerialPortData.MacroTypes.readall.ToDescription();
|
||||
string macro1 = SerialPortData.MacroTypes.macro1.ToDescription();
|
||||
string macro2 = SerialPortData.MacroTypes.macro2.ToDescription();
|
||||
string macro3 = SerialPortData.MacroTypes.macro3.ToDescription();
|
||||
|
||||
return new string[] { macro0, macro1, macro2, macro3 };
|
||||
return new string[] { "74" };
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -150,15 +60,12 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
|
||||
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities) { return new Configs.ParamsProvider.ComponentCfgCtrl(this, null); }
|
||||
|
||||
|
||||
|
||||
public void InitializeAll()
|
||||
{
|
||||
ComPortNr = 3;
|
||||
CliFileName = "HatCLI.exe";
|
||||
MeterType = SerialPortData.MeterProduct.Poseidon;
|
||||
HatType = SerialPortData.HatType.Mth;
|
||||
TimeOut = 30;
|
||||
MainMacro = SerialPortData.MacroTypes.readall.ToDescription();
|
||||
CliFileName = "HatCLI.exe";
|
||||
MeterType = 74;
|
||||
}
|
||||
|
||||
public bool ValidateParam(int i, string strValue, out string message)
|
||||
@@ -176,17 +83,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
if(System.Text.RegularExpressions.Regex.IsMatch(strValue, @"^[a-zA-Z0-9_-]+\.exe$")) return true; //validate exe file name
|
||||
break;
|
||||
case 2:
|
||||
if (TryParseMeter(strValue, out var meter)) return true;
|
||||
break;
|
||||
case 3:
|
||||
if (TryParseHatType(strValue, out var hatType)) return true;
|
||||
break;
|
||||
case 4:
|
||||
if (int.TryParse(strValue, out idummy) && idummy > 0) return true;
|
||||
break;
|
||||
case 5:
|
||||
if (TryParseMacro(strValue, out string macroType)) return true;
|
||||
break;
|
||||
default:
|
||||
message = "Invalid index";
|
||||
return false;
|
||||
@@ -202,38 +100,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
case 0: ComPortNr = int.Parse(str); return CfgUpdateFlags.RestartRqrd;
|
||||
case 1: CliFileName = str; return CfgUpdateFlags.RestartRqrd;
|
||||
case 2: TryParseMeter(str, out MeterType); return CfgUpdateFlags.RestartRqrd;
|
||||
case 3: TryParseHatType(str, out HatType); return CfgUpdateFlags.RestartRqrd;
|
||||
case 4: TimeOut = int.Parse(str); return CfgUpdateFlags.RestartRqrd;
|
||||
case 5: MainMacro = str; return CfgUpdateFlags.RestartRqrd;
|
||||
case 2: MeterType = int.Parse(str); return CfgUpdateFlags.RestartRqrd;
|
||||
default: return CfgUpdateFlags.None;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryParseHatType(string description, out SerialPortData.HatType hatType)
|
||||
{
|
||||
var found = Enum.GetValues(typeof(SerialPortData.HatType))
|
||||
.Cast<SerialPortData.HatType>()
|
||||
.FirstOrDefault(e =>
|
||||
string.Equals(
|
||||
e.GetType()
|
||||
.GetField(e.ToString())
|
||||
.GetCustomAttribute<DescriptionAttribute>()
|
||||
?.Description,
|
||||
description,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!Equals(found, default(SerialPortData.HatType)) ||
|
||||
string.Equals(description, "Mth", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
hatType = found;
|
||||
return true;
|
||||
}
|
||||
|
||||
hatType = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool UpdateEmbeddedDbEntity()
|
||||
{
|
||||
return true; /// =OK, do nothing
|
||||
@@ -268,12 +139,9 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
case 0: return ComPortNr.ToString();
|
||||
case 1: return CliFileName;
|
||||
case 2: return MeterType.ToString();
|
||||
case 3: return HatType.ToString();
|
||||
case 4: return TimeOut.ToString();
|
||||
case 5: return MainMacro;
|
||||
default:
|
||||
return string.Format("{0}: Com{1}, CliName =`{2}`, MeterType = {3}, HatType = {4}, TimeOut = {5}",
|
||||
Name, ComPortNr, CliFileName, MeterType, HatType, TimeOut);
|
||||
return string.Format("{0}: Com{1}, CliName =`{2}`, MeterType = {3}",
|
||||
Name, ComPortNr, CliFileName, MeterType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,9 +150,6 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
prms.ComPortNr = this.ComPortNr;
|
||||
prms.CliFileName = this.CliFileName;
|
||||
prms.MeterType = this.MeterType;
|
||||
prms.HatType = this.HatType;
|
||||
prms.TimeOut = this.TimeOut;
|
||||
prms.MainMacro = this.MainMacro;
|
||||
prms.ProcParams = this.ProcParams;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TBF.Rig;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
/// <summary>
|
||||
/// A single polling step for the Poseidon start/end read dialog.
|
||||
///
|
||||
/// A reader is armed only from None. In particular, a terminal reader
|
||||
/// must not be armed again: doing so starts a new CLI process on every
|
||||
/// polling iteration and leaves the dialog in Loading.
|
||||
/// </summary>
|
||||
public interface IPoseidonReadOperation
|
||||
{
|
||||
string Name { get; }
|
||||
bool IsNotStarted { get; }
|
||||
bool IsFinished { get; }
|
||||
bool HasError { get; }
|
||||
void Start(bool readStart);
|
||||
Event Run();
|
||||
}
|
||||
|
||||
public sealed class PoseidonReaderOperation : IPoseidonReadOperation
|
||||
{
|
||||
private readonly PoseidonReader reader;
|
||||
|
||||
public PoseidonReaderOperation(PoseidonReader reader)
|
||||
{
|
||||
if (reader == null) throw new ArgumentNullException(nameof(reader));
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public string Name { get { return reader.Name; } }
|
||||
public bool IsNotStarted { get { return reader.CurrentOp == PoseidonReader.CurrentPoseidonOp.None; } }
|
||||
public bool IsFinished
|
||||
{
|
||||
get
|
||||
{
|
||||
return reader.CurrentOp == PoseidonReader.CurrentPoseidonOp.Done ||
|
||||
reader.CurrentOp == PoseidonReader.CurrentPoseidonOp.Error;
|
||||
}
|
||||
}
|
||||
public bool HasError { get { return reader.CurrentOp == PoseidonReader.CurrentPoseidonOp.Error; } }
|
||||
public void Start(bool readStart)
|
||||
{
|
||||
reader.SetCurrentOp(readStart
|
||||
? PoseidonReader.CurrentPoseidonOp.ReadDataStream_Start
|
||||
: PoseidonReader.CurrentPoseidonOp.ReadDataStream_End);
|
||||
}
|
||||
public Event Run() { return reader.Run(); }
|
||||
}
|
||||
|
||||
public static class PoseidonReadCycle
|
||||
{
|
||||
public static bool RunIteration(IEnumerable<IPoseidonReadOperation> readers, bool readStart)
|
||||
{
|
||||
if (readers == null)
|
||||
return true;
|
||||
|
||||
bool allReadersFinished = true;
|
||||
foreach (IPoseidonReadOperation reader in readers)
|
||||
{
|
||||
if (reader == null)
|
||||
continue;
|
||||
|
||||
// None means that this dialog operation has not been started yet.
|
||||
// Done/Error are terminal and deliberately remain terminal.
|
||||
if (reader.IsNotStarted)
|
||||
reader.Start(readStart);
|
||||
|
||||
reader.Run();
|
||||
|
||||
if (!reader.IsFinished)
|
||||
allReadersFinished = false;
|
||||
}
|
||||
|
||||
return allReadersFinished;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Owns one logical dialog phase. A new phase must arm a reader even when
|
||||
/// the preceding phase left it in Done/Error; within this phase it is armed
|
||||
/// only once.
|
||||
/// </summary>
|
||||
public sealed class PoseidonReadPhaseRunner
|
||||
{
|
||||
private readonly bool readStart;
|
||||
private readonly HashSet<IPoseidonReadOperation> startedReaders =
|
||||
new HashSet<IPoseidonReadOperation>();
|
||||
|
||||
public PoseidonReadPhaseRunner(bool readStart)
|
||||
{
|
||||
this.readStart = readStart;
|
||||
}
|
||||
|
||||
public bool RunIteration(IEnumerable<IPoseidonReadOperation> readers)
|
||||
{
|
||||
if (readers == null)
|
||||
return true;
|
||||
|
||||
bool allReadersFinished = true;
|
||||
foreach (IPoseidonReadOperation reader in readers)
|
||||
{
|
||||
if (reader == null)
|
||||
continue;
|
||||
|
||||
if (startedReaders.Add(reader))
|
||||
reader.Start(readStart);
|
||||
|
||||
reader.Run();
|
||||
if (!reader.IsFinished)
|
||||
allReadersFinished = false;
|
||||
}
|
||||
|
||||
return allReadersFinished;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Ports;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -25,15 +24,14 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
public class PoseidonReader : ComponentBase, IDevice, IRegReaderDatastream, ISessionDataMngmnt, IOperation, ICommonRegReader
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(PoseidonReader));
|
||||
// The simulator must never open the configured physical Hat CLI. The
|
||||
// deterministic test CLI returns a valid Poseidon JSON response instead.
|
||||
internal const string SimulatedCliFileName = "cmdSleepTest.exe";
|
||||
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
||||
|
||||
readonly PoseidonCfg registerReaderCfg;
|
||||
readonly ControlBoard.IControlBoard controlBoard;
|
||||
|
||||
|
||||
|
||||
public int ComPortNr => registerReaderCfg?.ComPortNr ?? -1;
|
||||
public PoseidonCfg RegPoseidonCfg => registerReaderCfg;
|
||||
|
||||
private bool activeHandlerSessioEnabled = false;
|
||||
private CliRunner _cliRunner;
|
||||
|
||||
@@ -284,23 +282,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
if ((DebugLevel == DebugMode.Normal)||(DebugLevel == DebugMode.Simulate))
|
||||
{
|
||||
/// Prepare serial port
|
||||
string cliFileName = GetCliFileNameForMode(DebugLevel, registerReaderCfg.CliFileName);
|
||||
serialPort = new SerialPortData(string.Format("COM{0}", registerReaderCfg.ComPortNr),
|
||||
cliFileName,
|
||||
registerReaderCfg.MeterType, //registerReaderCfg.MeterType, //MeterProduct.Poseidon
|
||||
registerReaderCfg.HatType,//HatType.Mth
|
||||
registerReaderCfg.TimeOut, //timeoutSeconds
|
||||
registerReaderCfg.MainMacro
|
||||
);
|
||||
registerReaderCfg.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();
|
||||
//we can check file program if exists
|
||||
@@ -321,13 +307,6 @@ 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);
|
||||
@@ -353,21 +332,13 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
/// for debug purposes what time will consume answer
|
||||
/// </summary>
|
||||
private long startTimeInMilis = -1, fullTimeInMilis = -1;
|
||||
/// 30 seconds
|
||||
private static long SafetyTimeOut = 30 * 1000;
|
||||
private long incommingTime = -1;
|
||||
private bool _lastOpTimedOut;
|
||||
private bool lastCliReadingParsed;
|
||||
private string lastCliReadFailureReason;
|
||||
private bool lastCliReadSucceeded;
|
||||
|
||||
|
||||
/// 30 seconds
|
||||
|
||||
public long DeltaTime { get{return fullTimeInMilis;}}
|
||||
public long IncommingTime { get{return incommingTime;}}
|
||||
public bool LastCliReadingParsed { get { return lastCliReadingParsed; } }
|
||||
public string LastCliReadFailureReason { get { return lastCliReadFailureReason; } }
|
||||
public bool LastCliReadSucceeded { get { return lastCliReadSucceeded; } }
|
||||
|
||||
/// <summary>Run this operation</summary>
|
||||
/// <returns>eventDone</returns>
|
||||
@@ -381,262 +352,108 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
|
||||
if (_currentOp == CurrentPoseidonOp.SendStartDataStream)
|
||||
{
|
||||
CliRunner.Clear();
|
||||
_lastOpTimedOut = false;
|
||||
CliRunner.AddSendAsync(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
_currentOp = CurrentPoseidonOp.SendStartDataStream_Runing;
|
||||
return Event.Busy;
|
||||
return Event.Busy;
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Runing)
|
||||
{
|
||||
if (CliRunner.AreTasksDone())
|
||||
if (CliRunner.AreTasksDone()
|
||||
|| CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||
{
|
||||
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
|
||||
}
|
||||
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||
{
|
||||
_lastOpTimedOut = true;
|
||||
CliRunner.CancelUndoneTasksAsTimedOut();
|
||||
_currentOp = CurrentPoseidonOp.SendStartDataStream_Done;
|
||||
}
|
||||
|
||||
return Event.Busy;
|
||||
return Event.Busy;
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.SendStartDataStream_Done)
|
||||
{
|
||||
var firstTaskInfo = CliRunner.TaskPool
|
||||
.FindLast(t => t.Task is Task<string> && t.UseResult);
|
||||
var firstTask = CliRunner.TaskPool.FindLast(t => t is Task<string>);
|
||||
|
||||
if (firstTaskInfo != null)
|
||||
|
||||
if (firstTask != null && firstTask is Task<string>)
|
||||
{
|
||||
var task = (Task<string>)firstTaskInfo.Task;
|
||||
string data = task.Result;
|
||||
string data = null;
|
||||
data = (firstTask as Task<string>).Result;
|
||||
|
||||
if (!string.IsNullOrEmpty(data))
|
||||
if (data != null && TryGetDeviceId(data, out wmSerialNr))
|
||||
{
|
||||
TryGetDeviceId(data, out wmSerialNr);
|
||||
|
||||
}
|
||||
}
|
||||
else if (_lastOpTimedOut)
|
||||
{
|
||||
log.Warn(
|
||||
$"PoseidonReader {Name}: SendStartDataStream timed out, no completed result will be used.");
|
||||
}
|
||||
|
||||
CliRunner.Clear();
|
||||
_currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
|
||||
_currentOp = CurrentPoseidonOp.Done;
|
||||
return Event.Done;
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
|
||||
else if (_currentOp == CurrentPoseidonOp.ReadDataStream_Start
|
||||
|| _currentOp == CurrentPoseidonOp.ReadDataStream_End)
|
||||
{
|
||||
lastCliReadingParsed = false;
|
||||
lastCliReadFailureReason = null;
|
||||
lastCliReadSucceeded = false;
|
||||
startTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
startTimeInMilis = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
|
||||
incommingTime = -1;
|
||||
_isReadingStart = (_currentOp == CurrentPoseidonOp.ReadDataStream_Start);
|
||||
|
||||
CliRunner.Clear();
|
||||
_lastOpTimedOut = false;
|
||||
log.DebugFormat("{0}: starting CLI read, direction={1}, path='{2}', args='{3}'",
|
||||
Name,
|
||||
_isReadingStart ? "start" : "end",
|
||||
serialPort == null ? "<not initialized>" : serialPort.SerialPortCmdClientPath,
|
||||
serialPort == null ? "<not initialized>" : serialPort.DefaultArgSettings(SerialPortData.EMeterArg.AllParams));
|
||||
CliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
CliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
_currentOp = CurrentPoseidonOp.ReadDatastream_Running;
|
||||
return Event.Busy;
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
|
||||
}else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Running)
|
||||
{
|
||||
if (CliRunner.AreTasksDone())
|
||||
if (CliRunner.AreTasksDone()
|
||||
|| CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||
{
|
||||
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
|
||||
incommingTime = CliRunner.IncommingTime;
|
||||
log.DebugFormat("{0}: CLI read task finished, taskCount={1}, incomingTime={2}",
|
||||
Name, CliRunner.TaskPool.Count, incommingTime);
|
||||
}
|
||||
else if (CliRunner.TimeOutReceived(SafetyTimeOut))
|
||||
{
|
||||
_lastOpTimedOut = true;
|
||||
CliRunner.CancelUndoneTasksAsTimedOut();
|
||||
_currentOp = CurrentPoseidonOp.ReadDatastream_Done;
|
||||
//set time stamp - end of reading
|
||||
incommingTime = CliRunner.IncommingTime;
|
||||
}
|
||||
|
||||
return Event.Busy;
|
||||
}
|
||||
else if (_currentOp == CurrentPoseidonOp.ReadDatastream_Done)
|
||||
{
|
||||
fullTimeInMilis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTimeInMilis;
|
||||
log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis}");
|
||||
|
||||
fullTimeInMilis = (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - startTimeInMilis;
|
||||
log.Debug($"PoseidonReader.Run() Name= {Cfg.Name} fullTimeInMilis = {fullTimeInMilis} ");
|
||||
JsonDataFromPoseidon data = null;
|
||||
var first = CliRunner.TaskPool.FindLast(t => t is Task<JsonDataFromPoseidon>);
|
||||
if (first != null && first is Task<JsonDataFromPoseidon>)
|
||||
{
|
||||
data = (first as Task<JsonDataFromPoseidon>).Result;
|
||||
|
||||
var firstTaskInfo = CliRunner.TaskPool
|
||||
.FindLast(t => t.Task is Task<JsonDataFromPoseidon> && t.UseResult);
|
||||
|
||||
if (firstTaskInfo != null)
|
||||
{
|
||||
var task = (Task<JsonDataFromPoseidon>)firstTaskInfo.Task;
|
||||
data = task.Result;
|
||||
if (data == null)
|
||||
{
|
||||
lastCliReadFailureReason = firstTaskInfo.FailureReason ?? "CLI returned no Poseidon JSON data.";
|
||||
log.ErrorFormat("{0}: Poseidon {1} read failed. reason='{2}', exitCode={3}, stderr='{4}'",
|
||||
Name,
|
||||
_isReadingStart ? "Begin" : "End",
|
||||
lastCliReadFailureReason,
|
||||
firstTaskInfo.ExitCode,
|
||||
firstTaskInfo.StandardError);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var lastTaskInfo = CliRunner.TaskPool.FindLast(t => t.Task is Task<JsonDataFromPoseidon>);
|
||||
lastCliReadFailureReason = lastTaskInfo == null
|
||||
? "No CLI task was created."
|
||||
: lastTaskInfo.FailureReason ?? "CLI task was not usable, state=" + lastTaskInfo.State + ".";
|
||||
if (_lastOpTimedOut)
|
||||
{
|
||||
log.Warn($"PoseidonReader {Name}: ReadDatastream timed out, no completed result available. {lastCliReadFailureReason}");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Error($"PoseidonReader {Name}: no usable JsonDataFromPoseidon task. {lastCliReadFailureReason}");
|
||||
}
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
log.DebugFormat("{0}: CLI read result deviceId={1}, reading={2}, readingComplete={3}, nfcTagDetected={4}",
|
||||
Name, data.DeviceId, data.Reading, data.ReadingComplete, data.NfcTagDetected);
|
||||
string validationError;
|
||||
if (!TryValidateCliReadResponse(data, out validationError))
|
||||
{
|
||||
lastCliReadFailureReason = validationError;
|
||||
log.ErrorFormat("{0}: Poseidon {1} read rejected. {2}. Begin={3}, End={4}",
|
||||
Name, _isReadingStart ? "Begin" : "End", validationError, beginWMState, endWMState);
|
||||
}
|
||||
else
|
||||
{
|
||||
//GET serial number
|
||||
if (string.IsNullOrEmpty(wmSerialNr))
|
||||
{
|
||||
try
|
||||
{
|
||||
wmSerialNr = data.DeviceId ?? wmSerialNr;
|
||||
wmSerialNr = data?.DeviceId ?? wmSerialNr;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error("Serial Nr - parse error!", e);
|
||||
log.Error("Serial Nr - parse error!");
|
||||
}
|
||||
}
|
||||
|
||||
double volumeLi;
|
||||
string dialogValueFailureReason;
|
||||
if (TryGetDialogValue(data, out volumeLi, out dialogValueFailureReason))
|
||||
//GET volume
|
||||
if (data != null && Double.TryParse(data.Reading, out double Volume))
|
||||
{
|
||||
lastCliReadingParsed = true;
|
||||
lastCliReadSucceeded = true;
|
||||
double volume;
|
||||
TryParseCliReading(data.Reading, out volume);
|
||||
|
||||
double VolumeLi = Units.ConvertFrom(Unit.USgal, Volume);
|
||||
//double VolumeM3 = Units.ConvertTo(Unit.m3, VolumeLi);
|
||||
if (_isReadingStart)
|
||||
beginWMState = volumeLi;
|
||||
{
|
||||
//volume
|
||||
beginWMState = VolumeLi;
|
||||
}
|
||||
else
|
||||
endWMState = volumeLi;
|
||||
{
|
||||
//volume
|
||||
endWMState = VolumeLi;
|
||||
}
|
||||
}
|
||||
|
||||
log.InfoFormat("{0}: Poseidon {1} value stored. deviceId={2}, rawReading='{3}', gallons={4}, litres={5}, Begin={6}, End={7}",
|
||||
Name, _isReadingStart ? "Begin" : "End", data.DeviceId, data.Reading, volume, volumeLi, beginWMState, endWMState);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastCliReadFailureReason = dialogValueFailureReason;
|
||||
log.ErrorFormat("{0}: cannot parse CLI reading '{1}' using invariant or current culture.",
|
||||
Name, data.Reading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CliRunner.Clear();
|
||||
_currentOp = _lastOpTimedOut ? CurrentPoseidonOp.Error : CurrentPoseidonOp.Done;
|
||||
|
||||
//Finish reading and loop
|
||||
_currentOp = CurrentPoseidonOp.Done;
|
||||
return Event.Done;
|
||||
}
|
||||
|
||||
return Event.None;
|
||||
|
||||
return Event.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CLI values must not depend on the Windows UI culture. Both dot and comma
|
||||
/// are accepted as a decimal separator and normalized before parsing.
|
||||
/// </summary>
|
||||
public static bool TryParseCliReading(string reading, out double value)
|
||||
{
|
||||
value = 0;
|
||||
if (String.IsNullOrWhiteSpace(reading))
|
||||
return false;
|
||||
|
||||
string normalizedReading = reading.Trim().Replace(',', '.');
|
||||
return Double.TryParse(normalizedReading, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a validated CLI response to the value written into the
|
||||
/// start/end dialog. Poseidon reports US gallons; TBF stores litres.
|
||||
/// </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;
|
||||
}
|
||||
|
||||
if (!data.NfcTagDetected)
|
||||
{
|
||||
failureReason = "NfcTagDetected=false.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.ReadingComplete)
|
||||
{
|
||||
failureReason = "ReadingComplete=false.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(data.Reading))
|
||||
{
|
||||
failureReason = "Reading is empty.";
|
||||
return false;
|
||||
}
|
||||
|
||||
failureReason = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Stop this operation</summary>
|
||||
public void Stop()
|
||||
{
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
public class SerialPortData
|
||||
{
|
||||
public const string CliDirectory = @"C:\TBF\Cli";
|
||||
|
||||
private Boolean? _cliExists;
|
||||
public bool CliExists { get {
|
||||
if (_cliExists == null || !_cliExists.HasValue)
|
||||
@@ -18,245 +13,39 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
}
|
||||
return _cliExists.Value;
|
||||
} }
|
||||
public string SerialPortCmdClientPath
|
||||
{
|
||||
get
|
||||
{
|
||||
// The configured value is a file name, not an executable path.
|
||||
// Keeping the CLI directory fixed prevents a bench configuration
|
||||
// from starting an unintended executable or failing due to a
|
||||
// relative path/current-directory change.
|
||||
string cliFileName = Path.GetFileName(CmdClientName);
|
||||
if (string.IsNullOrWhiteSpace(cliFileName))
|
||||
cliFileName = "HalCli.exe";
|
||||
|
||||
return Path.Combine(CliDirectory, cliFileName);
|
||||
}
|
||||
}
|
||||
public string SerialPortCmdClientPath {
|
||||
#if DEBUG
|
||||
get { return Path.Combine("C:\\","TBF","Cli", CmdClientName);}
|
||||
#else
|
||||
get { return Path.Combine("..","Cli", CmdClientName);}
|
||||
#endif
|
||||
}
|
||||
public string CmdClientName { get; set; } = "HalCli.exe";
|
||||
public string PortName { get; set; }
|
||||
// Backward compatibility: old numeric meter type
|
||||
public int? OldMeterType { get; set; }
|
||||
public int MeterType { get; set; }
|
||||
|
||||
// New v2.0 parameters
|
||||
public HatType Hat { get; set; } = HatType.Mth;
|
||||
public int TimeoutSeconds { get; set; } = 10;
|
||||
public string Macro { get; set; } = "readall";
|
||||
|
||||
[XmlIgnore]
|
||||
public MeterProduct Meter { get; set; } = MeterProduct.Poseidon;
|
||||
|
||||
[XmlElement("MeterProduct")]
|
||||
public string MeterXml
|
||||
{
|
||||
get { return MeterToString(Meter); }
|
||||
set { Meter = StringToMeter(value); }
|
||||
}
|
||||
|
||||
public static string MeterToString(MeterProduct meter)
|
||||
{
|
||||
switch (meter)
|
||||
{
|
||||
case MeterProduct.Poseidon:
|
||||
return "Poseidon"; //Poseidon
|
||||
case MeterProduct.Ally:
|
||||
return "ally";
|
||||
case MeterProduct.IperlPlus:
|
||||
return "iperlplus";
|
||||
case MeterProduct.IperlLegacy:
|
||||
return "iperllegacy";
|
||||
default:
|
||||
return "74";
|
||||
}
|
||||
}
|
||||
|
||||
public static MeterProduct StringToMeter(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return MeterProduct.Poseidon;
|
||||
|
||||
// old numeric values: 74, 7889, etc.
|
||||
if (int.TryParse(value, out _))
|
||||
return MeterProduct.Poseidon;
|
||||
|
||||
if (string.Equals(value, "cTouchRead", StringComparison.OrdinalIgnoreCase))
|
||||
return MeterProduct.Poseidon;
|
||||
|
||||
if (string.Equals(value, "Poseidon", StringComparison.OrdinalIgnoreCase))
|
||||
return MeterProduct.Poseidon;
|
||||
|
||||
if (string.Equals(value, "ally", StringComparison.OrdinalIgnoreCase))
|
||||
return MeterProduct.Ally;
|
||||
|
||||
if (string.Equals(value, "iperlplus", StringComparison.OrdinalIgnoreCase))
|
||||
return MeterProduct.IperlPlus;
|
||||
|
||||
if (string.Equals(value, "iperllegacy", StringComparison.OrdinalIgnoreCase))
|
||||
return MeterProduct.IperlLegacy;
|
||||
|
||||
return MeterProduct.Poseidon;
|
||||
}
|
||||
|
||||
public static bool TryParseMeter(
|
||||
string description,
|
||||
out SerialPortData.MeterProduct meter)
|
||||
{
|
||||
foreach (SerialPortData.MeterProduct value in
|
||||
Enum.GetValues(typeof(SerialPortData.MeterProduct)))
|
||||
{
|
||||
var attr = value.GetType()
|
||||
.GetField(value.ToString())
|
||||
.GetCustomAttribute<DescriptionAttribute>();
|
||||
|
||||
if (string.Equals(attr?.Description, description, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(value.ToString(), description, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
meter = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
meter = SerialPortData.MeterProduct.Poseidon;
|
||||
return false;
|
||||
}
|
||||
|
||||
public enum MacroTypes
|
||||
{
|
||||
[Description("readall")]
|
||||
readall = 0,
|
||||
[Description("readmacro1")]
|
||||
macro1 = 1,
|
||||
[Description("readmacro2")]
|
||||
macro2 = 2,
|
||||
[Description("readmacro3")]
|
||||
macro3 = 3
|
||||
}
|
||||
|
||||
public enum HatType
|
||||
{
|
||||
[Description("Mth")]
|
||||
Mth = 0,
|
||||
[Description("Harry")]
|
||||
Harry = 1
|
||||
}
|
||||
|
||||
public enum MeterProduct
|
||||
{
|
||||
[Description("Poseidon")]
|
||||
[XmlEnum("74")]
|
||||
Poseidon = 74,
|
||||
|
||||
[Description("ally")]
|
||||
[XmlEnum("ally")]
|
||||
Ally = 75,
|
||||
|
||||
[Description("iperlplus")]
|
||||
[XmlEnum("iperlplus")]
|
||||
IperlPlus = 76,
|
||||
|
||||
[Description("iperllegacy")]
|
||||
[XmlEnum("iperllegacy")]
|
||||
IperlLegacy = 77
|
||||
}
|
||||
|
||||
public enum EMeterArg
|
||||
{
|
||||
public enum EMeterArg {
|
||||
Calibration = 0,
|
||||
AllParams = 2,
|
||||
DeviceId = 3,
|
||||
|
||||
// New macro operations - just waste
|
||||
ReadMacro1 = 10,
|
||||
ReadMacro2 = 11,
|
||||
ReadMacro3 = 12
|
||||
}
|
||||
}
|
||||
EMeterArg _eMeterArg = EMeterArg.AllParams;
|
||||
|
||||
public string DefaultArgSettings(EMeterArg eMeterArg)
|
||||
public string DefaultArgSettings(EMeterArg eMeterArg)
|
||||
{
|
||||
string commonArgs = BuildCommonArgs();
|
||||
|
||||
switch (eMeterArg)
|
||||
{
|
||||
case EMeterArg.Calibration:
|
||||
return $"{commonArgs} --operation write --parameter Calibration --value 1";
|
||||
|
||||
case EMeterArg.DeviceId:
|
||||
return $"{commonArgs} --operation read --parameter DeviceId";
|
||||
|
||||
return $"-p {PortName} -m {MeterType} --operation write --parameter Calibration --value 1";
|
||||
case EMeterArg.AllParams:
|
||||
case EMeterArg.ReadMacro1:
|
||||
case EMeterArg.ReadMacro2:
|
||||
case EMeterArg.ReadMacro3:
|
||||
return $"{commonArgs} --operation {Macro}";
|
||||
return $"-p {PortName} -m {MeterType} --operation readall";
|
||||
case EMeterArg.DeviceId:
|
||||
return $"-p {PortName} -m {MeterType} --operation read --parameter DeviceId";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildCommonArgs()
|
||||
{
|
||||
return $"-p {PortName} " +
|
||||
$"-m {GetMeterArgument()} " +
|
||||
$"--hat {GetHatArgument()} " +
|
||||
$"--timeout {TimeoutSeconds}";
|
||||
}
|
||||
|
||||
private string GetMeterArgument()
|
||||
{
|
||||
// Old compatibility mode
|
||||
if (OldMeterType.HasValue)
|
||||
return OldMeterType.Value.ToString();
|
||||
|
||||
switch (Meter)
|
||||
{
|
||||
case MeterProduct.Poseidon:
|
||||
return "74"; //return "Poseidon";
|
||||
|
||||
case MeterProduct.Ally:
|
||||
return "ally";
|
||||
|
||||
case MeterProduct.IperlPlus:
|
||||
return "iperlplus";
|
||||
|
||||
case MeterProduct.IperlLegacy:
|
||||
return "iperllegacy";
|
||||
|
||||
default:
|
||||
return "74"; //return "Poseidon";
|
||||
}
|
||||
}
|
||||
|
||||
private string GetHatArgument()
|
||||
{
|
||||
switch (Hat)
|
||||
{
|
||||
case HatType.Harry:
|
||||
return "harry";
|
||||
|
||||
case HatType.Mth:
|
||||
default:
|
||||
return "mth";
|
||||
}
|
||||
}
|
||||
|
||||
// New constructor for v2.0
|
||||
public SerialPortData(
|
||||
string portName,
|
||||
string cmdClientName,
|
||||
MeterProduct meter = MeterProduct.Poseidon,
|
||||
HatType hat = HatType.Mth,
|
||||
int timeoutSeconds = 10,
|
||||
string macro = "readall")
|
||||
{
|
||||
PortName = portName;
|
||||
CmdClientName = cmdClientName;
|
||||
Meter = meter;
|
||||
Hat = hat;
|
||||
TimeoutSeconds = timeoutSeconds;
|
||||
Macro = macro;
|
||||
}
|
||||
|
||||
// Old constructor kept for backward compatibility
|
||||
public SerialPortData(
|
||||
string portName,
|
||||
string cmdClientName,
|
||||
@@ -264,9 +53,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
PortName = portName;
|
||||
CmdClientName = cmdClientName;
|
||||
OldMeterType = meterType;
|
||||
MeterType = meterType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms.VisualStyles;
|
||||
using System.Xml.Linq;
|
||||
using Common;
|
||||
using Common.Iperl;
|
||||
@@ -11,6 +12,8 @@ using NHibernate;
|
||||
using Sensus.iPerl.NfcHandler;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.GenericDevices;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols;
|
||||
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
|
||||
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
|
||||
using CalibrationStruct = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.CalibrationStruct;
|
||||
@@ -45,6 +48,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
readonly PoseidonCfg _poseidonCfg;
|
||||
|
||||
|
||||
public PoseidonCfg RegPoseidonCfg { get { return _poseidonCfg; } }
|
||||
public int RfidComPortNr { get { return _poseidonCfg.RfidComPortNr; } }
|
||||
public int OptoComPortNr { get { return _poseidonCfg.OptoComPortNr; } }
|
||||
public MeterType MeterType { get { return _poseidonCfg.MeterType; } }
|
||||
@@ -86,6 +90,21 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
|
||||
float[] x;
|
||||
public float[] X { get { return x; } }
|
||||
|
||||
|
||||
|
||||
// Volume of water from the opto telegram
|
||||
private DateTime _firstSampleTime;
|
||||
private DateTime _lastSampleTime;
|
||||
|
||||
private double _averageFlow;
|
||||
private long _averageFlowCount;
|
||||
private readonly object _avgLock = new object();
|
||||
private bool _optoheadStarted = false;
|
||||
|
||||
private OptoHeadService _optoHeadService;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Passed to OptoTelegramRaw.UpdateFromString(...)
|
||||
@@ -164,9 +183,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
///
|
||||
/// Timestamp from the opto telegram
|
||||
///
|
||||
private Int64 lastTimestamp;
|
||||
private double timestampSec;
|
||||
private double timestampSec0;
|
||||
|
||||
|
||||
int timeFromStart; /// [s] Time from test start to determine when the test start sample should be taken
|
||||
|
||||
@@ -174,12 +191,22 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
/// Test start volume for metrology in seconds
|
||||
public double TimestampSecStart
|
||||
{
|
||||
get { return TimeFromSamples(optoData, optoDataCount, TestStartTelegramIx, StartEndFilterSamplesCount2); }
|
||||
get
|
||||
{
|
||||
return _lastSampleTime != DateTime.MinValue ? 1 : 0; // return one second if is initialized, 0 - is false
|
||||
//return TimeFromSamples(optoData, optoDataCount, TestStartTelegramIx, StartEndFilterSamplesCount2);
|
||||
}
|
||||
}
|
||||
/// Test end time for metrology in seconds
|
||||
public double TimestampSecEnd
|
||||
{
|
||||
get { return TimeFromSamples(optoData, optoDataCount, TestEndTelegramIx, StartEndFilterSamplesCount2); }
|
||||
get
|
||||
{
|
||||
if (_lastSampleTime == DateTime.MinValue) return 0;
|
||||
TimeSpan delta = _lastSampleTime - _firstSampleTime;
|
||||
return (delta.TotalSeconds + 1);
|
||||
//return TimeFromSamples(optoData, optoDataCount, TestEndTelegramIx, StartEndFilterSamplesCount2);
|
||||
}
|
||||
}
|
||||
///
|
||||
public bool NoSamples
|
||||
@@ -197,12 +224,20 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
/// Test start volume for metrology in liters
|
||||
public double VolumeLtrStart
|
||||
{
|
||||
get { return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestStartTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2); }
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
//return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestStartTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2);
|
||||
}
|
||||
}
|
||||
/// Test end volume for metrology in liters
|
||||
public double VolumeLtrEnd
|
||||
{
|
||||
get { return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestEndTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2); }
|
||||
get
|
||||
{
|
||||
return wmVolume; // complet calculated volume (time * flowrate)
|
||||
//return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestEndTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -274,9 +309,10 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
{
|
||||
if (_poseidonCfg != null)
|
||||
{
|
||||
OpenOptoSerialPort($"COM{_poseidonCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One,
|
||||
Handshake.None);
|
||||
CloseOptoSerialPort();
|
||||
|
||||
// OpenOptoSerialPort($"COM{_poseidonCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One,
|
||||
// Handshake.None);
|
||||
// CloseOptoSerialPort();
|
||||
log.FatalFormat($"{Name} initialized: {this}");
|
||||
}
|
||||
else
|
||||
@@ -401,6 +437,16 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
Q2CorrRL = 0;
|
||||
Q2CorrLR = 0;
|
||||
|
||||
|
||||
lock (_avgLock)
|
||||
{
|
||||
_firstSampleTime = DateTime.MinValue;
|
||||
_lastSampleTime = DateTime.MinValue;
|
||||
_averageFlow = 0;
|
||||
_averageFlowCount = 0;
|
||||
log.Debug("Initializing datastream state");
|
||||
}
|
||||
|
||||
simulatedPcbNr = null;
|
||||
|
||||
dataStreamState = DataStreamState.Flush;
|
||||
@@ -442,6 +488,12 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
{
|
||||
timeFromStart += StateMachine.Period;
|
||||
ReadPulses();
|
||||
|
||||
//TODO read flow
|
||||
if (_optoHeadService!= null && !_optoHeadService.IsRunning)
|
||||
StartOptohead();
|
||||
|
||||
|
||||
|
||||
if (!startSampleAcquired && (timeFromStart >= 8) && (currentTelegramIx >= 0))
|
||||
{
|
||||
@@ -462,7 +514,94 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
return Event.ReadRegisterDone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
private void StartOptohead()
|
||||
{
|
||||
lock (_avgLock)
|
||||
{
|
||||
_firstSampleTime = DateTime.MinValue;
|
||||
_lastSampleTime = DateTime.MinValue;
|
||||
_averageFlow = 0;
|
||||
_averageFlowCount = 0;
|
||||
}
|
||||
|
||||
StartOptoTestInputLoop(new EventHandler<CommonRR.IPerl.communication.OptoReceivedEventArgs>(OnOptoHandler));
|
||||
}
|
||||
|
||||
private bool OpenOptoConnection(PoseidonCfg iHeadCfg)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (iHeadCfg != null)
|
||||
{
|
||||
if (_optoHeadService != null) return false;
|
||||
|
||||
OptoHeadService.Con connection = new OptoHeadService.Con($"COM{iHeadCfg.OptoComPortNr}", iHeadCfg.DebugLevel);
|
||||
_optoHeadService = new OptoHeadService(connection);
|
||||
return _optoHeadService.CreateSerialConnection();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool StartOptoTestInputLoop(EventHandler<CommonRR.IPerl.communication.OptoReceivedEventArgs> onOptoReceivedHandler)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_optoHeadService != null)
|
||||
{
|
||||
if (_optoHeadService.IsRunning) return false;
|
||||
|
||||
_optoHeadService.RunLoop(onOptoReceivedHandler);
|
||||
//run loop runstate = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex.Message);
|
||||
throw ex;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnOptoHandler(object sender, CommonRR.IPerl.communication.OptoReceivedEventArgs e)
|
||||
{
|
||||
//received data from optohead
|
||||
WaterMetrologyData eWaterMetrologyData = e?.WaterMetrologyData;
|
||||
if (eWaterMetrologyData != null && eWaterMetrologyData.C7Data != null)
|
||||
{
|
||||
double flowRateLPerS = eWaterMetrologyData.C7Data?.FlowRateLPerS ?? 0;
|
||||
|
||||
lock (_avgLock)
|
||||
{
|
||||
if (_averageFlowCount == 0)
|
||||
{
|
||||
_firstSampleTime = eWaterMetrologyData.C7Data?.Dt ?? DateTime.Now;
|
||||
}
|
||||
|
||||
_lastSampleTime = eWaterMetrologyData.C7Data?.Dt ?? DateTime.Now;
|
||||
|
||||
_averageFlowCount++;
|
||||
|
||||
// Running average (no overflow)
|
||||
_averageFlow += (flowRateLPerS - _averageFlow) / _averageFlowCount;
|
||||
TimeSpan delta = _lastSampleTime - _firstSampleTime;
|
||||
if (delta.TotalMilliseconds == 0)
|
||||
wmVolume = 0;
|
||||
else
|
||||
wmVolume = _averageFlow * (delta.TotalMilliseconds / 1000); //volume in liters
|
||||
//wmVolume = Units.ConvertFrom(Unit.l, _averageFlow * (delta.TotalMilliseconds / 1000));
|
||||
log.Info("Calculated Value:" + wmVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Stop this operation
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
@@ -570,78 +709,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Q2 correction factor calculated from the last test (Q2).
|
||||
/// This factor should be used only for R800 meters.
|
||||
/// </summary>
|
||||
/// <param name="q2TestResult">A test result from which to calculate the factor</param>
|
||||
/// <param name="nominalFlow">Nominal flow in m3/h</param>
|
||||
/// <param name="currentFactor">0 or the current Q2 correction factor when updating the factor</param>
|
||||
/// <returns>Calculated Q2 correction factor</returns>
|
||||
public double CalculateQ2CorrectionFactor(Results.Entities.MeterTestRslt currentQ2Result, int currentFactor, double nominalFlow, double errorTarget = 0)
|
||||
{
|
||||
double nominalTestFlowLph = Units.ConvertTo(Unit.lph, nominalFlow);
|
||||
double volumeRefShiftedToTarget = currentQ2Result.VolumeRef * (1.0 + errorTarget / 100.0);
|
||||
double q2adjErrorShiftedToTarget = Config.Formulas.ErrorFromVolumes(currentQ2Result.VolumeMeter, volumeRefShiftedToTarget);
|
||||
|
||||
double A = 16.0 / ScalingFactor(); /// Raw units per ml: DN15=16, DN20=8, DN25=4, DN32=2, DN40=1
|
||||
const double B = 8.0; /// Raw units per minute, 8
|
||||
const double C = B * 60.0; /// Raw units per hour, 480
|
||||
double D = C / A; /// ml correction per hour
|
||||
double F = D / (nominalTestFlowLph * 10.0); /// Error corrected with 8 Raw Units per minute [%]
|
||||
double G = F / B; /// Error corrected with 1 Raw Unit per minute [%]
|
||||
|
||||
/// Do not change the factor for an invalid measurement (q2adjResult.VolumeMeter == 0)
|
||||
double q2CorrectionFactor = (Math.Abs(currentQ2Result.VolumeMeter) <= float.Epsilon) ? Convert.ToDouble(currentFactor) :
|
||||
Convert.ToDouble(currentFactor) - (q2adjErrorShiftedToTarget / G) * (volumeRefShiftedToTarget / currentQ2Result.VolumeMeter);
|
||||
|
||||
log.WarnFormat("CalculateQ2CorrectionFactor() : Pos={0}, PCB#={1}, Error={2}%, Target={3}%, Current factor={4} New factor={5}",
|
||||
Name,
|
||||
SerialNr,
|
||||
currentQ2Result.Error.ToString("F2"),
|
||||
errorTarget.ToString("F3"),
|
||||
currentFactor.ToString("F1"),
|
||||
q2CorrectionFactor.ToString("F1"));
|
||||
|
||||
return q2CorrectionFactor;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 2 Hz correction factor calculated from two Q3 tests - done at 2Hz and at 8Hz.
|
||||
/// This factors should be used only for DN32 and DN40 meters.
|
||||
/// </summary>
|
||||
/// <param name="resultAt2Hz">Test result @2Hz from which to calculate the factor</param>
|
||||
/// <param name="resultAt8Hz">Test result @8Hz from which to calculate the factor</param>
|
||||
/// <param name="hz2CorrectionFactor">The calculated Q2 correction factor</param>
|
||||
/// <returns>true = OK, false = failed</returns>
|
||||
public bool Calculate2HzCorrectionFactor(Results.Entities.MeterTestRslt resultAt2Hz,
|
||||
Results.Entities.MeterTestRslt resultAt8Hz,
|
||||
out double diff2Hz8Hz, out int hz2CorrectionFactor)
|
||||
{
|
||||
hz2CorrectionFactor = 0;
|
||||
diff2Hz8Hz = 0;
|
||||
|
||||
if ((resultAt2Hz == null) || (resultAt8Hz == null))
|
||||
{
|
||||
return false; /// Test result @2Hz and/or @8Hz is missing ==> water meter failed
|
||||
}
|
||||
|
||||
diff2Hz8Hz = resultAt2Hz.Error - resultAt8Hz.Error;
|
||||
|
||||
if (Math.Abs(diff2Hz8Hz) > 2.5) return false; /// Difference of errors > 2.5 % ==> water meter failed
|
||||
|
||||
hz2CorrectionFactor = -1 * (int)Math.Round(10 * diff2Hz8Hz);
|
||||
|
||||
log.WarnFormat("2Hz correction: Pos={0}, PCB#={1}, corrFactor={2}, erro@2Hz={3}%, erro@8Hz={4}%",
|
||||
Name,
|
||||
SerialNr,
|
||||
hz2CorrectionFactor,
|
||||
resultAt2Hz.Error.ToString("F2"),
|
||||
resultAt8Hz.Error.ToString("F2"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -703,147 +771,24 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
/// <param name="optoState">OptoState.Read or OptoState.Flush</param>
|
||||
void ReadOptoData(DataStreamState optoState)
|
||||
{
|
||||
if (optoSerialPort is null) return;
|
||||
lock (this)
|
||||
{
|
||||
int nrBytes = optoSerialPort.BytesToRead;
|
||||
if (nrBytes > 0)
|
||||
{
|
||||
char[] buffer = new char[nrBytes];
|
||||
optoSerialPort.Read(buffer, 0, nrBytes);
|
||||
string received = new string(buffer);
|
||||
|
||||
string allRcvd = partOfTelegram + received;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int pos = allRcvd.IndexOf("\r\n");
|
||||
|
||||
if (pos < 0)
|
||||
{
|
||||
/// No CR+LF found, wait for more characters in the next invocation
|
||||
partOfTelegram = allRcvd;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// CR+LF found
|
||||
if (optoState == DataStreamState.ProcessAndSave)
|
||||
{
|
||||
int bufferIx = BufferIdx(optoDataCount);
|
||||
|
||||
if (pos < OptoTelegramRaw.Length - 2)
|
||||
{
|
||||
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
if (synchronized)
|
||||
{
|
||||
optoData[bufferIx].Counter = optoDataCount;
|
||||
optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError);
|
||||
}
|
||||
synchronized = true;
|
||||
}
|
||||
else if (optoData[bufferIx].UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
|
||||
optoDataCount,
|
||||
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
|
||||
ref volumeRawExtLast, ref timestampExtLast))
|
||||
{
|
||||
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) && the telegram is OK
|
||||
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
||||
OptoTelegramReceived(optoDataCount, synchronized2, volumeRawExtLast, timestampExtLast);
|
||||
synchronized2 = synchronized;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) but the telgram was not OK
|
||||
optoData[bufferIx].Counter = optoDataCount;
|
||||
optoDataCount++;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
|
||||
optoDataCount++;
|
||||
}
|
||||
else /// optoState == OptoState.Flush
|
||||
{
|
||||
if (pos < OptoTelegramRaw.Length - 2)
|
||||
{
|
||||
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
synchronized = true;
|
||||
}
|
||||
// CR+LF found and (pos >= OptoTelegram.Length - 2)
|
||||
else if (toBeFlushed.UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
|
||||
0,
|
||||
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
|
||||
ref volumeRawExtLast, ref timestampExtLast))
|
||||
{
|
||||
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
||||
synchronized2 = synchronized;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
//OnOptoReceived(this, new OptoReceivedEventArgs("."));
|
||||
}
|
||||
}
|
||||
|
||||
// lock (this)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
public string ReadOptoData()
|
||||
{
|
||||
if (optoSerialPort is null) return "";
|
||||
string received = ".";
|
||||
lock (this)
|
||||
{
|
||||
int nrBytes = optoSerialPort.BytesToRead;
|
||||
if (nrBytes > 0)
|
||||
{
|
||||
char[] buffer = new char[nrBytes];
|
||||
optoSerialPort.Read(buffer, 0, nrBytes);
|
||||
received = new string(buffer);
|
||||
}
|
||||
}
|
||||
// lock (this)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
return received;
|
||||
}
|
||||
|
||||
|
||||
void OptoTelegramReceived(int currentIx, bool async, Int64 volumeRawExt, Int64 timestampRawExt)
|
||||
{
|
||||
currentTelegramIx = currentIx;
|
||||
|
||||
lastVolumeRaw = volumeRawExt;
|
||||
lastTimestamp = timestampRawExt;
|
||||
|
||||
if (volumeLtr == 0 && volumeLtr0 == 0)
|
||||
{
|
||||
volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
|
||||
volumeLtr0 = volumeLtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
|
||||
}
|
||||
|
||||
if (timestampSec == 0 && timestampSec0 == 0)
|
||||
{
|
||||
timestampSec = (double)lastTimestamp / 8192.0;
|
||||
timestampSec0 = timestampSec;
|
||||
}
|
||||
else
|
||||
{
|
||||
timestampSec = (double)lastTimestamp / 8192.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -1002,12 +947,15 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
|
||||
void ReadPulses()
|
||||
{
|
||||
beginWMState = volumeLtr0;
|
||||
endWMState = volumeLtr;
|
||||
TimeSpan delta = _lastSampleTime - _firstSampleTime;
|
||||
double volume = _averageFlow * (delta.TotalMilliseconds / 1000);
|
||||
//log.Debug("ReadPulses - Calculated Value:" + volume);
|
||||
beginWMState = 1;
|
||||
endWMState = beginWMState + volume ;
|
||||
wmVolume = Math.Abs(endWMState - beginWMState);
|
||||
wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5);
|
||||
wmRefPulses = StateMachine.ControlBoardMain.RefPulses;
|
||||
wmTestTime = timestampSec - timestampSec0;
|
||||
wmTestTime = delta.TotalSeconds;
|
||||
}
|
||||
|
||||
private void OpenOptoSerialPort(string comPort, int baudRate, Parity parity, int dataBits, StopBits stopBit, Handshake handshake)
|
||||
@@ -1018,6 +966,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
/// Open serial port: 9600 Bd, 8 data bits, 1 stop bit, no parity
|
||||
try
|
||||
{
|
||||
|
||||
CloseOptoSerialPort();
|
||||
optoSerialPort = new SerialPort(comPort, baudRate, parity, dataBits, stopBit);
|
||||
optoSerialPort.Handshake = handshake;
|
||||
@@ -1039,12 +988,15 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
|
||||
private void CloseOptoSerialPort()
|
||||
{
|
||||
if (optoSerialPort != null)
|
||||
if (_optoHeadService != null)
|
||||
{
|
||||
optoSerialPort.Close();
|
||||
optoSerialPort = null;
|
||||
_optoHeadService.CloseSerialConnection();
|
||||
_optoHeadService = null;
|
||||
log.FatalFormat($"{Name} OptoPort closed: {this}");
|
||||
}
|
||||
|
||||
|
||||
communication.OpticalHeadTest.SetActiveMode(_poseidonCfg);
|
||||
}
|
||||
|
||||
|
||||
@@ -1056,10 +1008,14 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenOptoSerialPort($"COM{_poseidonCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One, Handshake.None);
|
||||
//set test mode via the cli
|
||||
communication.OpticalHeadTest.SetTestMode(_poseidonCfg);
|
||||
//open opto serial port
|
||||
OpenOptoConnection(_poseidonCfg);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error($"{Name} OptoPort - error opening port: {_poseidonCfg.OptoComPortNr}, Details: {e.Message}");
|
||||
}
|
||||
/// Reset opto-data, etc.
|
||||
optoDataCount = 0;
|
||||
@@ -1482,8 +1438,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
|
||||
volumeLtr = 0;
|
||||
volumeLtr0 = 0;
|
||||
timestampSec = 0;
|
||||
timestampSec0 = 0;
|
||||
|
||||
|
||||
extraDataPath = null;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using log4net;
|
||||
using TBF.Rig.RegisterReaders.CommonRR.IPerl.communication;
|
||||
using TBF.Rig.RegisterReaders.iPerlReaderUNI.common;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.implementations;
|
||||
@@ -12,7 +11,6 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
{
|
||||
public partial class UniHeadTestCtrl : UserControl
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(UniHeadTestCtrl));
|
||||
private IUniHeadTestCtrl _ctrl;
|
||||
private IUniHeadTestCtrl Ctrl { get => _ctrl; }
|
||||
|
||||
@@ -79,12 +77,6 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
|
||||
RfidOutputListBox = rfidOutputListBox
|
||||
});
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// A device/CLI communication failure must not terminate Application.Run.
|
||||
log.Error("Poseidon head test command failed.", exception);
|
||||
rfidOutputListBox.Items.Add("Error: " + exception.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
commandTestButton.Enabled = true;
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using TBF.Rig.Output.Printers.Label;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils;
|
||||
using CliRunner = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils.CliRunnerOld;
|
||||
using OptoHeadStatus = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols.OptoHeadStatus;
|
||||
@@ -13,6 +15,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
public class NfcHeadServiceOld
|
||||
{
|
||||
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(NfcHeadServiceOld));
|
||||
private bool activeHandlerSessioEnabled = false;
|
||||
private CliRunner _cliRunner;
|
||||
|
||||
@@ -208,7 +211,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Failed to parse OptoHeadStatus from output. Result: {result}");
|
||||
log.Error($"Failed to parse OptoHeadStatus from output. Result: {result}");
|
||||
//throw new Exception($"Failed to parse OptoHeadStatus from output. Result: {result}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using System.Threading.Tasks;
|
||||
using Common;
|
||||
using log4net;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols;
|
||||
using TBF.Rig.Sequences;
|
||||
using SERIAL_Driver = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils.SERIAL_Driver;
|
||||
using WaterMetrologyData = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols.WaterMetrologyData;
|
||||
|
||||
@@ -8,9 +12,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
{
|
||||
public class OptoHeadService
|
||||
{
|
||||
|
||||
static readonly ILog log = LogManager.GetLogger("PoseidonConnection");
|
||||
|
||||
public class Con
|
||||
{
|
||||
|
||||
public string com = "COM5";
|
||||
public int baudrate = 38400;
|
||||
public int dataBits = 8;
|
||||
@@ -18,6 +24,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
public StopBits stopbits = StopBits.Two;
|
||||
public int readTimeout = 5000;
|
||||
public int writeTimeout = 1000;
|
||||
private DebugMode _debugLevel;
|
||||
public DebugMode DebugModeSetting { get => _debugLevel; }
|
||||
|
||||
public Con(string com, int baudrate, int dataBits, Parity parity, StopBits stopbits, int readTimeout,
|
||||
int writeTimeout) : this(com)
|
||||
@@ -29,10 +37,23 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
this.readTimeout = readTimeout;
|
||||
this.writeTimeout = writeTimeout;
|
||||
}
|
||||
|
||||
public Con(DebugMode debugLevel,string com, int baudrate, int dataBits, Parity parity, StopBits stopbits, int readTimeout,
|
||||
int writeTimeout) : this(com)
|
||||
{
|
||||
this._debugLevel = debugLevel;
|
||||
this.baudrate = baudrate;
|
||||
this.dataBits = dataBits;
|
||||
this.parity = parity;
|
||||
this.stopbits = stopbits;
|
||||
this.readTimeout = readTimeout;
|
||||
this.writeTimeout = writeTimeout;
|
||||
}
|
||||
|
||||
public Con(string com)
|
||||
public Con(string com, DebugMode debugLevel = DebugMode.Normal)
|
||||
{
|
||||
this.com = com;
|
||||
this._debugLevel = debugLevel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +76,19 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
OnOptoReceivedHandler = null;
|
||||
bool isopen = false;
|
||||
Con con = Connection;
|
||||
if (con == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] message = {0x00};
|
||||
byte[] bytesReceived;
|
||||
|
||||
if (!driver.isOpen())
|
||||
if (con?.DebugModeSetting == DebugMode.Simulate)
|
||||
{
|
||||
isopen = true;
|
||||
}
|
||||
else if (!driver.isOpen())
|
||||
{
|
||||
|
||||
isopen = driver.OpenConnection(
|
||||
@@ -72,6 +101,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
con.writeTimeout);
|
||||
}
|
||||
|
||||
_bRunStarted = false;
|
||||
|
||||
return isopen;
|
||||
}
|
||||
|
||||
@@ -79,24 +110,39 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
{
|
||||
dissableRunLoop = true;
|
||||
OnOptoReceivedHandler = null;
|
||||
driver.Close();
|
||||
OnOptoReceivedHandler = null;
|
||||
if (Connection?.DebugModeSetting != DebugMode.Simulate)
|
||||
{
|
||||
driver.Close();
|
||||
}
|
||||
_bRunStarted = false;
|
||||
}
|
||||
|
||||
private EventHandler<CommonRR.IPerl.communication.OptoReceivedEventArgs> OnOptoReceivedHandler;
|
||||
|
||||
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get { return !dissableRunLoop
|
||||
&& ((Connection?.DebugModeSetting != DebugMode.Simulate) ? driver.isOpen() : true)
|
||||
&& _bRunStarted;}
|
||||
}
|
||||
|
||||
public void RunLoop(EventHandler<CommonRR.IPerl.communication.OptoReceivedEventArgs> onOptoReceivedHandler)
|
||||
{
|
||||
log.Debug("RunLoop started on event!");
|
||||
OnOptoReceivedHandler = onOptoReceivedHandler;
|
||||
Task.Run(() => Run());
|
||||
}
|
||||
|
||||
public void RunLoop()
|
||||
{
|
||||
log.Debug("RunLoop started!");
|
||||
Task.Run(() => Run());
|
||||
}
|
||||
|
||||
private bool dissableRunLoop = false;
|
||||
private bool _bRunStarted = false;
|
||||
/// <summary>
|
||||
/// Run the service. Catch one communication to WaterMetrologyData field.
|
||||
/// </summary>
|
||||
@@ -104,10 +150,12 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
{
|
||||
while (!dissableRunLoop)
|
||||
{
|
||||
_bRunStarted = true;
|
||||
WaterMetrologyData = ParseData(RunReading());
|
||||
if (OnOptoReceivedHandler != null && WaterMetrologyData != null)
|
||||
{
|
||||
OnOptoReceivedHandler.Invoke(this, new CommonRR.IPerl.communication.OptoReceivedEventArgs(WaterMetrologyData.ToString()));
|
||||
log.Debug($"Received OptoData: {WaterMetrologyData}");
|
||||
OnOptoReceivedHandler?.Invoke(this, new CommonRR.IPerl.communication.OptoReceivedEventArgs(WaterMetrologyData?.ToString(), WaterMetrologyData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,13 +163,20 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
|
||||
byte[] RunReading()
|
||||
{
|
||||
if (Connection?.DebugModeSetting != DebugMode.Simulate)
|
||||
{
|
||||
return new byte[] {0x00};
|
||||
}
|
||||
if (driver.isOpen())
|
||||
{
|
||||
driver.SendMessage(new byte[] {0x00}, 1);
|
||||
return driver.GetRawData();
|
||||
byte[] rawData = driver.GetRawData();
|
||||
log.Debug($"Received data size: {rawData?.Length ?? 0} bytes, raw data: {(rawData==null? "" :BitConverter.ToString(rawData))}");
|
||||
return rawData;
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Debug("Serial port is not open.");
|
||||
dissableRunLoop = true;
|
||||
}
|
||||
|
||||
@@ -130,6 +185,10 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
|
||||
|
||||
public WaterMetrologyData ParseData(byte[] data)
|
||||
{
|
||||
if (Connection?.DebugModeSetting != DebugMode.Simulate)
|
||||
{
|
||||
return WaterMetrologyData.SimulateC7();
|
||||
}
|
||||
try
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
|
||||
+15
@@ -37,6 +37,21 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
|
||||
return waterMetrologyData;
|
||||
}
|
||||
|
||||
public static WaterMetrologyData SimulateC7()
|
||||
{
|
||||
WaterMetrologyData waterMetrologyData = new WaterMetrologyData();
|
||||
waterMetrologyData.c7Data = WaterMetrologyDataC7.Simulate();
|
||||
return waterMetrologyData;
|
||||
}
|
||||
|
||||
public static WaterMetrologyData SimulateC2()
|
||||
{
|
||||
WaterMetrologyData waterMetrologyData = new WaterMetrologyData();
|
||||
waterMetrologyData.c2Data = WaterMetrologyDataC2.Simulate();
|
||||
return waterMetrologyData;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Status: {_optoHeadStatus}, C7Data: {c7Data}, C2Data: {c2Data}";
|
||||
|
||||
+37
-1
@@ -21,7 +21,17 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
|
||||
public bool FastHPFC { get; set; }
|
||||
public bool FieldPolarity { get; set; }
|
||||
public bool ImpedancePolarity { get; set; }
|
||||
|
||||
|
||||
|
||||
public double FlowRateLPerS // Flow Rate in L/s metric units
|
||||
{
|
||||
get
|
||||
{
|
||||
double flowRateGPM = FlowRate / 10000; //investigation flow meter GPM
|
||||
double flowRateLPerS = flowRateGPM * 0.063090196432096 ; // conversion factor from GPM to L/s with minimal digit lost
|
||||
return flowRateLPerS;
|
||||
}
|
||||
}
|
||||
public double CalcFlowmLps
|
||||
{
|
||||
get { return FlowRate / 4.0; } // FlowRate is in 1/4 mL/s
|
||||
@@ -41,6 +51,32 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
|
||||
return Parse(data, dt, dutinfo);
|
||||
}
|
||||
|
||||
public static WaterMetrologyDataC2 Simulate()
|
||||
{
|
||||
var result = new WaterMetrologyDataC2();
|
||||
|
||||
result.DutInfo = "dutinfo";
|
||||
result.Dt = DateTime.Now;
|
||||
result.AdcSample = 1;
|
||||
result.LastField = 2;
|
||||
result.FlowRate = 12456;
|
||||
result.Accumulator = 789465;
|
||||
result.FlipPeriod = 1;
|
||||
result.VinfStart = 0;
|
||||
result.VinfEnd = 0;
|
||||
result.ElectrodeDelta = 1;
|
||||
result.Impedance = 1;
|
||||
result.FieldDriveTime = 0x00 ;
|
||||
|
||||
result.IsInLowFlow = false;
|
||||
result.IsInEmptyPipe = false;
|
||||
result.FastHPFC = false; // Fast High Pass Filter Constant in bit 2
|
||||
result.FieldPolarity = false; // Field Polarity in bit 3
|
||||
result.ImpedancePolarity = false; // Impedance Polarity in bit 4
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static WaterMetrologyDataC2 Parse(byte[] data, DateTime dt, string dutinfo)
|
||||
{
|
||||
if (data.Length < 24)
|
||||
|
||||
+41
@@ -23,6 +23,47 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
|
||||
public bool IsLearningActive { get; set; }
|
||||
public bool AdcShiftsUpdated { get; set; }
|
||||
|
||||
public static WaterMetrologyDataC7 Simulate()
|
||||
{
|
||||
var result = new WaterMetrologyDataC7();
|
||||
|
||||
result.DutInfo = "dutinfo";
|
||||
result.Dt = DateTime.Now;
|
||||
result.AdcSample = 1;
|
||||
result.LastField = 2;
|
||||
result.FlowRate = 12456;
|
||||
result.Accumulator = 789465;
|
||||
result.FlipPeriod = 1;
|
||||
result.VinfStart = 0;
|
||||
result.VinfEnd = 0;
|
||||
result.ElectrodeDelta = 1;
|
||||
result.Impedance = 1;
|
||||
result.FieldDriveTime = 0x00 ;
|
||||
|
||||
result.IsInLowFlow = false;
|
||||
result.IsInEmptyPipe = false;
|
||||
result.FastHPFC = false; // Fast High Pass Filter Constant in bit 2
|
||||
result.FieldPolarity = false; // Field Polarity in bit 3
|
||||
result.ImpedancePolarity = false; // Impedance Polarity in bit 4
|
||||
|
||||
result.MagTamperState = true; // bits 5 and 6 represent MagTamperState
|
||||
result.IsLearningActive = true; // bit 7 represents IsLearningActive
|
||||
|
||||
|
||||
|
||||
result.AdcShiftsUpdated = false; // bit 0 represents AdcShiftsUpdated
|
||||
|
||||
result.LastFieldmilliGauss = 0;
|
||||
result.ImpedanceI = 0; // in phase
|
||||
result.ImpedanceQ = 0; // out of phase
|
||||
result.NoiseMetric = 0; //
|
||||
result.LearningLockout = 0;
|
||||
result.ReverseBuffer = 0;
|
||||
result.ConditionedAdc = 0;
|
||||
result.Totalalizer = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static WaterMetrologyDataC7 Parse(string base64Data, DateTime dt, string dutinfo)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
|
||||
{
|
||||
[Description("..")] None,
|
||||
[Description("Nfc")] Nfc,
|
||||
[Description("Touch Capl")]Touched,
|
||||
[Description("cTouchRead")]Touched,
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
|
||||
internal class OpticalHeadTest
|
||||
{
|
||||
protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData");
|
||||
|
||||
DebugMode _debugMode;
|
||||
public DebugMode DebugMode { get => _debugMode; set => _debugMode = value; }
|
||||
|
||||
internal static string OpenSealing(ISmartReader iHead)
|
||||
{
|
||||
@@ -26,6 +29,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
|
||||
|
||||
internal static string ReadRequest_SerialNo(PoseidonCfg iHeadCfg)
|
||||
{
|
||||
if (iHeadCfg != null && iHeadCfg.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
return "1111";
|
||||
}
|
||||
|
||||
string serialNo = null;
|
||||
try
|
||||
{
|
||||
@@ -77,26 +85,26 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
|
||||
|
||||
internal static string SetActiveMode(PoseidonCfg iHeadCfg)
|
||||
{
|
||||
try
|
||||
if (iHeadCfg != null && iHeadCfg.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
SerialPortData serialPortData = new SerialPortData(
|
||||
$"COM{iHeadCfg.RfidComPortNr}",
|
||||
iHeadCfg.CliProgramName,
|
||||
iHeadCfg.CliMeterType);
|
||||
NfcHeadServiceOld headService = new NfcHeadServiceOld(serialPortData);
|
||||
OptoHeadStatus optoHeadStatus = headService.SetTestingMode(OptoHeadStatus.OptoHeadDisabled);
|
||||
if (optoHeadStatus == OptoHeadStatus.OptoHeadDisabled)
|
||||
{
|
||||
_lastOptoHeadStatus = optoHeadStatus;
|
||||
return "OK";
|
||||
}
|
||||
|
||||
return "Error Set Active Mode";
|
||||
return "OK";
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
||||
SerialPortData serialPortData = new SerialPortData(
|
||||
$"COM{iHeadCfg.RfidComPortNr}",
|
||||
iHeadCfg.CliProgramName,
|
||||
iHeadCfg.CliMeterType);
|
||||
NfcHeadServiceOld headService = new NfcHeadServiceOld(serialPortData);
|
||||
// Wait synchronously
|
||||
OptoHeadStatus optoHeadStatus = headService.SetTestingMode(OptoHeadStatus.OptoHeadDisabled);
|
||||
if (optoHeadStatus == OptoHeadStatus.OptoHeadDisabled)
|
||||
{
|
||||
rfidDataLogger.Error("Poseidon Set Active Mode failed.", exception);
|
||||
return "Error Set Active Mode: " + exception.Message;
|
||||
_lastOptoHeadStatus = optoHeadStatus;
|
||||
return "OK";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Error Set Test Mode";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,27 +113,27 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
|
||||
|
||||
internal static string SetTestMode(PoseidonCfg iHeadCfg)
|
||||
{
|
||||
try
|
||||
if (iHeadCfg != null && iHeadCfg.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
SerialPortData serialPortData = new SerialPortData(
|
||||
$"COM{iHeadCfg.RfidComPortNr}",
|
||||
iHeadCfg.CliProgramName,
|
||||
iHeadCfg.CliMeterType);
|
||||
NfcHeadServiceOld headService = new NfcHeadServiceOld(serialPortData);
|
||||
OptoHeadStatus optoHeadStatus = headService.SetTestingMode(OptoHeadStatus.OptoHeadC7);
|
||||
if (optoHeadStatus == OptoHeadStatus.OptoHeadC7)
|
||||
{
|
||||
_lastOptoHeadStatus = optoHeadStatus;
|
||||
_lastIHeadCfg = iHeadCfg;
|
||||
return "OK";
|
||||
}
|
||||
|
||||
return "Error Set Test Mode";
|
||||
return "OK";
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
||||
SerialPortData serialPortData = new SerialPortData(
|
||||
$"COM{iHeadCfg.RfidComPortNr}",
|
||||
iHeadCfg.CliProgramName,
|
||||
iHeadCfg.CliMeterType);
|
||||
NfcHeadServiceOld headService = new NfcHeadServiceOld(serialPortData);
|
||||
// Wait synchronously
|
||||
OptoHeadStatus optoHeadStatus = headService.SetTestingMode(OptoHeadStatus.OptoHeadC7);
|
||||
if (optoHeadStatus == OptoHeadStatus.OptoHeadC7)
|
||||
{
|
||||
rfidDataLogger.Error("Poseidon Set Test Mode failed.", exception);
|
||||
return "Error Set Test Mode: " + exception.Message;
|
||||
_lastOptoHeadStatus = optoHeadStatus;
|
||||
_lastIHeadCfg = iHeadCfg;
|
||||
return "OK";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Error Set Test Mode";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +141,10 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
|
||||
|
||||
public static void Deactivate()
|
||||
{
|
||||
if (_lastIHeadCfg != null && _lastIHeadCfg.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
StopOptoTestInputLoop();
|
||||
if (_lastOptoHeadStatus != OptoHeadStatus.Unknown &&
|
||||
_lastOptoHeadStatus != OptoHeadStatus.OptoHeadDisabled &&
|
||||
@@ -152,7 +164,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
|
||||
{
|
||||
if (optoHeadService != null) return false;
|
||||
|
||||
OptoHeadService.Con connection = new OptoHeadService.Con($"COM{iHeadCfg.OptoComPortNr}");
|
||||
OptoHeadService.Con connection = new OptoHeadService.Con($"COM{iHeadCfg.OptoComPortNr}", iHeadCfg.DebugLevel);
|
||||
optoHeadService = new OptoHeadService(connection);
|
||||
optoHeadService.CreateSerialConnection();
|
||||
optoHeadService.RunLoop(onOptoReceivedHandler);
|
||||
|
||||
+11
-13
@@ -85,20 +85,18 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.implementations
|
||||
rfidListItem.Text = $"ReadSerialNo: {OpticalHeadTest.ReadRequest_SerialNo(poseidonCfg)}";
|
||||
break;
|
||||
case Operations.SetTestModeOn:
|
||||
string setTestModeResult = OpticalHeadTest.SetTestMode(poseidonCfg);
|
||||
rfidListItem.Text = $"SetTestMode: {setTestModeResult}";
|
||||
// Start the opto worker only after a successful mode change.
|
||||
if (setTestModeResult == "OK")
|
||||
rfidListItem.Text = $"SetTestMode: {OpticalHeadTest.SetTestMode(poseidonCfg)}";
|
||||
//Do start thread
|
||||
|
||||
a.OptoListBox.Items.Clear();
|
||||
stopWorkerThread = false;
|
||||
optoThread = new Thread(OptoWorker);
|
||||
if (!optoThread.IsAlive)
|
||||
{
|
||||
a.OptoListBox.Items.Clear();
|
||||
stopWorkerThread = false;
|
||||
optoThread = new Thread(OptoWorker);
|
||||
if (!optoThread.IsAlive)
|
||||
{
|
||||
OpticalHeadTest.StartOptotestInputLoop(poseidonCfg, OptoReceivedHandler);
|
||||
optoThread.Start();
|
||||
}
|
||||
OpticalHeadTest.StartOptotestInputLoop(poseidonCfg,OptoReceivedHandler); // open opto port
|
||||
optoThread.Start();
|
||||
}
|
||||
|
||||
break;
|
||||
case Operations.SetTestModeOff:
|
||||
//Do stop thread
|
||||
@@ -135,4 +133,4 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.implementations
|
||||
this.stopWorkerThread = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ using TBF.Rig.Network.RestAPI;
|
||||
using TBF.Rig.Network.RestAPI.facade;
|
||||
using TBF.Rig.Output.Printers.GroupPrinting.Single;
|
||||
using TBF.UiBridge;
|
||||
using SharedComponents;
|
||||
using System.Text;
|
||||
using TBF.Rig.RegisterReaders.CommonRR.IPerl;
|
||||
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
|
||||
@@ -56,7 +57,11 @@ namespace TBF.Rig.Sequences
|
||||
try
|
||||
{
|
||||
/// 1nd argument
|
||||
ITestMethodCfg iPerlCfgIPerl = cfg as ITestMethodCfg;
|
||||
ITestMethodCfg testMethodCfg = cfg as ITestMethodCfg;
|
||||
if (testMethodCfg == null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// 2rd argument: as is
|
||||
|
||||
@@ -67,8 +72,7 @@ namespace TBF.Rig.Sequences
|
||||
/*myRef.modelessDlg = new TestMethods.iPerlCommunication.iPerlCommunicationForm(iPerlCfg, tests, iPerlCommParams);
|
||||
myRef.modelessDlg.Show();*/
|
||||
|
||||
myRef.modelessDlg = new SmartCommunicationForm(
|
||||
testMethod , tests, iPerlCommParams);
|
||||
myRef.modelessDlg = new SmartCommunicationForm( testMethod , tests, iPerlCommParams);
|
||||
myRef.modelessDlg.Show();
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -1547,7 +1551,7 @@ namespace TBF.Rig.Sequences
|
||||
}
|
||||
|
||||
// ✍️ Uloženie do vlastného logu
|
||||
//LiveLogCache.Instance.AddLog(logBuilder.ToString());
|
||||
LiveLogCache.Instance.AddLog(logBuilder.ToString());
|
||||
|
||||
Bridge.OnError(this, "Chyba pri aktualizácii výsledkov v MySQL.");
|
||||
goto error;
|
||||
|
||||
@@ -373,7 +373,7 @@ namespace TBF.Rig.TestMethods.SmartMeterFlyingStartMassCollection
|
||||
/// Show the modeless dialog with error indication
|
||||
///
|
||||
string componentName = (method as IComponent)?.Name ?? string.Empty;
|
||||
Program.MainWnd.Invoke(new SmartCommFormDlgt(OpenSmartCommForm), new object[] { this, method/*componentName*/, test, testParams });
|
||||
Program.MainWnd.Invoke(new SmartCommFormDlgt(OpenSmartCommForm), new object[] { this, method, test, testParams });
|
||||
|
||||
//------------------------------------------------
|
||||
Bridge.OnActivity(this, Strings.iPerl_Communication_in_progress);
|
||||
|
||||
@@ -513,8 +513,8 @@ namespace TBF.Rig.TestMethods.StandingStart
|
||||
{
|
||||
GenericDevices.IRegReader rr = sensPath.RegisterReaders[i];
|
||||
|
||||
if (rr is ISmartReader)
|
||||
(rr as ISmartReader).BeginWMState = dataEntryCmpnt.WMStartState(i);
|
||||
if (rr is ICommonRegReader)
|
||||
(rr as ICommonRegReader).BeginWMState = dataEntryCmpnt.WMStartState(i);
|
||||
|
||||
if (rr is Rig.RegisterReaders.StandingStartStop.RegisterReader)
|
||||
(rr as Rig.RegisterReaders.StandingStartStop.RegisterReader).BeginWMState = dataEntryCmpnt.WMStartState(i);
|
||||
@@ -766,9 +766,9 @@ namespace TBF.Rig.TestMethods.StandingStart
|
||||
for (int i = 0; i < Data.WMsCount; i++)
|
||||
{
|
||||
GenericDevices.IRegReader rr = sensPath.RegisterReaders[i];
|
||||
|
||||
if (rr is ISmartReader)
|
||||
(rr as ISmartReader).EndWMState = dataEntryCmpnt.WMEndState(i);
|
||||
|
||||
if (rr is ICommonRegReader)
|
||||
(rr as ICommonRegReader).EndWMState = dataEntryCmpnt.WMEndState(i);
|
||||
|
||||
if (rr is Rig.RegisterReaders.StandingStartStop.RegisterReader)
|
||||
(rr as Rig.RegisterReaders.StandingStartStop.RegisterReader).EndWMState = dataEntryCmpnt.WMEndState(i);
|
||||
@@ -1017,6 +1017,13 @@ namespace TBF.Rig.TestMethods.StandingStart
|
||||
&& (tstRslt.ErrorFlags == 0);
|
||||
meterRslt.TestDone = true;
|
||||
tstRslt.TestDone = true;
|
||||
if (regReader is ICommonRegReader regReaderCommon)
|
||||
// if (meterRslt.WaterMeter != null
|
||||
// && tstRslt is ICommonRegReader tstRsltCommon
|
||||
// && !string.IsNullOrEmpty(tstRsltCommon.SerialNr))
|
||||
{
|
||||
meterRslt.WaterMeter.SerialNr = regReaderCommon.SerialNr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using SchematicDrawing;
|
||||
using TBF.Rig.ControlBoard.Uni;
|
||||
using TBF.Boxes;
|
||||
using TBF.Resources;
|
||||
|
||||
using AppDiagnostic;
|
||||
|
||||
namespace TBF.Rig.Uni.FlowMeter
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using log4net;
|
||||
using SchematicDrawing;
|
||||
using TBF.Rig.ControlBoard.Uni;
|
||||
using TBF.Boxes;
|
||||
using SharedComponents;
|
||||
|
||||
namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
|
||||
{
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
|
||||
|
||||
using TBF.Rig.Configs.NameOnly;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.RegisterReaders.CommonRR.IPerl;
|
||||
|
||||
namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
{
|
||||
public interface ISmartTestMethod
|
||||
{
|
||||
public void MeterCommMilestone(int iItem, bool bValue);
|
||||
public bool IsMeterCommMilestone(int iItem);
|
||||
|
||||
public ITestMethodCfg TestMethodCfg { get; }
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
public static string SelectedTypeReader { get; set; }
|
||||
|
||||
private List<ICorrections> GetNewCorrectionList(ISmartTestMethod componentBase ,
|
||||
TestMethodCfg cfg, IList<Test> tests, IList<ITestParams> multiTestParams)
|
||||
ITestMethodCfg cfg, IList<Test> tests, IList<ITestParams> multiTestParams)
|
||||
{
|
||||
List<ICorrections> correctionsList = new List<ICorrections>();
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
{
|
||||
if (correctionsList.Any(x => x is SmartReader))
|
||||
continue;
|
||||
correctionsList.Add(new PoseidonCorrections(this));
|
||||
correctionsList.Add(new PoseidonCorrections(this,log, rfidDataLogger, componentBase, cfg, tests, multiTestParams));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
ShuffleTextBoxes(ProcessData.WMsCount, ProcessData.LineSize);
|
||||
|
||||
|
||||
this.ContextMenu = Correction.GetContextMenu();
|
||||
//this.ContextMenu = Correction.GetContextMenu();
|
||||
|
||||
}
|
||||
|
||||
@@ -325,9 +325,11 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
{
|
||||
checkBoxesEditMode = false;
|
||||
|
||||
ITestMethodCfg cfg = (componentBase as ITestMethodCfg);
|
||||
ISmartTestMethod smartTestMethod = componentBase as ISmartTestMethod;
|
||||
|
||||
//TODO get corrections based on defined meter
|
||||
_corrections = GetNewCorrectionList(componentBase as ISmartTestMethod,
|
||||
componentBase.Cfg as TestMethodCfg, tests, multiTestParams);
|
||||
_corrections = GetNewCorrectionList(smartTestMethod, smartTestMethod.TestMethodCfg, tests, multiTestParams);
|
||||
InitializeMeterTypeItems();
|
||||
UpdateHeads();
|
||||
|
||||
@@ -381,6 +383,27 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
|
||||
private void UpdateHeads()
|
||||
{
|
||||
if (!(waterMeterPositions0 == null || waterMeterPositions0.Count <= 0)
|
||||
&& labels != null && counters != null && messages != null && checkBoxes != null)
|
||||
{
|
||||
foreach (int position in waterMeterPositions0)
|
||||
{
|
||||
try
|
||||
{
|
||||
labels[position].Visible = false;
|
||||
counters[position].Visible = false;
|
||||
messages[position].Visible = false;
|
||||
checkBoxes[position].Visible = false;
|
||||
ckbIndex[position] = 0;
|
||||
ckbState[position] = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error("UpdateHeads()", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iperlHeads?.Clear();
|
||||
if (iperlHeads == null) iperlHeads = new List<ISmartReader>();
|
||||
waterMeterPositions0?.Clear();
|
||||
@@ -415,6 +438,21 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
if (wmPos >= ProcessData.WMsCount) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//we have items from the list, so we can enable the rows
|
||||
if (iperlHeads.Count > 0)
|
||||
{
|
||||
WaterMetersCount = iperlHeads.Count;
|
||||
ShuffleTextBoxes(WaterMetersCount, ProcessData.LineSize);
|
||||
|
||||
this.ContextMenu = Correction.GetContextMenu();
|
||||
Correction.PrepareForTestsActivities(WaterMetersCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ContextMenu = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,6 +496,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
/// this part works fine if we are on <b>test loop</b>
|
||||
/// - because ProcessData.RegisterReaders is initialized in test loop
|
||||
/// </summary>
|
||||
/// <param name="selectedTypeReader"></param>
|
||||
private static void InitializeSmartReaderLists()
|
||||
{
|
||||
iperlHeads = new List<ISmartReader>();
|
||||
@@ -501,43 +540,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
/// <param name="lineSize">Number of watermeters in one line</param>
|
||||
void ShuffleTextBoxes(int wmsCount, int lineSize)
|
||||
{
|
||||
labels = new Label[MaxTextBoxesCount]
|
||||
{
|
||||
wmLabel1, wmLabel2, wmLabel3, wmLabel4, wmLabel5, wmLabel6, wmLabel7, wmLabel8, wmLabel9, wmLabel10,
|
||||
wmLabel11, wmLabel12, wmLabel13, wmLabel14, wmLabel15, wmLabel16, wmLabel17, wmLabel18, wmLabel19, wmLabel20,
|
||||
wmLabel21, wmLabel22, wmLabel23, wmLabel24, wmLabel25, wmLabel26, wmLabel27, wmLabel28, wmLabel29, wmLabel30,
|
||||
wmLabel31, wmLabel32, wmLabel33, wmLabel34, wmLabel35, wmLabel36, wmLabel37, wmLabel38, wmLabel39, wmLabel40,
|
||||
wmLabel41, wmLabel42, wmLabel43, wmLabel44, wmLabel45, wmLabel46, wmLabel47, wmLabel48,
|
||||
};
|
||||
counters = new PictureBox[MaxTextBoxesCount]
|
||||
{
|
||||
pictureBox1, pictureBox2, pictureBox3, pictureBox4, pictureBox5, pictureBox6, pictureBox7, pictureBox8, pictureBox9, pictureBox10,
|
||||
pictureBox11, pictureBox12, pictureBox13, pictureBox14, pictureBox15, pictureBox16, pictureBox17, pictureBox18, pictureBox19, pictureBox20,
|
||||
pictureBox21, pictureBox22, pictureBox23, pictureBox24, pictureBox25, pictureBox26, pictureBox27, pictureBox28, pictureBox29, pictureBox30,
|
||||
pictureBox31, pictureBox32, pictureBox33, pictureBox34, pictureBox35, pictureBox36, pictureBox37, pictureBox38, pictureBox39, pictureBox40,
|
||||
pictureBox41, pictureBox42, pictureBox43, pictureBox44, pictureBox45, pictureBox46, pictureBox47, pictureBox48,
|
||||
};
|
||||
messages = new TextBox[MaxTextBoxesCount]
|
||||
{
|
||||
wmTextBox1, wmTextBox2, wmTextBox3, wmTextBox4, wmTextBox5, wmTextBox6, wmTextBox7, wmTextBox8, wmTextBox9, wmTextBox10,
|
||||
wmTextBox11, wmTextBox12, wmTextBox13, wmTextBox14, wmTextBox15, wmTextBox16, wmTextBox17, wmTextBox18, wmTextBox19, wmTextBox20,
|
||||
wmTextBox21, wmTextBox22, wmTextBox23, wmTextBox24, wmTextBox25, wmTextBox26, wmTextBox27, wmTextBox28, wmTextBox29, wmTextBox30,
|
||||
wmTextBox31, wmTextBox32, wmTextBox33, wmTextBox34, wmTextBox35, wmTextBox36, wmTextBox37, wmTextBox38, wmTextBox39, wmTextBox40,
|
||||
wmTextBox41, wmTextBox42, wmTextBox43, wmTextBox44, wmTextBox45, wmTextBox46, wmTextBox47, wmTextBox48,
|
||||
};
|
||||
checkBoxes = new CheckBoxImage[MaxTextBoxesCount]
|
||||
{
|
||||
checkBoxImage1, checkBoxImage2, checkBoxImage3, checkBoxImage4, checkBoxImage5, checkBoxImage6, checkBoxImage7, checkBoxImage8, checkBoxImage9, checkBoxImage10,
|
||||
checkBoxImage11, checkBoxImage12, checkBoxImage13, checkBoxImage14, checkBoxImage15, checkBoxImage16, checkBoxImage17, checkBoxImage18, checkBoxImage19, checkBoxImage20,
|
||||
checkBoxImage21, checkBoxImage22, checkBoxImage23, checkBoxImage24, checkBoxImage25, checkBoxImage26, checkBoxImage27, checkBoxImage28, checkBoxImage29, checkBoxImage30,
|
||||
checkBoxImage31, checkBoxImage32, checkBoxImage33, checkBoxImage34, checkBoxImage35, checkBoxImage36, checkBoxImage37, checkBoxImage38, checkBoxImage39, checkBoxImage40,
|
||||
checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45, checkBoxImage46, checkBoxImage47, checkBoxImage48,
|
||||
};
|
||||
ckbIndex = new int[MaxTextBoxesCount];
|
||||
ckbState = new bool[MaxTextBoxesCount];
|
||||
|
||||
|
||||
textBoxesCount = MaxTextBoxesCount;
|
||||
InitializeTextBoxArrays();
|
||||
///
|
||||
if (wmsCount < textBoxesCount && lineSize > 0)
|
||||
{
|
||||
@@ -576,7 +579,56 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
ResizeDlgToFitEnabledControls();
|
||||
}
|
||||
|
||||
void ResizeDlgToFitEnabledControls()
|
||||
private void InitializeTextBoxArrays()
|
||||
{
|
||||
//if is initialized before we ignore initialization
|
||||
if (labels != null
|
||||
&& counters != null
|
||||
&& messages != null
|
||||
&& checkBoxes != null
|
||||
&& ckbIndex != null
|
||||
&& ckbState != null) return;
|
||||
|
||||
labels = new Label[MaxTextBoxesCount]
|
||||
{
|
||||
wmLabel1, wmLabel2, wmLabel3, wmLabel4, wmLabel5, wmLabel6, wmLabel7, wmLabel8, wmLabel9, wmLabel10,
|
||||
wmLabel11, wmLabel12, wmLabel13, wmLabel14, wmLabel15, wmLabel16, wmLabel17, wmLabel18, wmLabel19, wmLabel20,
|
||||
wmLabel21, wmLabel22, wmLabel23, wmLabel24, wmLabel25, wmLabel26, wmLabel27, wmLabel28, wmLabel29, wmLabel30,
|
||||
wmLabel31, wmLabel32, wmLabel33, wmLabel34, wmLabel35, wmLabel36, wmLabel37, wmLabel38, wmLabel39, wmLabel40,
|
||||
wmLabel41, wmLabel42, wmLabel43, wmLabel44, wmLabel45, wmLabel46, wmLabel47, wmLabel48,
|
||||
};
|
||||
counters = new PictureBox[MaxTextBoxesCount]
|
||||
{
|
||||
pictureBox1, pictureBox2, pictureBox3, pictureBox4, pictureBox5, pictureBox6, pictureBox7, pictureBox8, pictureBox9, pictureBox10,
|
||||
pictureBox11, pictureBox12, pictureBox13, pictureBox14, pictureBox15, pictureBox16, pictureBox17, pictureBox18, pictureBox19, pictureBox20,
|
||||
pictureBox21, pictureBox22, pictureBox23, pictureBox24, pictureBox25, pictureBox26, pictureBox27, pictureBox28, pictureBox29, pictureBox30,
|
||||
pictureBox31, pictureBox32, pictureBox33, pictureBox34, pictureBox35, pictureBox36, pictureBox37, pictureBox38, pictureBox39, pictureBox40,
|
||||
pictureBox41, pictureBox42, pictureBox43, pictureBox44, pictureBox45, pictureBox46, pictureBox47, pictureBox48,
|
||||
};
|
||||
messages = new TextBox[MaxTextBoxesCount]
|
||||
{
|
||||
wmTextBox1, wmTextBox2, wmTextBox3, wmTextBox4, wmTextBox5, wmTextBox6, wmTextBox7, wmTextBox8, wmTextBox9, wmTextBox10,
|
||||
wmTextBox11, wmTextBox12, wmTextBox13, wmTextBox14, wmTextBox15, wmTextBox16, wmTextBox17, wmTextBox18, wmTextBox19, wmTextBox20,
|
||||
wmTextBox21, wmTextBox22, wmTextBox23, wmTextBox24, wmTextBox25, wmTextBox26, wmTextBox27, wmTextBox28, wmTextBox29, wmTextBox30,
|
||||
wmTextBox31, wmTextBox32, wmTextBox33, wmTextBox34, wmTextBox35, wmTextBox36, wmTextBox37, wmTextBox38, wmTextBox39, wmTextBox40,
|
||||
wmTextBox41, wmTextBox42, wmTextBox43, wmTextBox44, wmTextBox45, wmTextBox46, wmTextBox47, wmTextBox48,
|
||||
};
|
||||
checkBoxes = new CheckBoxImage[MaxTextBoxesCount]
|
||||
{
|
||||
checkBoxImage1, checkBoxImage2, checkBoxImage3, checkBoxImage4, checkBoxImage5, checkBoxImage6, checkBoxImage7, checkBoxImage8, checkBoxImage9, checkBoxImage10,
|
||||
checkBoxImage11, checkBoxImage12, checkBoxImage13, checkBoxImage14, checkBoxImage15, checkBoxImage16, checkBoxImage17, checkBoxImage18, checkBoxImage19, checkBoxImage20,
|
||||
checkBoxImage21, checkBoxImage22, checkBoxImage23, checkBoxImage24, checkBoxImage25, checkBoxImage26, checkBoxImage27, checkBoxImage28, checkBoxImage29, checkBoxImage30,
|
||||
checkBoxImage31, checkBoxImage32, checkBoxImage33, checkBoxImage34, checkBoxImage35, checkBoxImage36, checkBoxImage37, checkBoxImage38, checkBoxImage39, checkBoxImage40,
|
||||
checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45, checkBoxImage46, checkBoxImage47, checkBoxImage48,
|
||||
};
|
||||
ckbIndex = new int[MaxTextBoxesCount];
|
||||
ckbState = new bool[MaxTextBoxesCount];
|
||||
|
||||
|
||||
textBoxesCount = MaxTextBoxesCount;
|
||||
}
|
||||
|
||||
void ResizeDlgToFitEnabledControls()
|
||||
{
|
||||
int xMax = 0;
|
||||
int yMax = 0;
|
||||
@@ -645,7 +697,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
}
|
||||
|
||||
/// Start communication process by incrementing 'currentGroup'.
|
||||
currentGroup++;
|
||||
//currentGroup++;
|
||||
|
||||
int wtId = 0;
|
||||
foreach (var wt in Correction.GetAllThreads())
|
||||
@@ -869,6 +921,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
if (senderCombo == null) return;
|
||||
SelectedTypeReader = senderCombo.SelectedItem?.ToString();
|
||||
UpdateHeads();
|
||||
SmartCommunicationForm_Load(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
using TBF.Rig.Configs.NameOnly;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.RegisterReaders.CommonRR.IPerl;
|
||||
|
||||
namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
|
||||
{
|
||||
public abstract class SmartComponentBase : ComponentBase, ISmartTestMethod
|
||||
{
|
||||
private ITestMethodCfg cfg;
|
||||
|
||||
public abstract void MeterCommMilestone(int iItem, bool bValue);
|
||||
public abstract bool IsMeterCommMilestone(int iItem);
|
||||
|
||||
public ITestMethodCfg TestMethodCfg { get => cfg; }
|
||||
|
||||
|
||||
public SmartComponentBase()
|
||||
: base()
|
||||
{
|
||||
cfg = null;
|
||||
}
|
||||
|
||||
public SmartComponentBase(IComponentCfg cfg)
|
||||
: base(cfg)
|
||||
{
|
||||
this.cfg = cfg as ITestMethodCfg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
|
||||
{
|
||||
public static class EnumExtensions
|
||||
{
|
||||
public static bool TryParseByDescription<TEnum>(string description, out TEnum result)
|
||||
where TEnum : struct, Enum
|
||||
{
|
||||
foreach (var field in typeof(TEnum).GetFields())
|
||||
{
|
||||
var attribute = Attribute.GetCustomAttribute(field,
|
||||
typeof(DescriptionAttribute)) as DescriptionAttribute;
|
||||
|
||||
if ((attribute != null && attribute.Description == description) ||
|
||||
field.Name == description)
|
||||
{
|
||||
result = (TEnum)field.GetValue(null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+458
-15
@@ -1,19 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Common;
|
||||
using Config.Entities;
|
||||
using log4net;
|
||||
using Results.Entities;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.RegisterReaders.CommonRR.IPerl;
|
||||
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.communication;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.implementations;
|
||||
using TBF.Rig.Sequences;
|
||||
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
|
||||
using TBF.Rig.TestMethods.SmartTest;
|
||||
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
|
||||
using CheckBoxImage = TBF.Boxes.CheckBoxImage;
|
||||
using Factory = TBF.Rig.RegisterReaders.iPerlReaderUNI.Factory;
|
||||
using PoseidonReader = TBF.Rig.RegisterReaders.PoseidonCmdStartStop.PoseidonReader;
|
||||
|
||||
namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
|
||||
{
|
||||
@@ -32,6 +41,16 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
|
||||
private IList<Test> tests;
|
||||
private IList<ITestParams> multiTestParams;
|
||||
private SmartCommunicationForm _parentFrom;
|
||||
|
||||
static IList<Thread> workerThreads;
|
||||
static bool stopWorkerThreads;
|
||||
static int currentActivityStep;
|
||||
static int currentGroup;
|
||||
|
||||
/// form -> worker thread (0 = none)
|
||||
static int lastGroup;
|
||||
|
||||
static int completedCommCount;
|
||||
|
||||
|
||||
public string TypeIdentificatorName()
|
||||
@@ -56,12 +75,122 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
|
||||
|
||||
public IList<ISmartReader> iperlHeads { get => ParentFrom.Heads;}
|
||||
|
||||
private Label activityLabel { get => ParentFrom?.ActivityLabel;}
|
||||
private Label[] labels { get => ParentFrom?.Labels; }
|
||||
private PictureBox[] counters{get => ParentFrom?.Counters;}
|
||||
private TextBox[] messages{get => ParentFrom?.Messages;}
|
||||
private CheckBoxImage[] checkBoxes{get => ParentFrom?.CheckBoxes;}
|
||||
private int[] ckbIndex{get => ParentFrom?.CkbIndex;}
|
||||
private bool[] ckbState{get => ParentFrom?.CkbState;}
|
||||
|
||||
|
||||
|
||||
|
||||
public void Worker(object threadData)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
int threadID = (threadData as Boxes.IntBox)?.Val ?? -1;
|
||||
|
||||
int activityStep = 0; /// activity step > 0 in case multiTestParams are used
|
||||
|
||||
for (int iMultiTestParamsItem = 0; iMultiTestParamsItem < MultiTestParams.Count; iMultiTestParamsItem++)
|
||||
{
|
||||
Test currentTest = Tests[iMultiTestParamsItem];
|
||||
ITestParams currentTestParams = MultiTestParams[iMultiTestParamsItem];
|
||||
string currentActivity = currentTestParams.Activity; /// Current activity
|
||||
|
||||
TBF.UiBridge.TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 10, 0, 140, 0, 0, 0 });
|
||||
TBF.UiBridge.Bridge.OnTestProgress(null,
|
||||
new TBF.UiBridge.TestProgressEventArgs(currentTest, Progress.JustStarted));
|
||||
|
||||
|
||||
if (threadID == 0)
|
||||
{
|
||||
StartDataStreamProcessingForActiveMeters(iMultiTestParamsItem);
|
||||
|
||||
/// A new activity starts - information into RFID data log
|
||||
rfidDataLogger.InfoFormat("");
|
||||
rfidDataLogger.WarnFormat("Activity = {0}", currentActivity);
|
||||
rfidDataLogger.InfoFormat("");
|
||||
}
|
||||
|
||||
for (int group = 1; group <= lastGroup; group++)
|
||||
{
|
||||
/// Synchronize with QuidoRS and other threads
|
||||
while (((group != currentGroup) || (activityStep != currentActivityStep)) &&
|
||||
!GetStopWorkerThreads())
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
if (GetStopWorkerThreads()) break;
|
||||
|
||||
// #if TURA_SPECIAL
|
||||
int threadIx = threadID; /// Just one thread for TURA_SPECIAL
|
||||
// #else
|
||||
// for (int threadIx = threadID; threadIx < threadID + 4; threadIx += Cfg.NrThreads)
|
||||
// #endif
|
||||
{
|
||||
bool wmFound = false;
|
||||
|
||||
for (int wmNr0 = 0; wmNr0 < iperlHeads.Count; wmNr0++)
|
||||
{
|
||||
//TODO BUMI doplnit if podomienky - last grop je teraz 1 ak existuju readre
|
||||
if(iperlHeads[wmNr0] is SmartReader ihead)
|
||||
// if ((ihead.Group == group) && (threadIx < muxBrdOrGroup14Nrs.Count) &&
|
||||
// (ihead.MuxBoardNrOrGroup14 == muxBrdOrGroup14Nrs[threadIx]))
|
||||
{
|
||||
wmFound = true;
|
||||
|
||||
WaterMeter wm = null;
|
||||
if (ProcessData.BatchRslts.Batch.WaterMeters != null)
|
||||
{
|
||||
foreach (var w in ProcessData.BatchRslts.Batch.WaterMeters)
|
||||
{
|
||||
if (w.WMPosition == wmNr0 + 1)
|
||||
{
|
||||
wm = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Do worker activity
|
||||
CommErr error = CommErr.None;
|
||||
string resultStr = string.Empty;
|
||||
|
||||
|
||||
WorkerActivity(currentActivity, ihead, wm, currentTest,
|
||||
wmNr0, ref error, ref resultStr, ckbState, threadID, currentActivityStep);
|
||||
ProcessResultOfWorkerActivity(iMultiTestParamsItem, currentActivity,
|
||||
currentGroup, ihead, wm, wmNr0, error, resultStr, ckbState, threadID);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
TBF.UiBridge.Bridge.OnTestProgress(null,
|
||||
new TBF.UiBridge.TestProgressEventArgs(Tests[iMultiTestParamsItem],
|
||||
Progress.FlowSetting));
|
||||
}
|
||||
|
||||
if (!wmFound)
|
||||
{
|
||||
SmartCommunicationForm.OnCommCompleted(null,
|
||||
new CommCompletedEventArgs(threadID, -1, null, null, string.Empty,
|
||||
CommErr.None)); /// Send negative wmNr
|
||||
}
|
||||
|
||||
if (GetStopWorkerThreads()) break;
|
||||
}
|
||||
|
||||
if (GetStopWorkerThreads()) break;
|
||||
} /// for (int group
|
||||
|
||||
TBF.UiBridge.Bridge.OnTestProgress(null,
|
||||
new TBF.UiBridge.TestProgressEventArgs(Tests[iMultiTestParamsItem], Progress.Completed));
|
||||
activityStep++;
|
||||
|
||||
if (GetStopWorkerThreads()) break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool WorkerActivity(string currentActivity, ISmartReader iHead, WaterMeter wm, Test currentTest, int wmNr0,
|
||||
@@ -78,17 +207,17 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
|
||||
|
||||
public void StopWorkerThreads(bool bStopAllThreads)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
stopWorkerThreads = bStopAllThreads;
|
||||
}
|
||||
|
||||
public bool GetStopWorkerThreads()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return stopWorkerThreads;
|
||||
}
|
||||
|
||||
public IList<Thread> GetAllThreads()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return workerThreads;
|
||||
}
|
||||
|
||||
public ICorrections GetNewCorrection()
|
||||
@@ -100,37 +229,229 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ContextMenu GetContextMenu()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public void PrepareForTestsActivities( int waterMeterPositions0)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
StartTime = DateTime.Now;
|
||||
StartTimeSec = StateMachine.Time;
|
||||
|
||||
///
|
||||
/// Prepare worker threads, 'rfidPortNrs', 'lastGroup', etc..
|
||||
///
|
||||
currentActivityStep = 0;
|
||||
currentGroup = 0;
|
||||
completedCommCount = 0;
|
||||
stopWorkerThreads = false;
|
||||
lastGroup = 0;
|
||||
|
||||
if (iperlHeads != null)
|
||||
{
|
||||
foreach (var iSmartReader in iperlHeads)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (iSmartReader is SmartReader reader){
|
||||
if (reader != null ) lastGroup = 1;
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
log.ErrorFormat("PrepareForTestsActivities: {0}", E.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workerThreads = new List<Thread>();
|
||||
if (Cfg != null)
|
||||
{
|
||||
for (int i = 0; i < Cfg.NrThreads; i++)
|
||||
{
|
||||
Thread thread = new Thread(Worker);
|
||||
thread.CurrentCulture = CultureInfo.CurrentCulture;
|
||||
thread.CurrentUICulture = CultureInfo.CurrentUICulture;
|
||||
workerThreads.Add(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Load(Label[] labels, PictureBox[] counters, TextBox[] messages, CheckBoxImage[] checkBoxes, int[] ckbIndex,
|
||||
bool[] ckbState, IList<ISmartReader> iperlHeads, int textBoxesCount, bool checkBoxesEditMode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (iperlHeads == null)
|
||||
{
|
||||
for (int i = 0; i < textBoxesCount; i++)
|
||||
{
|
||||
checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = true;
|
||||
messages[i].Text = "---";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
///
|
||||
/// Set checkbox states accroding to iPerlHeads[i].Disabled states
|
||||
///
|
||||
for (int i = 0; i < textBoxesCount; i++)
|
||||
{
|
||||
labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = true;
|
||||
|
||||
|
||||
ISmartReader iperlHead = iperlHeads[i];
|
||||
if (!checkBoxesEditMode && (iperlHead == null || iperlHead.Disabled))
|
||||
{
|
||||
/// iPerl position i+1 is disabled
|
||||
checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = false;
|
||||
counters[i].BackColor = iPerlCommunicationConstants.DisabledColor;
|
||||
messages[i].Text = "Strings.Head_was_disabled_by_the_user";
|
||||
}
|
||||
else
|
||||
{
|
||||
/// iPerl position i+1 is enabled
|
||||
checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = true;
|
||||
messages[i].Text = "---";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int GetHeadsCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return ParentFrom?.Heads?.Count() ?? 0;
|
||||
}
|
||||
|
||||
public void StartDataStreamProcessingForActiveMeters(int iMultiTestParamsItem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
/// Check whether previous activity was 'Set test mode A0' or 'A4'
|
||||
if (iMultiTestParamsItem > 0 &&
|
||||
MultiTestParams[iMultiTestParamsItem - 1].Activity.ToLower()
|
||||
.Contains(iPerlCommunicationConstants.SetTestModeStr.ToLower()) &&
|
||||
!MultiTestParams[iMultiTestParamsItem - 1].Activity.Contains("80"))
|
||||
{
|
||||
/// Start processing of opto-datastreams from all iPERL-s
|
||||
int count = 0;
|
||||
for (int wmNr0 = 0; wmNr0 < iperlHeads.Count; wmNr0++)
|
||||
{
|
||||
WaterMeter wm = (ProcessData.BatchRslts.Batch.WaterMeters != null &&
|
||||
ProcessData.BatchRslts.Batch.WaterMeters.Count > wmNr0)
|
||||
? ProcessData.BatchRslts.Batch.WaterMeters[wmNr0]
|
||||
: null;
|
||||
|
||||
ISmartReader ihead = iperlHeads[wmNr0];
|
||||
|
||||
if (ihead != null && wm != null && !wm.Disabled)
|
||||
{
|
||||
lock (ihead)
|
||||
{
|
||||
ihead.StartDataStreamProcessing();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.WarnFormat("End of activity '{0}', StartDataStreamProcessing() of {1} heads was called.",
|
||||
MultiTestParams[iMultiTestParamsItem - 1].Activity, count);
|
||||
}
|
||||
}
|
||||
|
||||
public void DoOnCommCompleted(object sender, CommCompletedEventArgs data, IList<int> waterMeterPositions0)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
try
|
||||
{
|
||||
///
|
||||
/// Update the text message
|
||||
///
|
||||
if (data.WMNr0 >= 0) messages[data.WMNr0].Text = data.CommMessage;
|
||||
|
||||
///
|
||||
/// Update head active/inactive switch
|
||||
///
|
||||
if (data.WMNr0 >= 0 && data.CommErr == CommErr.HeadDisabledByUser)
|
||||
{
|
||||
/// iPerl head was disabled by the user
|
||||
ckbState[data.WMNr0] = false;
|
||||
checkBoxes[data.WMNr0].Checked = false;
|
||||
checkBoxes[data.WMNr0].Enabled = false;
|
||||
if (data.Ihead != null) data.Ihead.Disabled = true;
|
||||
if (data.Wm != null) data.Wm.Disabled = true;
|
||||
}
|
||||
else if (data.WMNr0 >= 0 && data.CommErr == CommErr.None)
|
||||
{
|
||||
/// One RFID communication successful => iPerl cannot be disabled by the user anymore
|
||||
ckbState[data.WMNr0] = true;
|
||||
checkBoxes[data.WMNr0].Checked = true;
|
||||
checkBoxes[data.WMNr0].Enabled = false;
|
||||
}
|
||||
|
||||
///
|
||||
/// Update opto-communication indication
|
||||
///
|
||||
for (int i = 0; i < iperlHeads.Count; i++)
|
||||
{
|
||||
if (iperlHeads[i] == null || iperlHeads[i].Disabled)
|
||||
{
|
||||
counters[i].BackColor = iPerlCommunicationConstants.DisabledColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iperlHeads[i] is SmartReader iperlHead)
|
||||
{
|
||||
|
||||
OptoHeadState checkFlowDirection =
|
||||
((iperlHead == null) ? OptoHeadState.Disabled : iperlHead.CheckFlowDirection());
|
||||
switch (checkFlowDirection)
|
||||
{
|
||||
case OptoHeadState.OptoAndDirOK:
|
||||
counters[i].BackColor = iPerlCommunicationConstants.OptoAndDirOKColor;
|
||||
break;
|
||||
|
||||
case OptoHeadState.DirNok:
|
||||
counters[i].BackColor = iPerlCommunicationConstants.DirNokColor;
|
||||
break;
|
||||
|
||||
default:
|
||||
case OptoHeadState.OptoNok:
|
||||
counters[i].BackColor = iPerlCommunicationConstants.OptoNokColor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !TURA_SPECIAL
|
||||
///
|
||||
/// Branch
|
||||
///
|
||||
lock (this)
|
||||
{
|
||||
if (++completedCommCount < 4) return;
|
||||
completedCommCount = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (currentGroup < lastGroup)
|
||||
{
|
||||
/// Go to the next step / next group
|
||||
currentGroup++;
|
||||
}
|
||||
else if (currentActivityStep + 1 < MultiTestParams.Count)
|
||||
{
|
||||
currentGroup = 0;
|
||||
currentActivityStep++;
|
||||
activityLabel.Text = MultiTestParams[currentActivityStep].Activity;
|
||||
currentGroup++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Wait until all threads are finished
|
||||
workerThreads[data.ThreadId].Join(2000);
|
||||
ParentFrom.NormalClose();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.ErrorFormat("DoOnCommCompleted({0}) failed: {1}", data, e.Message);
|
||||
log.FatalFormat("StackTrace : {0}{1}", Environment.NewLine, e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
public void NormalClose(IList<int> waterMeterPositions0)
|
||||
@@ -171,7 +492,129 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
|
||||
this.cfg = cfg;
|
||||
this.multiTestParams = multiTestParams;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
///
|
||||
private MenuItem NewMenuItem(string text, string tag)
|
||||
{
|
||||
MenuItem menuItem = new MenuItem { Text = text, Tag = tag };
|
||||
menuItem.Click += OnClick_Optical_Heads_Settings_Menu;
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
private async void OnClick_Optical_Heads_Settings_Menu(object sender, EventArgs e)
|
||||
{
|
||||
// Validate sender
|
||||
if (!(sender is MenuItem menuItem))
|
||||
{
|
||||
log?.Error("OnClick_Optical_Heads_Settings_Menu: sender is not a MenuItem");
|
||||
return;
|
||||
}
|
||||
|
||||
if (activityLabel != null)
|
||||
activityLabel.Text = menuItem.Text;
|
||||
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (var iSmartReader in ProcessData.SmartHeadsUni)
|
||||
{
|
||||
if (!(iSmartReader is SmartReader iHead))
|
||||
{
|
||||
continue;//ignore different types of heads
|
||||
}
|
||||
|
||||
int position = iHead.Position;
|
||||
if (position < 0 || position >= checkBoxes.Length || position >= messages.Length)
|
||||
{
|
||||
continue; // Skip this head if position is out of range
|
||||
}
|
||||
if (!checkBoxes[position].Checked)
|
||||
{
|
||||
if (position < messages.Length) messages[position].Text = "";
|
||||
continue;
|
||||
}
|
||||
|
||||
messages[position].Text = $@"COM{iHead.RfidComPortNr}";
|
||||
Application.DoEvents(); // Refresh UI
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
string result = await ProcessTask(iHead.RegPoseidonCfg, menuItem.Tag);
|
||||
ParentFrom?.Invoke((Action)(() =>
|
||||
{
|
||||
messages[position].Text = result;
|
||||
Application.DoEvents(); // Refresh UI
|
||||
}));
|
||||
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
public static bool TryParseByDescription(string description, out PoseidonImplHeadTestCtrl.Operations result)
|
||||
{
|
||||
foreach (PoseidonImplHeadTestCtrl.Operations op
|
||||
in Enum.GetValues(typeof(PoseidonImplHeadTestCtrl.Operations)))
|
||||
{
|
||||
// step-by-step compare
|
||||
var desc = ((Enum)op).ToDescription(); // uses extension above
|
||||
|
||||
// exact compare, you can use OrdinalIgnoreCase if you want
|
||||
if (string.Equals(desc, description, StringComparison.Ordinal))
|
||||
{
|
||||
result = op;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
result = PoseidonImplHeadTestCtrl.Operations.Empty;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
private async Task<string> ProcessTask(IComponentCfg head, object tag)
|
||||
{
|
||||
string txt = "";
|
||||
PoseidonImplHeadTestCtrl.Operations operation;
|
||||
if (!TryParseByDescription((string)tag, out operation))
|
||||
{
|
||||
operation = PoseidonImplHeadTestCtrl.Operations.Empty;
|
||||
}
|
||||
|
||||
if (head is PoseidonCfg poseidonCfg)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case PoseidonImplHeadTestCtrl.Operations.ReadSerialNo:
|
||||
txt = OpticalHeadTest.ReadRequest_SerialNo(poseidonCfg);
|
||||
break;
|
||||
case PoseidonImplHeadTestCtrl.Operations.SetTestModeOn:
|
||||
txt = OpticalHeadTest.SetTestMode(poseidonCfg);
|
||||
break;
|
||||
case PoseidonImplHeadTestCtrl.Operations.SetTestModeOff:
|
||||
txt = OpticalHeadTest.SetActiveMode(poseidonCfg);
|
||||
break;
|
||||
default:
|
||||
txt = "-";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
public ContextMenu GetContextMenu()
|
||||
{
|
||||
ContextMenu cm = new ContextMenu();
|
||||
foreach (KeyValuePair<string, PoseidonImplHeadTestCtrl.Operations> itemsOperation in PoseidonImplHeadTestCtrl.ItemsOperations)
|
||||
{
|
||||
cm.MenuItems.Add(NewMenuItem(itemsOperation.Key, itemsOperation.Value.ToDescription()));
|
||||
}
|
||||
return cm;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+10
-7
@@ -63,7 +63,6 @@
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;CAMERA;LANG_PL;IPERL;</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
@@ -75,7 +74,6 @@
|
||||
<DefineConstants>TRACE;CAMERA;LANG_PL;IPERL;</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
@@ -1323,15 +1321,11 @@
|
||||
<DependentUpon>RRCfgCtrl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunner.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliTaskInfo.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliTaskState.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\Factory.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\GetDescription.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\JsonDataFromPoseidon.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonCfg.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonProcParams.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReader.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReadCycle.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\ProcessExtensions.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\ScopedLoggerFactory.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\SerialPortData.cs" />
|
||||
@@ -2073,6 +2067,7 @@
|
||||
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\common\ICommonRegReader.cs" />
|
||||
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\common\ICorrections.cs" />
|
||||
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\common\ISmartReader.cs" />
|
||||
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\EnumExtensions.cs" />
|
||||
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\IPerlCorrections.cs" />
|
||||
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\PoseidonCorrections.cs" />
|
||||
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\SmartComponentBase.cs" />
|
||||
@@ -3974,6 +3969,10 @@
|
||||
<Project>{5954d496-caab-4f7a-bde2-bdc8f47dab19}</Project>
|
||||
<Name>NfcS5_DLL</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\AppDiagnostic\AppDiagnostic.csproj">
|
||||
<Project>{fa9abad1-7184-4295-ade8-d44f2e3de6b2}</Project>
|
||||
<Name>AppDiagnostic</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Common\Common.csproj">
|
||||
<Project>{c8939821-ba5c-4988-a3d0-bf53b74865c7}</Project>
|
||||
<Name>Common</Name>
|
||||
@@ -4014,6 +4013,10 @@
|
||||
<Project>{0f79ca69-9dbc-41f3-a6fc-5a2937365343}</Project>
|
||||
<Name>SchematicDrawing</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SharedComponents\SharedComponents.csproj">
|
||||
<Project>{8f942729-f454-4c99-ba6c-746962065ae3}</Project>
|
||||
<Name>SharedComponents</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SharedDatabase\SharedDatabase.csproj">
|
||||
<Project>{211b5e3f-9996-48a7-abde-c878dd2d71c2}</Project>
|
||||
<Name>SharedDatabase</Name>
|
||||
@@ -4031,4 +4034,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
+6
-2
@@ -13,9 +13,13 @@ using Common;
|
||||
using Config.Entities;
|
||||
using Dirichlet.Numerics;
|
||||
using SchematicDrawing;
|
||||
using SharedDatabase;
|
||||
using TBF.Resources;
|
||||
using TBF.UiBridge;
|
||||
using TBF.UI.Shared;
|
||||
using AppDiagnostic;
|
||||
using SharedComponents;
|
||||
using System.Diagnostics;
|
||||
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
|
||||
|
||||
namespace TBF.UI
|
||||
@@ -1160,8 +1164,8 @@ namespace TBF.UI
|
||||
ILog loger = LogManager.GetLogger("logCache");
|
||||
log.Debug("ahoj, ideme");*/
|
||||
|
||||
//LiveLogCache.Instance.AddLog("=== Live diagnostic entry ===");
|
||||
//DiagApi liveDiagApi = new DiagApi();
|
||||
LiveLogCache.Instance.AddLog("=== Live diagnostic entry ===");
|
||||
DiagApi liveDiagApi = new DiagApi();
|
||||
|
||||
/*try
|
||||
{
|
||||
|
||||
@@ -12,450 +12,161 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
[TestSubject(typeof(CliRunner))]
|
||||
public class CliRunnerTest
|
||||
{
|
||||
|
||||
[TestMethod]
|
||||
public void ExtractJson_Test()
|
||||
{
|
||||
string allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
|
||||
String allOutput = " {\n \"DeviceId\": \"1000000267\"\n}\n";
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
|
||||
string json = cliRunner.ExtractJson(allOutput);
|
||||
|
||||
Assert.IsFalse(string.IsNullOrEmpty(json));
|
||||
|
||||
Assert.IsTrue(!string.IsNullOrEmpty(json));
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void ExtractJson_Test2()
|
||||
{
|
||||
string allOutput =
|
||||
"{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
|
||||
String allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
|
||||
string json = cliRunner.ExtractJson(allOutput);
|
||||
|
||||
Assert.IsFalse(string.IsNullOrEmpty(json));
|
||||
|
||||
Assert.IsTrue(!string.IsNullOrEmpty(json));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryJsonStringDeserialize_Test()
|
||||
{
|
||||
string allOutput =
|
||||
"{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
|
||||
|
||||
String allOutput = "{\n \"NfcTagDetected\": true,\n \"ProductType\": 74,\n \"ProductTypeVersion\": \"B1.0.13\",\n \"DeviceId\": \"1000000267\",\n \"Reading\": \"0003292.1\",\n \"Reading_Totalizer\": \"000329218\",\n \"Reading_Digits\": \"8\",\n \"Reading_Shift\": \"-1\",\n \"Reading_Resolution\": \"-2\",\n \"Reading_Units\": \"2\",\n \"Reading_FlowDirection\": \"3\",\n \"Reading_FlowRate\": \"0\",\n \"CalibrationFactor\": \"3197\",\n \"ReadingComplete\": true,\n \"MeterState\": \"0x02\",\n \"OpticalDataMode\": \"0x00\",\n \"SpreadSpectrumParameters\": \"Disabled: 0xTrue\",\n \"BuildInformation\": \"\"\n}\n";
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
string json = cliRunner.ExtractJson(allOutput);
|
||||
|
||||
bool ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
|
||||
|
||||
var ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
|
||||
Assert.IsTrue(ok, "Deserialization failed");
|
||||
Assert.IsNotNull(dto);
|
||||
Assert.AreEqual("1000000267", dto.DeviceId);
|
||||
Assert.AreEqual("0003292.1", dto.Reading);
|
||||
Assert.AreEqual("000329218", dto.Reading_Totalizer);
|
||||
Assert.AreEqual(true, dto.NfcTagDetected);
|
||||
Assert.AreEqual("74", dto.ProductType);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerialPortData_NewDefaults_ShouldUsePoseidonMthTimeout10()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData(
|
||||
"COM3",
|
||||
"HalCli.exe",
|
||||
SerialPortData.MeterProduct.Poseidon);
|
||||
|
||||
string args = serialPort.DefaultArgSettings(SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat mth --timeout 10 --operation readall",
|
||||
args);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerialPortData_HarryHat_ShouldUseHarry()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData(
|
||||
"COM3",
|
||||
"HalCli.exe",
|
||||
SerialPortData.MeterProduct.Poseidon,
|
||||
SerialPortData.HatType.Harry,
|
||||
30);
|
||||
|
||||
string args = serialPort.DefaultArgSettings(SerialPortData.EMeterArg.DeviceId);
|
||||
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat harry --timeout 30 --operation read --parameter DeviceId",
|
||||
args);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerialPortData_OldConstructor_ShouldKeepOldMeterType()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData("COM3", "HalCli.exe", 74);
|
||||
|
||||
string args = serialPort.DefaultArgSettings(SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat mth --timeout 10 --operation readall",
|
||||
args);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerialPortData_NewMeterTypes_ShouldGenerateCorrectMeterNames()
|
||||
{
|
||||
var testCases = new[]
|
||||
{
|
||||
new { Meter = SerialPortData.MeterProduct.Poseidon, Expected = "74" },
|
||||
new { Meter = SerialPortData.MeterProduct.Ally, Expected = "ally" },
|
||||
new { Meter = SerialPortData.MeterProduct.IperlPlus, Expected = "iperlplus" },
|
||||
new { Meter = SerialPortData.MeterProduct.IperlLegacy, Expected = "iperllegacy" }
|
||||
};
|
||||
|
||||
foreach (var testCase in testCases)
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData(
|
||||
"COM3",
|
||||
"HalCli.exe",
|
||||
testCase.Meter);
|
||||
|
||||
string args = serialPort.DefaultArgSettings(SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
StringAssert.Contains(args, $"-m {testCase.Expected}");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerialPortData_ReadMacros_ShouldGenerateCorrectOperations()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData(
|
||||
"COM3",
|
||||
"HalCliDemo.exe",
|
||||
SerialPortData.MeterProduct.Poseidon);
|
||||
|
||||
serialPort.Macro = "readmacro1";
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat mth --timeout 10 --operation readmacro1",
|
||||
serialPort.DefaultArgSettings(SerialPortData.EMeterArg.ReadMacro1));
|
||||
|
||||
serialPort.Macro = "readmacro2";
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat mth --timeout 10 --operation readmacro2",
|
||||
serialPort.DefaultArgSettings(SerialPortData.EMeterArg.ReadMacro2));
|
||||
|
||||
serialPort.Macro = "readmacro3";
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat mth --timeout 10 --operation readmacro3",
|
||||
serialPort.DefaultArgSettings(SerialPortData.EMeterArg.ReadMacro3));
|
||||
|
||||
serialPort.Macro = "xxx";
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat mth --timeout 10 --operation xxx",
|
||||
serialPort.DefaultArgSettings(SerialPortData.EMeterArg.AllParams));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerialPortData_CliPath_ShouldAlwaysUseFixedCliDirectory()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData(
|
||||
"COM3",
|
||||
@"D:\obsolete\HatCliDemo.exe",
|
||||
SerialPortData.MeterProduct.Poseidon);
|
||||
|
||||
Assert.AreEqual(
|
||||
@"C:\TBF\Cli\HatCliDemo.exe",
|
||||
serialPort.SerialPortCmdClientPath);
|
||||
Assert.AreEqual(74, dto.ProductType);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RunMultipleTimesTestProgram_CheckParalelWork()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData(
|
||||
"COM3",
|
||||
"cmdSleepTest.exe",
|
||||
SerialPortData.MeterProduct.Poseidon,
|
||||
SerialPortData.HatType.Mth,
|
||||
10);
|
||||
|
||||
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74);
|
||||
|
||||
// Check if the executable exists in current directory
|
||||
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
|
||||
{
|
||||
Assert.Inconclusive(
|
||||
$"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
|
||||
Assert.Inconclusive($"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
|
||||
return;
|
||||
}
|
||||
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
|
||||
|
||||
CliRunner cliRunnerOne = new CliRunner(false);
|
||||
DateTime startTime = DateTime.Now;
|
||||
|
||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
DateTime StartTime = DateTime.Now;
|
||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
cliRunnerOne.WaitAll();
|
||||
|
||||
TimeSpan oneEndTime = DateTime.Now - startTime;
|
||||
|
||||
startTime = DateTime.Now;
|
||||
Console.WriteLine($"Start Time Loop: {startTime:yyyy-MM-dd HH:mm:ss}");
|
||||
|
||||
TimeSpan OneEndTime = DateTime.Now - StartTime;
|
||||
|
||||
StartTime = DateTime.Now;
|
||||
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
}
|
||||
|
||||
cliRunner.WaitAll();
|
||||
|
||||
DateTime endTime = DateTime.Now;
|
||||
Console.WriteLine($"End Time Loop: {endTime:yyyy-MM-dd HH:mm:ss}");
|
||||
|
||||
TimeSpan delta = endTime - startTime;
|
||||
|
||||
Console.WriteLine(
|
||||
$"Delta Time One process {oneEndTime.Hours:D2}:{oneEndTime.Minutes:D2}:{oneEndTime.Seconds:D2}.{oneEndTime.Milliseconds:D3} " +
|
||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||
|
||||
DateTime EndTime = DateTime.Now;
|
||||
Console.WriteLine($"End Time Loop: {EndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
TimeSpan delta = EndTime - StartTime;
|
||||
Console.WriteLine($"Delta Time One process {OneEndTime.Hours:D2}:{OneEndTime.Minutes:D2}:{OneEndTime.Seconds:D2}.{OneEndTime.Milliseconds:D3} " +
|
||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||
Console.WriteLine($"Total tasks: {cliRunner.TaskPool.Count}");
|
||||
|
||||
foreach (CliTaskInfo taskInfo in cliRunner.TaskPool)
|
||||
|
||||
|
||||
foreach (Task<JsonDataFromPoseidon> task in cliRunner.TaskPool)
|
||||
{
|
||||
Assert.IsNotNull(taskInfo);
|
||||
Assert.IsNotNull(taskInfo.Task);
|
||||
|
||||
if (taskInfo.UseResult)
|
||||
if (task != null)
|
||||
{
|
||||
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
||||
Assert.IsNotNull(task, "Expected Task<JsonDataFromPoseidon>.");
|
||||
|
||||
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
|
||||
|
||||
Console.WriteLine($"Result: {jsonDataFromPoseidon?.DeviceId}");
|
||||
|
||||
Console.WriteLine($"Result: {jsonDataFromPoseidon.DeviceId}");
|
||||
Assert.IsNotNull(jsonDataFromPoseidon);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail(
|
||||
$"Task did not complete successfully. State={taskInfo.State}, Status={taskInfo.Task.Status}");
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue((cliRunner.TaskPool.Count * oneEndTime.Ticks) > delta.Ticks);
|
||||
|
||||
// Check that the total time is greater than the sum of the individual times
|
||||
Assert.IsTrue((cliRunner.TaskPool.Count*OneEndTime.Ticks) > delta.Ticks);
|
||||
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public async Task RunMultipleTimesTestProgram_CheckParalelWorkCumulative()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData(
|
||||
"COM3",
|
||||
"cmdSleepTest.exe",
|
||||
SerialPortData.MeterProduct.Poseidon,
|
||||
SerialPortData.HatType.Mth,
|
||||
10);
|
||||
|
||||
SerialPortData serialPort = new SerialPortData("COM3","cmdSleepTest.exe",74);
|
||||
|
||||
// Check if the executable exists in current directory
|
||||
if (!System.IO.File.Exists(serialPort.SerialPortCmdClientPath))
|
||||
{
|
||||
Assert.Inconclusive(
|
||||
$"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
|
||||
Assert.Inconclusive($"Test executable '{serialPort.SerialPortCmdClientPath}' not found. Please ensure cmdSleepTest.exe exists in the test directory.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<CliRunner> cliRunnerList = new List<CliRunner>();
|
||||
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
cliRunnerList.Add(new CliRunner(false));
|
||||
}
|
||||
|
||||
|
||||
CliRunner cliRunnerOne = new CliRunner(false);
|
||||
DateTime startTime = DateTime.Now;
|
||||
|
||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
DateTime StartTime = DateTime.Now;
|
||||
cliRunnerOne.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort, SerialPortData.EMeterArg.AllParams);
|
||||
cliRunnerOne.WaitAll();
|
||||
|
||||
TimeSpan oneEndTime = DateTime.Now - startTime;
|
||||
|
||||
startTime = DateTime.Now;
|
||||
Console.WriteLine($"Start Time Loop: {startTime:yyyy-MM-dd HH:mm:ss}");
|
||||
TimeSpan OneEndTime = DateTime.Now - StartTime;
|
||||
|
||||
StartTime = DateTime.Now;
|
||||
Console.WriteLine($"Start Time Loop: {StartTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
|
||||
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
|
||||
{
|
||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
serialPort,
|
||||
cliRunnerList[iCliRunner].AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(serialPort,
|
||||
SerialPortData.EMeterArg.AllParams);
|
||||
}
|
||||
}
|
||||
|
||||
Task[] allTasks = cliRunnerList
|
||||
.SelectMany(r => r.TaskPool)
|
||||
.Where(ti => ti != null && ti.Task != null)
|
||||
.Select(ti => ti.Task)
|
||||
.ToArray();
|
||||
|
||||
// Wait for ALL tasks from ALL runners
|
||||
var allTasks = cliRunnerList.SelectMany(r => r.TaskPool).ToArray();
|
||||
await Task.WhenAll(allTasks);
|
||||
|
||||
DateTime endTime = DateTime.Now;
|
||||
Console.WriteLine($"End Time Loop: {endTime:yyyy-MM-dd HH:mm:ss}");
|
||||
|
||||
TimeSpan delta = endTime - startTime;
|
||||
|
||||
Console.WriteLine(
|
||||
$"Delta Time One process {oneEndTime.Hours:D2}:{oneEndTime.Minutes:D2}:{oneEndTime.Seconds:D2}.{oneEndTime.Milliseconds:D3} " +
|
||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||
|
||||
DateTime EndTime = DateTime.Now;
|
||||
Console.WriteLine($"End Time Loop: {EndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
TimeSpan delta = EndTime - StartTime;
|
||||
Console.WriteLine($"Delta Time One process {OneEndTime.Hours:D2}:{OneEndTime.Minutes:D2}:{OneEndTime.Seconds:D2}.{OneEndTime.Milliseconds:D3} " +
|
||||
$"vs Loop: {delta.Hours:D2}:{delta.Minutes:D2}:{delta.Seconds:D2}.{delta.Milliseconds:D3}");
|
||||
Console.WriteLine($"Total tasks: {cliRunnerList.Sum(runner => runner.TaskPool.Count)}");
|
||||
|
||||
|
||||
for (int iCliRunner = 0; iCliRunner < cliRunnerList.Count; iCliRunner++)
|
||||
{
|
||||
string results = $"iCliRunner: {iCliRunner}";
|
||||
|
||||
foreach (CliTaskInfo taskInfo in cliRunnerList[iCliRunner].TaskPool)
|
||||
foreach (Task<JsonDataFromPoseidon> task in cliRunnerList[iCliRunner].TaskPool)
|
||||
{
|
||||
Assert.IsNotNull(taskInfo);
|
||||
Assert.IsNotNull(taskInfo.Task);
|
||||
|
||||
if (taskInfo.UseResult)
|
||||
if (task != null)
|
||||
{
|
||||
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
||||
Assert.IsNotNull(task, "Expected Task<JsonDataFromPoseidon>.");
|
||||
|
||||
JsonDataFromPoseidon jsonDataFromPoseidon = task.Result;
|
||||
|
||||
results += $" ID: {jsonDataFromPoseidon?.DeviceId}";
|
||||
|
||||
results += ($" ID: {jsonDataFromPoseidon.DeviceId}");
|
||||
Assert.IsNotNull(jsonDataFromPoseidon);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail(
|
||||
$"Task did not complete successfully. Runner={iCliRunner}, State={taskInfo.State}, Status={taskInfo.Task.Status}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine(results);
|
||||
}
|
||||
|
||||
Assert.IsTrue((cliRunnerList.Count * oneEndTime.Ticks) > delta.Ticks);
|
||||
// Check that the total time is greater than the sum of the individual times
|
||||
Assert.IsTrue((cliRunnerList.Count*OneEndTime.Ticks) > delta.Ticks);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void TryJsonStringDeserialize_ReadMacro1_Test()
|
||||
{
|
||||
string allOutput = @"{
|
||||
""DeviceId"": ""1000000176"",
|
||||
""MeterState"": ""MB012501A177"",
|
||||
""CalibrationFactor"": ""3250"",
|
||||
""Reading_Units"": ""0"",
|
||||
""Reading_Totalizer"": 0,
|
||||
""Reading_FlowRate"": 0,
|
||||
""Reading_FlowDirection"": 0,
|
||||
""Reading_Digits"": 0,
|
||||
""Reading_Shift"": 0,
|
||||
""Reading_Resolution"": 0,
|
||||
""OpticalDataMode"": ""199"",
|
||||
""MeterSize"": ""3"",
|
||||
""ManufactureDate"": ""2026/04/20 00:56:00"",
|
||||
""ProductType"": ""74"",
|
||||
""ProductTypeVersion"": ""B1.0.29"",
|
||||
""CliVersion"": ""2.0.0.0"",
|
||||
""ReadingComplete"": true,
|
||||
""NfcTagDetected"": true
|
||||
}";
|
||||
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
string json = cliRunner.ExtractJson(allOutput);
|
||||
|
||||
bool ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
Assert.IsNotNull(dto);
|
||||
Assert.AreEqual("1000000176", dto.DeviceId);
|
||||
Assert.AreEqual("MB012501A177", dto.MeterState);
|
||||
Assert.AreEqual("3250", dto.CalibrationFactor);
|
||||
Assert.AreEqual("199", dto.OpticalDataMode);
|
||||
Assert.AreEqual("3", dto.MeterSize);
|
||||
Assert.AreEqual("74", dto.ProductType);
|
||||
Assert.AreEqual("2.0.0.0", dto.CliVersion);
|
||||
Assert.IsTrue(dto.ReadingComplete);
|
||||
Assert.IsTrue(dto.NfcTagDetected);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryJsonStringDeserialize_ReadMacro2_Test()
|
||||
{
|
||||
string allOutput = @"{
|
||||
""DeviceId"": ""1000000176"",
|
||||
""Reading_Units"": ""0"",
|
||||
""Reading_Totalizer"": 0,
|
||||
""Reading_FlowRate"": 0,
|
||||
""Reading_FlowDirection"": 0,
|
||||
""Reading_Digits"": 0,
|
||||
""Reading_Shift"": 0,
|
||||
""Reading_Resolution"": 0,
|
||||
""ProductType"": ""74"",
|
||||
""ProductTypeVersion"": ""B1.0.29"",
|
||||
""CliVersion"": ""2.0.0.0"",
|
||||
""ReadingComplete"": true,
|
||||
""NfcTagDetected"": true
|
||||
}";
|
||||
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
string json = cliRunner.ExtractJson(allOutput);
|
||||
|
||||
bool ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
Assert.IsNotNull(dto);
|
||||
Assert.AreEqual("1000000176", dto.DeviceId);
|
||||
Assert.AreEqual("0", dto.Reading_Units);
|
||||
Assert.AreEqual("74", dto.ProductType);
|
||||
Assert.AreEqual("B1.0.29", dto.ProductTypeVersion);
|
||||
Assert.AreEqual("2.0.0.0", dto.CliVersion);
|
||||
Assert.IsTrue(dto.ReadingComplete);
|
||||
Assert.IsTrue(dto.NfcTagDetected);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryJsonStringDeserialize_ReadMacro3_Test()
|
||||
{
|
||||
string allOutput = @"{
|
||||
""DeviceId"": ""1000000176"",
|
||||
""Reading_Units"": ""0"",
|
||||
""Reading_Totalizer"": 0,
|
||||
""Reading_FlowRate"": 0,
|
||||
""Reading_FlowDirection"": 0,
|
||||
""Reading_Digits"": 0,
|
||||
""Reading_Shift"": 0,
|
||||
""Reading_Resolution"": 0,
|
||||
""CliVersion"": ""2.0.0.0"",
|
||||
""ReadingComplete"": true,
|
||||
""NfcTagDetected"": true
|
||||
}";
|
||||
|
||||
CliRunner cliRunner = new CliRunner(false);
|
||||
string json = cliRunner.ExtractJson(allOutput);
|
||||
|
||||
bool ok = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out var dto);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
Assert.IsNotNull(dto);
|
||||
Assert.AreEqual("1000000176", dto.DeviceId);
|
||||
Assert.AreEqual("0", dto.Reading_Units);
|
||||
Assert.AreEqual("2.0.0.0", dto.CliVersion);
|
||||
Assert.IsTrue(dto.ReadingComplete);
|
||||
Assert.IsTrue(dto.NfcTagDetected);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
|
||||
|
||||
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
[TestClass]
|
||||
[TestSubject(typeof(CliRunner))]
|
||||
public class CliRunnerTimeoutTest
|
||||
{
|
||||
private static SerialPortData CreateDefaultSerialPortData()
|
||||
{
|
||||
return new SerialPortData(
|
||||
"COM3",
|
||||
"cmdSleepTest.exe",
|
||||
SerialPortData.MeterProduct.Poseidon,
|
||||
SerialPortData.HatType.Mth,
|
||||
30,
|
||||
TBF.Rig.RegisterReaders.PoseidonCmdStartStop.GetDescription.ToDescription(SerialPortData.MacroTypes.readall));
|
||||
}
|
||||
|
||||
private static string GetCmdSleepTestPath()
|
||||
{
|
||||
return CreateDefaultSerialPortData().SerialPortCmdClientPath;
|
||||
}
|
||||
|
||||
private static void EnsureExeExists(string exePath)
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
Assert.Inconclusive(
|
||||
$"Test executable '{exePath}' not found. " +
|
||||
"Please ensure cmdSleepTest.exe exists in the test directory.");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerialPortData_ForTimeoutTests_ShouldUseVersion2Defaults()
|
||||
{
|
||||
SerialPortData serialPort = CreateDefaultSerialPortData();
|
||||
|
||||
string args = serialPort.DefaultArgSettings(SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat mth --timeout 30 --operation readall",
|
||||
args);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerialPortData_ForTimeoutTests_ShouldStillSupportOldMeterType()
|
||||
{
|
||||
SerialPortData serialPort = new SerialPortData(
|
||||
"COM3",
|
||||
"cmdSleepTest.exe",
|
||||
74);
|
||||
|
||||
string args = serialPort.DefaultArgSettings(SerialPortData.EMeterArg.AllParams);
|
||||
|
||||
Assert.AreEqual(
|
||||
"-p COM3 -m 74 --hat mth --timeout 10 --operation readall",
|
||||
args);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Timeout_ShouldBeDetected_ForLongRunningTask()
|
||||
{
|
||||
string exePath = GetCmdSleepTestPath();
|
||||
EnsureExeExists(exePath);
|
||||
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||
|
||||
await Task.Delay(200);
|
||||
|
||||
bool timedOut = cliRunner.TimeOutReceived(100);
|
||||
|
||||
Assert.IsTrue(timedOut, "Timeout should have been detected.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CancelUndoneTasksAsTimedOut_ShouldMarkRunningTaskAsTimedOut()
|
||||
{
|
||||
string exePath = GetCmdSleepTestPath();
|
||||
EnsureExeExists(exePath);
|
||||
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||
|
||||
await Task.Delay(150);
|
||||
|
||||
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
||||
|
||||
var taskInfo = cliRunner.TaskPool.Single();
|
||||
|
||||
Assert.IsNotNull(taskInfo);
|
||||
Assert.AreEqual(CliTaskState.TimedOut, taskInfo.State);
|
||||
Assert.IsFalse(taskInfo.UseResult, "Timed out task must not be used.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CancelUndoneTasksAsTimedOut_ShouldKeepCompletedTaskCompleted()
|
||||
{
|
||||
string exePath = GetCmdSleepTestPath();
|
||||
EnsureExeExists(exePath);
|
||||
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50");
|
||||
|
||||
try
|
||||
{
|
||||
cliRunner.WaitAll();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Let assertions inspect states.
|
||||
}
|
||||
|
||||
cliRunner.RefreshTaskStates();
|
||||
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
||||
|
||||
var taskInfo = cliRunner.TaskPool.Single();
|
||||
|
||||
Assert.IsNotNull(taskInfo);
|
||||
Assert.AreEqual(CliTaskState.Completed, taskInfo.State);
|
||||
Assert.IsTrue(taskInfo.UseResult, "Completed task should remain usable.");
|
||||
|
||||
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
||||
|
||||
Assert.IsNotNull(task);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
|
||||
Assert.IsNotNull(task.Result);
|
||||
Assert.IsFalse(string.IsNullOrEmpty(task.Result.DeviceId));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CancelUndoneTasksAsTimedOut_ShouldKeepDoneTask_AndMarkUndoneTask()
|
||||
{
|
||||
string exePath = GetCmdSleepTestPath();
|
||||
EnsureExeExists(exePath);
|
||||
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50");
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||
|
||||
await Task.Delay(250);
|
||||
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
Assert.AreEqual(2, cliRunner.TaskPool.Count);
|
||||
Assert.AreEqual(
|
||||
1,
|
||||
cliRunner.TaskPool.Count(t => t.State == CliTaskState.Completed),
|
||||
"Exactly one fast task should be completed before timeout handling.");
|
||||
|
||||
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
var doneTask = cliRunner.TaskPool.Single(t => t.State == CliTaskState.Completed);
|
||||
var timedOutTask = cliRunner.TaskPool.Single(t => t.State == CliTaskState.TimedOut);
|
||||
|
||||
Assert.IsTrue(doneTask.UseResult);
|
||||
Assert.IsFalse(timedOutTask.UseResult);
|
||||
|
||||
var completed = doneTask.Task as Task<JsonDataFromPoseidon>;
|
||||
|
||||
Assert.IsNotNull(completed);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, completed.Status);
|
||||
Assert.IsNotNull(completed.Result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task AreTasksDone_ShouldReturnTrue_AfterTimeoutCancellation()
|
||||
{
|
||||
string exePath = GetCmdSleepTestPath();
|
||||
EnsureExeExists(exePath);
|
||||
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||
|
||||
await Task.Delay(150);
|
||||
|
||||
Assert.IsFalse(
|
||||
cliRunner.AreTasksDone(),
|
||||
"Task should still be running before timeout cancellation.");
|
||||
|
||||
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
Assert.IsTrue(
|
||||
cliRunner.AreTasksDone(),
|
||||
"After timed out tasks are marked, runner should report no running tasks.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TimedOutTask_ShouldNotHaveUseResult()
|
||||
{
|
||||
string exePath = GetCmdSleepTestPath();
|
||||
EnsureExeExists(exePath);
|
||||
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
||||
|
||||
await Task.Delay(150);
|
||||
|
||||
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
var taskInfo = cliRunner.TaskPool.Single();
|
||||
|
||||
Assert.AreEqual(CliTaskState.TimedOut, taskInfo.State);
|
||||
Assert.IsFalse(taskInfo.UseResult);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MultipleSlowTasks_ShouldAllBeMarkedTimedOut()
|
||||
{
|
||||
string exePath = GetCmdSleepTestPath();
|
||||
EnsureExeExists(exePath);
|
||||
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
exePath,
|
||||
"sleep=5000");
|
||||
}
|
||||
|
||||
await Task.Delay(200);
|
||||
|
||||
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
Assert.AreEqual(5, cliRunner.TaskPool.Count);
|
||||
Assert.AreEqual(5, cliRunner.TaskPool.Count(t => t.State == CliTaskState.TimedOut));
|
||||
Assert.AreEqual(0, cliRunner.TaskPool.Count(t => t.UseResult));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FastTask_WithInvalidJson_ShouldFinishButNotProduceUsefulResult()
|
||||
{
|
||||
string exePath = GetCmdSleepTestPath();
|
||||
EnsureExeExists(exePath);
|
||||
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
exePath,
|
||||
"sleep=50 invalidjson=true");
|
||||
|
||||
try
|
||||
{
|
||||
cliRunner.WaitAll();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Let assertions inspect states.
|
||||
}
|
||||
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
||||
|
||||
var taskInfo = cliRunner.TaskPool.Single();
|
||||
|
||||
Assert.AreEqual(CliTaskState.Completed, taskInfo.State);
|
||||
|
||||
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
||||
|
||||
Assert.IsNotNull(task);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
|
||||
|
||||
Assert.IsNull(task.Result, "Invalid JSON must not create a default reading object.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CliProcess_ExitErrorWithoutJson_RecordsDiagnosticsAndReturnsNoReading()
|
||||
{
|
||||
string cmdExe = System.Environment.GetEnvironmentVariable("ComSpec") ?? @"C:\\Windows\\System32\\cmd.exe";
|
||||
var cliRunner = new CliRunner(false);
|
||||
var info = new CliTaskInfo { Name = "missing-dependency-simulation" };
|
||||
|
||||
JsonDataFromPoseidon result = await cliRunner.RunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
||||
cmdExe,
|
||||
"/d /c \"echo Could not load file or assembly 'log4net' 1>&2 & exit /b 17\"",
|
||||
info);
|
||||
|
||||
Assert.IsNull(result, "A failed CLI process must not create a default zero reading.");
|
||||
Assert.AreEqual(17, info.ExitCode);
|
||||
StringAssert.Contains(info.FailureReason, "exit code 17");
|
||||
StringAssert.Contains(info.StandardError, "log4net");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
|
||||
|
||||
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
[TestClass]
|
||||
[TestSubject(typeof(CliRunner))]
|
||||
public class CliRunnerTimeoutUnitTest
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task CancelUndoneTasksAsTimedOut_ShouldMarkOnlyRunningTasks()
|
||||
{
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
var completedTask = Task.FromResult("done");
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var runningTask = Task.Delay(TimeSpan.FromSeconds(30), cts.Token);
|
||||
|
||||
cliRunner.AddTaskForTest(completedTask, "completed");
|
||||
cliRunner.AddTaskForTest(runningTask, "running", cts);
|
||||
|
||||
await Task.Delay(50);
|
||||
|
||||
cliRunner.RefreshTaskStates();
|
||||
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
Assert.AreEqual(2, cliRunner.TaskPool.Count);
|
||||
|
||||
var completed = cliRunner.TaskPool.First(t => t.Name == "completed");
|
||||
var timedOut = cliRunner.TaskPool.First(t => t.Name == "running");
|
||||
|
||||
Assert.AreEqual(CliTaskState.Completed, completed.State);
|
||||
Assert.IsTrue(completed.UseResult);
|
||||
|
||||
Assert.AreEqual(CliTaskState.TimedOut, timedOut.State);
|
||||
Assert.IsFalse(timedOut.UseResult);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TimeOutReceived_ShouldReturnTrue_WhenElapsedExceedsTimeout()
|
||||
{
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
Thread.Sleep(30);
|
||||
|
||||
Assert.IsTrue(cliRunner.TimeOutReceived(1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task AreTasksDone_ShouldReturnTrue_WhenTimedOutTasksAreMarked()
|
||||
{
|
||||
var cliRunner = new CliRunner(false);
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var runningTask = Task.Delay(TimeSpan.FromSeconds(30), cts.Token);
|
||||
|
||||
cliRunner.AddTaskForTest(runningTask, "running", cts);
|
||||
|
||||
await Task.Delay(50);
|
||||
|
||||
Assert.IsFalse(cliRunner.AreTasksDone());
|
||||
|
||||
cliRunner.CancelUndoneTasksAsTimedOut();
|
||||
cliRunner.RefreshTaskStates();
|
||||
|
||||
Assert.IsTrue(cliRunner.AreTasksDone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
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.txt (2026-09-01).
|
||||
/// They are non-zero responses returned by HatCliDemo for COM43 and COM45.
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class PoseidonCustomerCliResponseTest
|
||||
{
|
||||
// The expected fixtures were calculated independently. A 0.1 ml
|
||||
// tolerance covers insignificant IEEE-754 conversion differences while
|
||||
// still detecting any meaningful incorrect transfer to the dialog.
|
||||
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()
|
||||
{
|
||||
// The first pair is a valid START dialog result; neither value is zero.
|
||||
AssertDialogValue(Com43InitialResponse, "1000000219", "002780.99", 10527.1923171862);
|
||||
AssertDialogValue(Com45InitialResponse, "1000000279", "002316.63", 8769.39850116792);
|
||||
|
||||
// The later pair must be usable by a new START or END phase, rather
|
||||
// than TBF retaining the initial START values or displaying zero.
|
||||
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);
|
||||
Assert.IsTrue(response.NfcTagDetected == true);
|
||||
Assert.IsTrue(response.ReadingComplete == true);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
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
|
||||
{
|
||||
// Exact non-zero reading shape returned by HatCliDemo in the PT50 customer log.
|
||||
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);
|
||||
Assert.AreNotEqual(0d, cliValueLitres);
|
||||
|
||||
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);
|
||||
|
||||
TextBox textBox = FindTextBox(dialog, "startTextBox1");
|
||||
Assert.AreEqual(dialog.WMStartStateStr[0], textBox.Text, "CLI value was not prefilled into the Start dialog.");
|
||||
|
||||
ConfirmDialog(dialog);
|
||||
Assert.IsTrue(dialog.Completed);
|
||||
Assert.AreEqual(expectedValue, dialog.WMStartState[0], 0.000001, "Start dialog did not parse the confirmed value.");
|
||||
|
||||
var entryForm = CreateEntryFormForTransfer(EntryForm.CurrentOp.ReadDatastream_StartStates);
|
||||
TransferAcceptedDialogValues(entryForm, dialog);
|
||||
Assert.AreEqual(expectedValue, entryForm.WMStartState(0), 0.000001,
|
||||
"EntryForm did not receive the Start value confirmed in the dialog.");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
TextBox textBox = FindTextBox(dialog, "endTextBox1");
|
||||
Assert.AreEqual(expectedValue.ToString(), textBox.Text, "CLI value was not prefilled into the End dialog.");
|
||||
|
||||
ConfirmDialog(dialog);
|
||||
Assert.IsTrue(dialog.Completed);
|
||||
Assert.AreEqual(expectedValue, dialog.WMEndState[0], 0.000001, "End dialog did not parse the confirmed value.");
|
||||
|
||||
var entryForm = CreateEntryFormForTransfer(EntryForm.CurrentOp.ReadDatastream_EndStates);
|
||||
TransferAcceptedDialogValues(entryForm, dialog);
|
||||
Assert.AreEqual(expectedValue, entryForm.WMEndState(0), 0.000001,
|
||||
"EntryForm did not receive the End value confirmed in the dialog.");
|
||||
}
|
||||
}
|
||||
|
||||
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 transferMethod = typeof(EntryForm).GetMethod("StoreAcceptedDialogValues",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.IsNotNull(transferMethod);
|
||||
transferMethod.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig;
|
||||
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
|
||||
using CmdPoseidonReader = TBF.Rig.RegisterReaders.PoseidonCmdStartStop.PoseidonReader;
|
||||
|
||||
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
[TestClass]
|
||||
[TestSubject(typeof(PoseidonReadCycle))]
|
||||
public class PoseidonReadCycleTest
|
||||
{
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
private sealed class FakePoseidonReader : IPoseidonReadOperation
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public CmdPoseidonReader.CurrentPoseidonOp CurrentOp { get; private set; }
|
||||
public int StartCount { get; private set; }
|
||||
public int RunCount { get; private set; }
|
||||
private readonly int runningIterationsBeforeDone;
|
||||
private int runningIterations;
|
||||
|
||||
public FakePoseidonReader(string name, int runningIterationsBeforeDone = 1)
|
||||
{
|
||||
Name = name;
|
||||
this.runningIterationsBeforeDone = runningIterationsBeforeDone;
|
||||
}
|
||||
|
||||
public bool IsNotStarted { get { return CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.None; } }
|
||||
public bool IsFinished
|
||||
{
|
||||
get
|
||||
{
|
||||
return CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.Done ||
|
||||
CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.Error;
|
||||
}
|
||||
}
|
||||
public bool HasError { get { return CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.Error; } }
|
||||
|
||||
public void Start(bool readStart)
|
||||
{
|
||||
StartCount++;
|
||||
CurrentOp = readStart
|
||||
? CmdPoseidonReader.CurrentPoseidonOp.ReadDataStream_Start
|
||||
: CmdPoseidonReader.CurrentPoseidonOp.ReadDataStream_End;
|
||||
}
|
||||
|
||||
public Event Run()
|
||||
{
|
||||
RunCount++;
|
||||
if (CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.ReadDataStream_Start ||
|
||||
CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.ReadDataStream_End)
|
||||
{
|
||||
CurrentOp = CmdPoseidonReader.CurrentPoseidonOp.ReadDatastream_Running;
|
||||
return Event.Busy;
|
||||
}
|
||||
|
||||
if (CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.ReadDatastream_Running)
|
||||
{
|
||||
runningIterations++;
|
||||
if (runningIterations >= runningIterationsBeforeDone)
|
||||
{
|
||||
CurrentOp = CmdPoseidonReader.CurrentPoseidonOp.Done;
|
||||
return Event.Done;
|
||||
}
|
||||
|
||||
return Event.Busy;
|
||||
}
|
||||
|
||||
return Event.None;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RunIteration_FullBench_DoesNotRestartCompletedReaders()
|
||||
{
|
||||
var readers = new List<IPoseidonReadOperation>();
|
||||
for (int i = 1; i <= 48; i++)
|
||||
readers.Add(new FakePoseidonReader("PoseidonPos" + i, 1 + (i % 3)));
|
||||
|
||||
TestContext.WriteLine("PoseidonReadCycle | scenario=FullBench | phase=Start | readers=48");
|
||||
Assert.IsFalse(RunAndLogCycleIteration("FullBench", 1, readers, true));
|
||||
Assert.IsFalse(RunAndLogCycleIteration("FullBench", 2, readers, true));
|
||||
Assert.IsFalse(RunAndLogCycleIteration("FullBench", 3, readers, true));
|
||||
Assert.IsTrue(RunAndLogCycleIteration("FullBench", 4, readers, true));
|
||||
|
||||
foreach (FakePoseidonReader reader in readers)
|
||||
{
|
||||
Assert.AreEqual(1, reader.StartCount, reader.Name + " must be armed exactly once.");
|
||||
Assert.AreEqual(CmdPoseidonReader.CurrentPoseidonOp.Done, reader.CurrentOp);
|
||||
}
|
||||
LogReaderSummary("FullBench final", readers);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RunIteration_ReducedBench_DoesNotRestartCompletedReaders()
|
||||
{
|
||||
var readers = new List<IPoseidonReadOperation>
|
||||
{
|
||||
new FakePoseidonReader("PoseidonPos1", 1),
|
||||
new FakePoseidonReader("PoseidonPos2", 3)
|
||||
};
|
||||
|
||||
TestContext.WriteLine("PoseidonReadCycle | scenario=ReducedBench | phase=End | readers=2");
|
||||
Assert.IsFalse(RunAndLogCycleIteration("ReducedBench", 1, readers, false));
|
||||
// Pos1 is now Done while Pos2 is still running. This is the state
|
||||
// which used to relaunch Pos1's CLI process over and over.
|
||||
Assert.IsFalse(RunAndLogCycleIteration("ReducedBench", 2, readers, false));
|
||||
Assert.AreEqual(1, ((FakePoseidonReader)readers[0]).StartCount);
|
||||
Assert.IsFalse(RunAndLogCycleIteration("ReducedBench", 3, readers, false));
|
||||
Assert.IsTrue(RunAndLogCycleIteration("ReducedBench", 4, readers, false));
|
||||
|
||||
foreach (FakePoseidonReader reader in readers)
|
||||
{
|
||||
Assert.AreEqual(1, reader.StartCount, reader.Name + " must be armed exactly once.");
|
||||
}
|
||||
LogReaderSummary("ReducedBench final", readers);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StartThenEnd_PhasesRearmCompletedReadersExactlyOncePerPhase()
|
||||
{
|
||||
var reader = new FakePoseidonReader("PoseidonPos1", 1);
|
||||
var readers = new List<IPoseidonReadOperation> { reader };
|
||||
var startPhase = new PoseidonReadPhaseRunner(true);
|
||||
|
||||
TestContext.WriteLine("PoseidonReadCycle | scenario=StartThenEnd | phase=Start | readers=1");
|
||||
Assert.IsFalse(RunAndLogPhaseIteration("StartThenEnd", "Start", 1, startPhase, readers));
|
||||
Assert.IsTrue(RunAndLogPhaseIteration("StartThenEnd", "Start", 2, startPhase, readers));
|
||||
Assert.AreEqual(1, reader.StartCount);
|
||||
Assert.AreEqual(CmdPoseidonReader.CurrentPoseidonOp.Done, reader.CurrentOp);
|
||||
|
||||
var endPhase = new PoseidonReadPhaseRunner(false);
|
||||
TestContext.WriteLine("PoseidonReadCycle | scenario=StartThenEnd | phase=End | rearming reader completed by Start");
|
||||
Assert.IsFalse(RunAndLogPhaseIteration("StartThenEnd", "End", 1, endPhase, readers));
|
||||
Assert.IsTrue(RunAndLogPhaseIteration("StartThenEnd", "End", 2, endPhase, readers));
|
||||
|
||||
Assert.AreEqual(2, reader.StartCount,
|
||||
"A completed START read must be rearmed once for the END phase.");
|
||||
Assert.AreEqual(CmdPoseidonReader.CurrentPoseidonOp.Done, reader.CurrentOp);
|
||||
LogReaderSummary("StartThenEnd final", readers);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void InputJson_ZeroReading_WithDecimalCommaOrDot_IsDeserializedAndParsedCorrectly()
|
||||
{
|
||||
TestContext.WriteLine("Poseidon JSON parsing | verifies zero is retained for both decimal separators.");
|
||||
AssertInputJsonReading("00000,0", 0.0);
|
||||
AssertInputJsonReading("00000.0", 0.0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void InputJson_NonZeroReading_WithDecimalCommaOrDot_IsDeserializedAndParsedCorrectly()
|
||||
{
|
||||
TestContext.WriteLine("Poseidon JSON parsing | verifies non-zero readings for both decimal separators.");
|
||||
AssertInputJsonReading("002433,50", 2433.50);
|
||||
AssertInputJsonReading("002433.50", 2433.50);
|
||||
AssertInputJsonReading("002898,60", 2898.60);
|
||||
AssertInputJsonReading("002898.60", 2898.60);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CliReadResponse_ZeroReading_IsAcceptedOnlyWhenNfcAndReadingAreComplete()
|
||||
{
|
||||
var validZero = new JsonDataFromPoseidon
|
||||
{
|
||||
NfcTagDetected = true,
|
||||
ReadingComplete = true,
|
||||
Reading = "00000.0"
|
||||
};
|
||||
string failureReason;
|
||||
|
||||
Assert.IsTrue(CmdPoseidonReader.TryValidateCliReadResponse(validZero, out failureReason));
|
||||
Assert.IsNull(failureReason);
|
||||
TestContext.WriteLine("Poseidon CLI validation | zero reading accepted when NFC=true and ReadingComplete=true.");
|
||||
|
||||
validZero.ReadingComplete = false;
|
||||
Assert.IsFalse(CmdPoseidonReader.TryValidateCliReadResponse(validZero, out failureReason));
|
||||
Assert.AreEqual("ReadingComplete=false.", failureReason);
|
||||
TestContext.WriteLine("Poseidon CLI validation | rejected response: " + failureReason);
|
||||
|
||||
validZero.ReadingComplete = true;
|
||||
validZero.NfcTagDetected = false;
|
||||
Assert.IsFalse(CmdPoseidonReader.TryValidateCliReadResponse(validZero, out failureReason));
|
||||
Assert.AreEqual("NfcTagDetected=false.", failureReason);
|
||||
TestContext.WriteLine("Poseidon CLI validation | rejected response: " + failureReason);
|
||||
}
|
||||
|
||||
private void AssertInputJsonReading(string reading, double expectedValue)
|
||||
{
|
||||
string json = "{\"NfcTagDetected\":true,\"ReadingComplete\":true," +
|
||||
"\"DeviceId\":\"1000000322\",\"Reading\":\"" + reading + "\"}";
|
||||
var cliRunner = new CliRunner(false);
|
||||
JsonDataFromPoseidon data;
|
||||
|
||||
bool deserialized = cliRunner.TryJsonStringDeserialize<JsonDataFromPoseidon>(json, out data);
|
||||
Assert.IsTrue(deserialized, "CLI JSON must be deserialized for Reading='" + reading + "'.");
|
||||
Assert.IsNotNull(data);
|
||||
Assert.IsTrue(data.NfcTagDetected);
|
||||
Assert.IsTrue(data.ReadingComplete);
|
||||
Assert.AreEqual("1000000322", data.DeviceId);
|
||||
Assert.AreEqual(reading, data.Reading);
|
||||
|
||||
double parsedReading;
|
||||
bool parsed = CmdPoseidonReader.TryParseCliReading(data.Reading, out parsedReading);
|
||||
Assert.IsTrue(parsed, "CLI Reading must be parsed for '" + reading + "'.");
|
||||
Assert.AreEqual(expectedValue, parsedReading, 0.000001);
|
||||
TestContext.WriteLine(string.Format(
|
||||
"Poseidon JSON parsing | rawReading='{0}' | parsed={1} | expected={2} | deviceId={3}",
|
||||
reading, parsedReading, expectedValue, data.DeviceId));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SimulationMode_AlwaysUsesCmdSleepTestInsteadOfConfiguredHatCli()
|
||||
{
|
||||
string simulatedCli = CmdPoseidonReader.GetCliFileNameForMode(
|
||||
Common.DebugMode.Simulate, "HatCliDemo.exe");
|
||||
string normalCli = CmdPoseidonReader.GetCliFileNameForMode(
|
||||
Common.DebugMode.Normal, "HatCliDemo.exe");
|
||||
|
||||
Assert.AreEqual("cmdSleepTest.exe", simulatedCli);
|
||||
Assert.AreEqual("HatCliDemo.exe", normalCli);
|
||||
TestContext.WriteLine(
|
||||
"Poseidon simulation CLI | mode=Simulate | configured=HatCliDemo.exe | selected=" + simulatedCli);
|
||||
TestContext.WriteLine(
|
||||
"Poseidon simulation CLI | mode=Normal | configured=HatCliDemo.exe | selected=" + normalCli);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[TestCategory("Integration")]
|
||||
public void CmdSleepTest_OneReader_CompletesAndStoresStartValue()
|
||||
{
|
||||
const string cliFile = "cmdSleepTest.exe";
|
||||
string cliPath = Path.Combine(SerialPortData.CliDirectory, cliFile);
|
||||
if (!File.Exists(cliPath))
|
||||
Assert.Inconclusive("cmdSleepTest.exe is not installed in " + SerialPortData.CliDirectory);
|
||||
|
||||
CmdPoseidonReader reader = CreateReader("PoseidonPos1", 3, cliFile);
|
||||
reader.SetCurrentOp(CmdPoseidonReader.CurrentPoseidonOp.ReadDataStream_Start);
|
||||
TestContext.WriteLine("Poseidon cmdSleepTest | reader=PoseidonPos1 | phase=Start | cli=" + cliPath);
|
||||
|
||||
for (int iteration = 0; iteration < 300 && !IsTerminal(reader); iteration++)
|
||||
{
|
||||
reader.Run();
|
||||
if (iteration == 0 || iteration % 25 == 0 || IsTerminal(reader))
|
||||
TestContext.WriteLine(string.Format(
|
||||
"Poseidon cmdSleepTest | iteration={0} | state={1} | serial='{2}' | begin={3}",
|
||||
iteration + 1, reader.CurrentOp, reader.SerialNr, reader.BeginWMState));
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
Assert.AreEqual(CmdPoseidonReader.CurrentPoseidonOp.Done, reader.CurrentOp);
|
||||
Assert.IsFalse(string.IsNullOrEmpty(reader.SerialNr));
|
||||
Assert.IsTrue(reader.BeginWMState > 0, "The JSON reading from cmdSleepTest must be transferred to BeginWMState.");
|
||||
TestContext.WriteLine(string.Format(
|
||||
"Poseidon cmdSleepTest | completed | state={0} | serial='{1}' | begin={2}",
|
||||
reader.CurrentOp, reader.SerialNr, reader.BeginWMState));
|
||||
}
|
||||
|
||||
private bool RunAndLogCycleIteration(string scenario, int iteration,
|
||||
IList<IPoseidonReadOperation> readers, bool readStart)
|
||||
{
|
||||
bool completed = PoseidonReadCycle.RunIteration(readers, readStart);
|
||||
LogReaderSummary(scenario + " iteration=" + iteration + " phase=" + (readStart ? "Start" : "End") +
|
||||
" completed=" + completed, readers);
|
||||
return completed;
|
||||
}
|
||||
|
||||
private bool RunAndLogPhaseIteration(string scenario, string phase, int iteration,
|
||||
PoseidonReadPhaseRunner runner, IList<IPoseidonReadOperation> readers)
|
||||
{
|
||||
bool completed = runner.RunIteration(readers);
|
||||
LogReaderSummary(scenario + " iteration=" + iteration + " phase=" + phase +
|
||||
" completed=" + completed, readers);
|
||||
return completed;
|
||||
}
|
||||
|
||||
private void LogReaderSummary(string label, IEnumerable<IPoseidonReadOperation> readers)
|
||||
{
|
||||
var readerList = readers.ToList();
|
||||
int notStartedCount = readerList.Count(reader => reader.IsNotStarted);
|
||||
int errorCount = readerList.Count(reader => reader.HasError);
|
||||
int doneCount = readerList.Count(reader => reader.IsFinished && !reader.HasError);
|
||||
int runningCount = readerList.Count - notStartedCount - errorCount - doneCount;
|
||||
string stateCounts = string.Format("NotStarted={0}, Running={1}, Done={2}, Error={3}",
|
||||
notStartedCount, runningCount, doneCount, errorCount);
|
||||
string startCounts = string.Join(", ", readerList
|
||||
.OfType<FakePoseidonReader>()
|
||||
.GroupBy(reader => reader.StartCount)
|
||||
.OrderBy(group => group.Key)
|
||||
.Select(group => "starts" + group.Key + "=" + group.Count()));
|
||||
|
||||
string details = readerList.Count <= 10
|
||||
? string.Join("; ", readerList.OfType<FakePoseidonReader>().Select(reader =>
|
||||
string.Format("{0}[state={1}, starts={2}, runs={3}]",
|
||||
reader.Name, reader.CurrentOp, reader.StartCount, reader.RunCount)))
|
||||
: "details=omitted for full bench";
|
||||
|
||||
TestContext.WriteLine(string.Format(
|
||||
"PoseidonReadCycle | {0} | readerCount={1} | states={2} | {3} | {4}",
|
||||
label, readerList.Count, stateCounts, startCounts, details));
|
||||
}
|
||||
|
||||
internal static CmdPoseidonReader CreateReader(string name, int comPort, string cliFile)
|
||||
{
|
||||
var cfg = new PoseidonCfg(name, new Factory())
|
||||
{
|
||||
ComPortNr = comPort,
|
||||
CliFileName = cliFile,
|
||||
TimeOut = 10
|
||||
};
|
||||
var reader = new CmdPoseidonReader(cfg, null);
|
||||
reader.DebugLevel = Common.DebugMode.Normal;
|
||||
reader.Initialize();
|
||||
return reader;
|
||||
}
|
||||
|
||||
internal static bool IsTerminal(CmdPoseidonReader reader)
|
||||
{
|
||||
return reader.CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.Done ||
|
||||
reader.CurrentOp == CmdPoseidonReader.CurrentPoseidonOp.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
|
||||
using CmdPoseidonReader = TBF.Rig.RegisterReaders.PoseidonCmdStartStop.PoseidonReader;
|
||||
|
||||
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
@@ -41,30 +37,5 @@ namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
Assert.IsTrue(tryGetDeviceId);
|
||||
Assert.AreEqual("1000000267", strOut);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryParseCliReading_DecimalDot_IsIndependentOfWindowsCulture()
|
||||
{
|
||||
CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
|
||||
try
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("sk-SK");
|
||||
double value;
|
||||
|
||||
bool parsed = CmdPoseidonReader.TryParseCliReading("0014383.3", out value);
|
||||
|
||||
Assert.IsTrue(parsed);
|
||||
Assert.AreEqual(14383.3, value, 0.000001);
|
||||
|
||||
parsed = CmdPoseidonReader.TryParseCliReading("0014383,3", out value);
|
||||
|
||||
Assert.IsTrue(parsed);
|
||||
Assert.AreEqual(14383.3, value, 0.000001);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = originalCulture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.DataEntry.PoseidonCmd;
|
||||
using CmdPoseidonReader = TBF.Rig.RegisterReaders.PoseidonCmdStartStop.PoseidonReader;
|
||||
|
||||
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
||||
{
|
||||
/// <summary>
|
||||
/// Manual, opt-in test for a real Poseidon meter/test-hat. It is deliberately
|
||||
/// inconclusive unless TBF_POSEIDON_COM_PORT_X or _Y is configured. Do not
|
||||
/// use it while an ASIC meter is connected to the selected test hat.
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
[TestCategory("Integration")]
|
||||
public class PoseidonSingleMeterIntegrationTest
|
||||
{
|
||||
// Set exactly one of these to the real COM number after a Poseidon meter
|
||||
// is connected. Keep both values 0 while an ASIC meter is connected.
|
||||
private const int RealPoseidonComPortHat = 3;
|
||||
private const int RealPoseidonComPortOptho = 4;
|
||||
private const string RealPoseidonCliFile = "HatCliDemo.exe";
|
||||
|
||||
[TestMethod]
|
||||
[TestCategory("Manual")]
|
||||
public void RealMeter_ReadAndFillStartDialog()
|
||||
{
|
||||
int comPort = RealPoseidonComPortHat > 0
|
||||
? RealPoseidonComPortHat
|
||||
: RealPoseidonComPortOptho;
|
||||
if (comPort <= 0)
|
||||
{
|
||||
Assert.Inconclusive(
|
||||
"Set RealPoseidonComPortHat or RealPoseidonComPortOptho in this test class before running the manual integration test.");
|
||||
}
|
||||
|
||||
Exception failure = null;
|
||||
Thread testThread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
RunRealMeterScenario(comPort, RealPoseidonCliFile);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
failure = exception;
|
||||
}
|
||||
});
|
||||
testThread.SetApartmentState(ApartmentState.STA);
|
||||
testThread.IsBackground = true;
|
||||
testThread.Start();
|
||||
testThread.Join(TimeSpan.FromSeconds(75));
|
||||
|
||||
Assert.IsFalse(testThread.IsAlive, "The real-meter integration test did not finish within 75 seconds.");
|
||||
if (failure != null) throw failure;
|
||||
}
|
||||
|
||||
private static void RunRealMeterScenario(int comPort, string cliFile)
|
||||
{
|
||||
var reader = PoseidonReadCycleTest.CreateReader("PoseidonPos1", comPort, cliFile);
|
||||
reader.SetCurrentOp(CmdPoseidonReader.CurrentPoseidonOp.ReadDataStream_Start);
|
||||
|
||||
for (int iteration = 0; iteration < 3500 && !PoseidonReadCycleTest.IsTerminal(reader); iteration++)
|
||||
{
|
||||
reader.Run();
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
Assert.AreEqual(CmdPoseidonReader.CurrentPoseidonOp.Done, reader.CurrentOp);
|
||||
Assert.IsTrue(reader.LastCliReadingParsed,
|
||||
"CLI returned no parseable Reading. Check the Poseidon reader and CLI logs for the raw JSON response.");
|
||||
|
||||
TBF.Data.SetData(1, 1, 1);
|
||||
using (var dialog = new TestStartEndForm(1, new TBF.Rig.GenericDevices.IRegReader[] { reader }, new bool[1]))
|
||||
{
|
||||
dialog.CreateControl();
|
||||
dialog.WMStartState[0] = reader.BeginWMState;
|
||||
dialog.WMStartStateStr[0] = reader.BeginWMState.ToString();
|
||||
dialog.UpdateValues(true, true, reader.DeltaTime);
|
||||
|
||||
TextBox startTextBox = dialog.Controls.Find("startTextBox1", true).OfType<TextBox>().FirstOrDefault();
|
||||
Assert.IsNotNull(startTextBox, "The start-state text box must exist.");
|
||||
Assert.AreEqual(dialog.WMStartStateStr[0], startTextBox.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Common;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
|
||||
using IPerlCommunicationForm = TBF.Rig.TestMethods.iPerlCommunication.iPerlCommunicationFormTestMethod;
|
||||
using IPerlConfigStruct = TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.ConfigStruct;
|
||||
using IPerlHead = TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlHead;
|
||||
using IPerlHeadCfg = TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.IperlHeadCfg;
|
||||
using IPerlHeadFactory = TBF.Rig.TestMethods.iPerlCommunication.iPerlHead.Factory;
|
||||
using static Sensus.iPerl.NfcHandler.MCI_Protocol;
|
||||
|
||||
namespace TBFTests.Rig.TestMethods.iPerlCommunication
|
||||
{
|
||||
/// <summary>
|
||||
/// Manual, read-only integration test through the legacy iPerl communication form.
|
||||
/// It reads Configuration only; it never writes to the connected meter.
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
[TestCategory("Integration")]
|
||||
public class IPerlCommunicationFormIntegrationTest
|
||||
{
|
||||
// Set the COM port of the currently connected iPerl/ASIC test head.
|
||||
// Keep 0 to disable this manual test.
|
||||
private const int RealRfidComPort = 3;
|
||||
private const int RealOptoComPort = 0;
|
||||
private const CommunicationInterface RealCommunicationInterface = CommunicationInterface.RFID;
|
||||
|
||||
[TestMethod]
|
||||
[TestCategory("Manual")]
|
||||
public void ReadConfiguration_ThroughIPerlCommunicationForm()
|
||||
{
|
||||
if (RealRfidComPort <= 0)
|
||||
{
|
||||
Assert.Inconclusive(
|
||||
"Set RealRfidComPort in IPerlCommunicationFormIntegrationTest before running this manual test.");
|
||||
}
|
||||
|
||||
Exception failure = null;
|
||||
Thread testThread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ReadConfiguration();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
failure = exception;
|
||||
}
|
||||
});
|
||||
testThread.SetApartmentState(ApartmentState.STA);
|
||||
testThread.IsBackground = true;
|
||||
testThread.Start();
|
||||
testThread.Join(TimeSpan.FromSeconds(30));
|
||||
|
||||
Assert.IsFalse(testThread.IsAlive, "iPerl configuration read did not finish within 30 seconds.");
|
||||
if (failure != null) throw failure;
|
||||
}
|
||||
|
||||
private static void ReadConfiguration()
|
||||
{
|
||||
var headCfg = new IPerlHeadCfg(new IPerlHeadFactory())
|
||||
{
|
||||
Name = "iPerl1",
|
||||
RfidComPortNr = RealRfidComPort,
|
||||
OptoComPortNr = RealOptoComPort,
|
||||
CommunicationInterface = RealCommunicationInterface
|
||||
};
|
||||
var head = new IPerlHead(headCfg)
|
||||
{
|
||||
DebugLevel = DebugMode.Normal
|
||||
};
|
||||
head.StartSession();
|
||||
|
||||
// The form initializes the same CfgIPerl and request pipeline that
|
||||
// production iPerlCommunication uses. No form is shown to the user.
|
||||
using (var form = new IPerlCommunicationForm(true))
|
||||
{
|
||||
byte[] buffer;
|
||||
int result = IPerlCommunicationForm.ReadRequestPort(
|
||||
head,
|
||||
MessageID.Configuration,
|
||||
StructName.Configuration,
|
||||
0,
|
||||
IPerlConfigStruct.Length,
|
||||
out buffer);
|
||||
|
||||
Assert.AreEqual(0, result, "iPerlCommunicationForm could not read the meter Configuration.");
|
||||
Assert.IsNotNull(buffer);
|
||||
Assert.AreEqual(IPerlConfigStruct.Length, buffer.Length);
|
||||
|
||||
IPerlConfigStruct configuration = IPerlConfigStruct.FromByteArray(buffer);
|
||||
Assert.IsNotNull(configuration);
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(configuration.GetPcbNrString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using Common;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.RegisterReaders.PoseidonReader.implementations;
|
||||
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations;
|
||||
|
||||
namespace TBFTests.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
|
||||
{
|
||||
[TestClass]
|
||||
[TestSubject(typeof(PoseidonCorrections))]
|
||||
public class PoseidonCorrectionsTest
|
||||
{
|
||||
|
||||
[TestMethod]
|
||||
public void TryParseByDescription_test()
|
||||
{
|
||||
PoseidonImplHeadTestCtrl.Operations testOp = PoseidonImplHeadTestCtrl.Operations.ReadSerialNo;
|
||||
ValidateOperationParsing(testOp.ToDescription());
|
||||
testOp = PoseidonImplHeadTestCtrl.Operations.SetTestModeOn;
|
||||
ValidateOperationParsing(testOp.ToDescription());
|
||||
testOp = PoseidonImplHeadTestCtrl.Operations.SetTestModeOff;
|
||||
ValidateOperationParsing(testOp.ToDescription());
|
||||
|
||||
//negative test
|
||||
ValidateOperationParsing("khvcdh jkbhvf", false);
|
||||
}
|
||||
|
||||
private static void ValidateOperationParsing(string tag, bool expectedResult = true)
|
||||
{
|
||||
PoseidonImplHeadTestCtrl.Operations operation;
|
||||
if (!PoseidonCorrections.TryParseByDescription((string)tag, out operation))
|
||||
{
|
||||
Assert.IsFalse(expectedResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsTrue(operation.ToDescription() == tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,26 +104,16 @@
|
||||
<Compile Include="Rig\Network\Camera\RoiForFixedStartKeyence\RoiTest.cs" />
|
||||
<Compile Include="Rig\Output\FileWriters\Enhanced\WriterTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTimeoutUnitTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\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\TestMethods\iPerlCommunication\IPerlCommunicationFormIntegrationTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />
|
||||
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\PoseidonCorrectionsTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\NfcS5_DLL\NfcS5_DLL.csproj">
|
||||
<Project>{5954d496-caab-4f7a-bde2-bdc8f47dab19}</Project>
|
||||
<Name>NfcS5_DLL</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Common\Common.csproj">
|
||||
<Project>{c8939821-ba5c-4988-a3d0-bf53b74865c7}</Project>
|
||||
<Name>Common</Name>
|
||||
@@ -180,4 +170,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user