Compare commits

..
Author SHA1 Message Date
michal c8b5732046 Fix Genesis scheduling and synchronization for up to 10 workers
(cherry picked from commit 684513ee59)
2026-09-10 22:59:56 +02:00
michal 02de7c6c57 FIX GENESIS
Enhance Genesis configuration and validation mechanisms

- Introduce new test methods for testing Genesis properties, boundary validations, runtime behavior, and tooltips with culture-specific fallback.
- Expand configuration validation logic with enhanced error messaging and logging.
- Simplify Genesis property UI: remove legacy settings and refactor controls for robust behavior.
- Add detailed logging for runtime and configuration updates.
- Provide tooltips and localized support for Genesis properties in multiple languages (e.g., `de`, `sk`).
- Update project file to include new test files.
2026-09-10 21:33:55 +02:00
14 changed files with 522 additions and 548 deletions
+43 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -1776,4 +1776,46 @@
<data name="IperlCalibFactorNominalTooltip" xml:space="preserve"> <data name="IperlCalibFactorNominalTooltip" xml:space="preserve">
<value>Nominální surový kalibrační faktor odpovídající 100 %. Tato hodnota se používá pro výpočet položky „iPerl CalibFactor (%)“ v konfiguraci výsledků.</value> <value>Nominální surový kalibrační faktor odpovídající 100 %. Tato hodnota se používá pro výpočet položky „iPerl CalibFactor (%)“ v konfiguraci výsledků.</value>
</data> </data>
<data name="GenesisTooltip_nameTextBox" xml:space="preserve"><value>Name
Účel: Identifikuje komponentu, na kterou odkazují testy procedury.
Použití: Vyhledání komponenty a výběr Method v proceduře.
Rozsah hodnot: Jedinečný název komponenty.
Výchozí hodnota: SmartCommunicationGenesis
Uplatnění: Po přejmenování je nutný restart; zkontrolujte odkazy v procedurách.</value></data>
<data name="GenesisTooltip_commTimeoutTextBox" xml:space="preserve"><value>CommTimeout
Účel: Časový limit pro požadavek na zrušení jednotlivého Genesis GCI volání.
Použití: Volání GciBridge z GenesisCommunicationForm (připojení, login, registry a související slot operace).
Rozsah hodnot: Celé číslo: 5005000 ms.
Výchozí hodnota: 1800 ms.
Uplatnění: Načte se na začátku aktivity vodoměru. Každé volání/pokus má vlastní limit; více kroků může trvat déle. Zrušení musí podporovat spodní vrstva ovladače. Ostatní uživatelé bridge zachovají původní limity.</value></data>
<data name="GenesisTooltip_maxCommRetriesTextBox" xml:space="preserve"><value>MaxCommRetries
Účel: Uložené nastavení opakování komunikace.
Použití: Aktuální tok Genesis slot/GCI jej nečte; nespouští opakování neúspěšné operace.
Rozsah hodnot: Celé číslo: 110.
Výchozí hodnota: 4
Uplatnění: Zachováno pro kompatibilitu; bez vlivu na opakování slot/GCI.</value></data>
<data name="GenesisTooltip_delayBetweenRetriesTextBox" xml:space="preserve"><value>DelayBetweenRetries
Účel: Uložená prodleva mezi opakováními komunikace.
Použití: Aktuální tok Genesis slot/GCI ji nečte; neřídí zpoždění zavření formuláře.
Rozsah hodnot: Celé číslo: 05000 ms.
Výchozí hodnota: 0 ms
Uplatnění: Zachováno pro kompatibilitu; bez vlivu na prodlevy slot/GCI.</value></data>
<data name="GenesisTooltip_nrThreadsTextBox" xml:space="preserve"><value>NrThreads
Účel: Počet pracovních vláken pro nakonfigurované hlavy Genesis.
Použití: GenesisCommunicationForm: vytvoření vláken a rozdělení slotů. Nemění počet vodoměrů.
Rozsah hodnot: Celé číslo: 110.
Výchozí hodnota: 10
Uplatnění: Vyžaduje restart. Sestavení TURA_SPECIAL používají samostatné přidělování slotů.</value></data>
<data name="GenesisTooltip_iperlCheckErrorsToStopTextBox" xml:space="preserve"><value>IperlCheckErrorsToStop
Účel: Počet neúspěšných vodoměrů, při kterém se zobrazí výsledek kontroly.
Použití: GenesisCommunicationSeq: aktivita iPERL_check při sestavení s IPERL. Uživatel může pokračovat nebo přerušit; nejde o obecný limit chyb slotů.
Rozsah hodnot: Celé číslo: 140. Práh: počet neúspěšných &gt;= nastavení.
Výchozí hodnota: 10
Uplatnění: Použije se při následující příslušné kontrole; ne při běžných slot aktivitách.</value></data>
</root> </root>
+43 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -2238,4 +2238,46 @@
<data name="IperlCalibFactorNominalTooltip" xml:space="preserve"> <data name="IperlCalibFactorNominalTooltip" xml:space="preserve">
<value>Roher Kalibrierfaktor, der 100 % entspricht. Dieser Wert wird zur Berechnung von „iPerl CalibFactor (%)“ in der Ergebnis-Konfiguration verwendet.</value> <value>Roher Kalibrierfaktor, der 100 % entspricht. Dieser Wert wird zur Berechnung von „iPerl CalibFactor (%)“ in der Ergebnis-Konfiguration verwendet.</value>
</data> </data>
<data name="GenesisTooltip_nameTextBox" xml:space="preserve"><value>Name
Zweck: Identifiziert die Komponente, auf die die Tests der Prozedur verweisen.
Verwendung: Komponentensuche und Auswahl von Method in der Prozedur.
Wertebereich: Eindeutiger Komponentenname.
Standardwert: SmartCommunicationGenesis
Anwendung: Nach Umbenennung neu starten und Prozedurverweise prüfen.</value></data>
<data name="GenesisTooltip_commTimeoutTextBox" xml:space="preserve"><value>CommTimeout
Zweck: Frist für die Abbruchanforderung eines einzelnen Genesis-GCI-Aufrufs.
Verwendung: GciBridge-Aufrufe aus GenesisCommunicationForm (Verbindung, Login, Register und zugehörige Slot-Operationen).
Wertebereich: Ganzzahl: 5005000 ms.
Standardwert: 1800 ms.
Anwendung: Zu Beginn jeder Zähleraktivität gelesen. Jeder Aufruf/Versuch erhält eine eigene Frist; mehrere Schritte können länger dauern. Der Treiber muss den Abbruch unterstützen. Andere Bridge-Aufrufer behalten ihre bisherigen Zeitlimits.</value></data>
<data name="GenesisTooltip_maxCommRetriesTextBox" xml:space="preserve"><value>MaxCommRetries
Zweck: Gespeicherte Einstellung für Kommunikationswiederholungen.
Verwendung: Der aktuelle Genesis-Slot/GCI-Ablauf liest diesen Wert nicht; fehlgeschlagene Operationen werden dadurch nicht wiederholt.
Wertebereich: Ganzzahl: 110.
Standardwert: 4
Anwendung: Für Kompatibilität gespeichert; keine Wirkung auf Slot/GCI-Wiederholungen.</value></data>
<data name="GenesisTooltip_delayBetweenRetriesTextBox" xml:space="preserve"><value>DelayBetweenRetries
Zweck: Gespeicherte Pause zwischen Kommunikationswiederholungen.
Verwendung: Der aktuelle Genesis-Slot/GCI-Ablauf liest diesen Wert nicht; die Verzögerung beim Schließen des Formulars bleibt unverändert.
Wertebereich: Ganzzahl: 05000 ms.
Standardwert: 0 ms
Anwendung: Für Kompatibilität gespeichert; keine Wirkung auf Slot/GCI-Pausen.</value></data>
<data name="GenesisTooltip_nrThreadsTextBox" xml:space="preserve"><value>NrThreads
Zweck: Anzahl der Arbeitsthreads für konfigurierte Genesis-Köpfe.
Verwendung: GenesisCommunicationForm: Thread-Erstellung und Slot-Verteilung. Ändert nicht die Anzahl der Zähler.
Wertebereich: Ganzzahl: 110.
Standardwert: 10
Anwendung: Neustart erforderlich. TURA_SPECIAL-Builds verwenden eine separate Slot-Zuweisung.</value></data>
<data name="GenesisTooltip_iperlCheckErrorsToStopTextBox" xml:space="preserve"><value>IperlCheckErrorsToStop
Zweck: Anzahl fehlgeschlagener Zähler, ab der das Prüfergebnis angezeigt wird.
Verwendung: GenesisCommunicationSeq: Aktivität iPERL_check bei Builds mit IPERL. Der Benutzer kann fortfahren oder abbrechen; kein allgemeiner Grenzwert für Slot-Fehler.
Wertebereich: Ganzzahl: 140. Schwelle: Fehleranzahl &gt;= Einstellung.
Standardwert: 10
Anwendung: Gilt bei der nächsten entsprechenden Prüfung, nicht bei normalen Slot-Aktivitäten.</value></data>
</root> </root>
+43 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -2509,4 +2509,46 @@
<data name="IperlCalibFactorNominalTooltip" xml:space="preserve"> <data name="IperlCalibFactorNominalTooltip" xml:space="preserve">
<value>Raw calibration factor representing 100 %. This value is used to calculate 'iPerl CalibFactor (%)' in the results configuration.</value> <value>Raw calibration factor representing 100 %. This value is used to calculate 'iPerl CalibFactor (%)' in the results configuration.</value>
</data> </data>
<data name="GenesisTooltip_nameTextBox" xml:space="preserve"><value>Name
Purpose: Identifies the component referenced by procedure tests.
Used in: Component lookup and procedure Method selection.
Allowed values: Use a unique component name.
Default: SmartCommunicationGenesis.
Application: Restart required after renaming; review procedure references.</value></data>
<data name="GenesisTooltip_commTimeoutTextBox" xml:space="preserve"><value>CommTimeout
Purpose: Cancellation deadline for each Genesis GCI request.
Used in: GciBridge requests made by GenesisCommunicationForm (connect, login, registers and related slot operations).
Allowed values: Integer: 5005000 ms.
Default: 1800 ms.
Application: Read at the start of each meter activity. Each request/attempt gets its own deadline; a multi-step test can take longer. Cancellation is cooperative: the underlying driver must honor the token. Legacy callers outside this flow retain their original timeouts.</value></data>
<data name="GenesisTooltip_maxCommRetriesTextBox" xml:space="preserve"><value>MaxCommRetries
Purpose: Stored communication retry setting.
Used in: Not consumed by the current Genesis slot/GCI workflow; does not cause failed slot operations to retry.
Allowed values: Integer: 110.
Default: 4.
Application: Stored for compatibility; no slot/GCI retry effect.</value></data>
<data name="GenesisTooltip_delayBetweenRetriesTextBox" xml:space="preserve"><value>DelayBetweenRetries
Purpose: Stored delay between communication retries.
Used in: Not consumed by the current Genesis slot/GCI workflow; does not control the form closing delay.
Allowed values: Integer: 05000 ms.
Default: 0 ms.
Application: Stored for compatibility; no slot/GCI delay effect.</value></data>
<data name="GenesisTooltip_nrThreadsTextBox" xml:space="preserve"><value>NrThreads
Purpose: Number of worker threads for processing configured Genesis heads.
Used in: GenesisCommunicationForm: worker creation and slot distribution. Does not change the number of configured meters.
Allowed values: Integer: 110.
Default: 10.
Application: Restart required. TURA_SPECIAL builds use a separate slot scheduling branch.</value></data>
<data name="GenesisTooltip_iperlCheckErrorsToStopTextBox" xml:space="preserve"><value>IperlCheckErrorsToStop
Purpose: Number of failed meters that triggers a check-result prompt.
Used in: GenesisCommunicationSeq: iPERL_check activity, when compiled with IPERL. User may Continue or Abort. Not a general slot-error stop limit.
Allowed values: Integer: 140.
Default: 10. Threshold uses failed count &gt;= setting.
Application: Applied to the next applicable check; no effect on ordinary slot activities.</value></data>
</root> </root>
+43 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -274,4 +274,46 @@
<data name="IperlCalibFactorNominalTooltip" xml:space="preserve"> <data name="IperlCalibFactorNominalTooltip" xml:space="preserve">
<value>Nominálny surový kalibračný faktor zodpovedajúci 100 %. Táto hodnota sa používa na výpočet položky „iPerl CalibFactor (%)“ v konfigurácii výsledkov.</value> <value>Nominálny surový kalibračný faktor zodpovedajúci 100 %. Táto hodnota sa používa na výpočet položky „iPerl CalibFactor (%)“ v konfigurácii výsledkov.</value>
</data> </data>
<data name="GenesisTooltip_nameTextBox" xml:space="preserve"><value>Name
Účel: Identifikuje komponent, na ktorý odkazujú testy procedúry.
Použitie: Vyhľadanie komponentu a výber Method v procedúre.
Rozsah hodnôt: Jedinečný názov komponentu.
Prednastavená hodnota: SmartCommunicationGenesis
Uplatnenie: Po premenovaní je potrebný reštart; skontrolujte odkazy v procedúrach.</value></data>
<data name="GenesisTooltip_commTimeoutTextBox" xml:space="preserve"><value>CommTimeout
Účel: Časový limit pre požiadavku na zrušenie jednotlivého Genesis GCI volania.
Použitie: Volania GciBridge z GenesisCommunicationForm (pripojenie, login, registre a súvisiace slot operácie).
Rozsah hodnôt: Celé číslo: 5005000 ms.
Prednastavená hodnota: 1800 ms.
Uplatnenie: Načíta sa na začiatku aktivity vodomera. Každé volanie/pokus má vlastný limit; viac krokov môže trvať dlhšie. Zrušenie musí podporovať spodná vrstva ovládača. Ostatní používatelia bridge zachovajú pôvodné limity.</value></data>
<data name="GenesisTooltip_maxCommRetriesTextBox" xml:space="preserve"><value>MaxCommRetries
Účel: Uložené nastavenie opakovania komunikácie.
Použitie: Aktuálny tok Genesis slot/GCI ho nečíta; nespúšťa opakovanie neúspešnej operácie.
Rozsah hodnôt: Celé číslo: 110.
Prednastavená hodnota: 4
Uplatnenie: Zachované pre kompatibilitu; bez vplyvu na opakovanie slot/GCI.</value></data>
<data name="GenesisTooltip_delayBetweenRetriesTextBox" xml:space="preserve"><value>DelayBetweenRetries
Účel: Uložená pauza medzi opakovaniami komunikácie.
Použitie: Aktuálny tok Genesis slot/GCI ju nečíta; neriadi oneskorenie zatvorenia formulára.
Rozsah hodnôt: Celé číslo: 05000 ms.
Prednastavená hodnota: 0 ms
Uplatnenie: Zachované pre kompatibilitu; bez vplyvu na pauzy slot/GCI.</value></data>
<data name="GenesisTooltip_nrThreadsTextBox" xml:space="preserve"><value>NrThreads
Účel: Počet pracovných vlákien pre nakonfigurované hlavy Genesis.
Použitie: GenesisCommunicationForm: vytvorenie vlákien a rozdelenie slotov. Nemení počet vodomerov.
Rozsah hodnôt: Celé číslo: 110.
Prednastavená hodnota: 10
Uplatnenie: Vyžaduje reštart. Zostavenia TURA_SPECIAL používajú samostatné prideľovanie slotov.</value></data>
<data name="GenesisTooltip_iperlCheckErrorsToStopTextBox" xml:space="preserve"><value>IperlCheckErrorsToStop
Účel: Počet neúspešných vodomerov, pri ktorom sa zobrazí výsledok kontroly.
Použitie: GenesisCommunicationSeq: aktivita iPERL_check pri zostavení s IPERL. Používateľ môže pokračovať alebo prerušiť; nejde o všeobecný limit chýb slotov.
Rozsah hodnôt: Celé číslo: 140. Prah: počet neúspešných &gt;= nastavenie.
Prednastavená hodnota: 10
Uplatnenie: Použije sa pri nasledujúcej príslušnej kontrole; nie pri bežných slot aktivitách.</value></data>
</root> </root>
@@ -1,4 +1,4 @@
using CordonelPreadjustmentUi; using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary; using CordonelPreadjustmentUi.Processes.Itinerary;
using GenesisCordonelInterface.API; using GenesisCordonelInterface.API;
using GenesisCordonelInterface.Core; using GenesisCordonelInterface.Core;
@@ -37,6 +37,41 @@ using UdsWriterType = TBF.Rig.Output.DataStorage.UniDataStorageWriter.Writer;
namespace TBF.Rig.BridgeComponents.GciBridge namespace TBF.Rig.BridgeComponents.GciBridge
{ {
/// <summary>Per-async-flow Genesis request timeout; absent for legacy callers.</summary>
public static class GenesisRequestTimeout
{
private static readonly AsyncLocal<int?> current = new AsyncLocal<int?>();
private static readonly ILog timeoutLog = LogManager.GetLogger(typeof(GenesisRequestTimeout));
public static IDisposable Begin(int milliseconds)
{
if (milliseconds < 500 || milliseconds > 5000) throw new ArgumentOutOfRangeException(nameof(milliseconds));
var previous = current.Value;
current.Value = milliseconds;
return new Scope(previous);
}
public static CancellationTokenSource CreateDeadline(CancellationToken callerToken, string operation)
{
var milliseconds = current.Value;
if (!milliseconds.HasValue) return null;
var source = CancellationTokenSource.CreateLinkedTokenSource(callerToken);
timeoutLog.DebugFormat("Genesis CommTimeout applied: Operation={0}, TimeoutMs={1}", operation, milliseconds.Value);
source.Token.Register(() => timeoutLog.WarnFormat("Genesis request cancellation requested: Operation={0}, TimeoutMs={1}, CallerCancelled={2}", operation, milliseconds.Value, callerToken.IsCancellationRequested));
source.CancelAfter(milliseconds.Value);
return source;
}
private sealed class Scope : IDisposable
{
private readonly int? previous;
private bool disposed;
public Scope(int? previous) { this.previous = previous; }
public void Dispose() { if (!disposed) { current.Value = previous; disposed = true; } }
}
}
/// <summary> /// <summary>
/// TBF bridge component for integration with the sibling GCI project. /// TBF bridge component for integration with the sibling GCI project.
/// The component can be linked to UniDataStorage reader and writer components. /// The component can be linked to UniDataStorage reader and writer components.
@@ -288,6 +323,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
GciPublicModels.GciInitSlotRequest request, GciPublicModels.GciInitSlotRequest request,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "InitSlotAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (request == null) if (request == null)
@@ -369,6 +406,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
GciPublicModels.GciInitSlotRequest request, GciPublicModels.GciInitSlotRequest request,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "UpdateSlotAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (request == null) if (request == null)
@@ -447,6 +486,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slotId, int slotId,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "GetSlotAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slotId <= 0) if (slotId <= 0)
@@ -523,6 +564,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slot, int slot,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "CleanSlotAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slot <= 0) if (slot <= 0)
@@ -599,6 +642,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
public async Task<GciPublicModels.GciCleanAllSlotsResult> CleanAllSlotsAsync( public async Task<GciPublicModels.GciCleanAllSlotsResult> CleanAllSlotsAsync(
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "CleanAllSlotsAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
GciPublicModels.GciCleanAllSlotsResult result = GciPublicModels.GciCleanAllSlotsResult result =
@@ -671,6 +716,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slotId, int slotId,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "GetPcbIdAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slotId <= 0) if (slotId <= 0)
@@ -788,6 +835,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slot, int slot,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "ConnectAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slot <= 0) if (slot <= 0)
@@ -820,6 +869,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slotId, int slotId,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "LoginAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slotId <= 0) if (slotId <= 0)
@@ -891,6 +942,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slotId, int slotId,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "DisconnectAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slotId <= 0) if (slotId <= 0)
@@ -970,6 +1023,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
string password, string password,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "SetPasswordAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slotId <= 0) if (slotId <= 0)
@@ -1046,6 +1101,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
string registerName, string registerName,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "ReadRegisterAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slotId <= 0) if (slotId <= 0)
@@ -1136,6 +1193,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
bool refreshSystemState = false, bool refreshSystemState = false,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "WriteRegisterAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
if (slotId <= 0) if (slotId <= 0)
@@ -1239,6 +1298,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
string pcbId, string pcbId,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "GetPasswordAsync");
token = deadline == null ? token : deadline.Token;
EnsureReader(); EnsureReader();
if (string.IsNullOrWhiteSpace(pcbId)) if (string.IsNullOrWhiteSpace(pcbId))
@@ -1317,6 +1378,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int meterSize, int meterSize,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "GetPreAdjustmentCalibrationParamsAsync");
token = deadline == null ? token : deadline.Token;
EnsureReader(); EnsureReader();
if (meterSize < 0) if (meterSize < 0)
@@ -1504,6 +1567,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
/// <returns>Complete workflow result including all retry statistics and operation results.</returns> /// <returns>Complete workflow result including all retry statistics and operation results.</returns>
public async Task<GciFullLoginResult> ConnectFullPassLoginWithRetryAsync(int slotId, CancellationToken token = default) public async Task<GciFullLoginResult> ConnectFullPassLoginWithRetryAsync(int slotId, CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "ConnectFullPassLoginWithRetryAsync");
token = deadline == null ? token : deadline.Token;
EnsureExternalInterface(); EnsureExternalInterface();
EnsureReader(); EnsureReader();
@@ -1605,6 +1670,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
IEnumerable<GciPublicModels.MeterBatchDebugStatus> selectedSlots, IEnumerable<GciPublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "PreAdjustment_DetectAsync");
token = deadline == null ? token : deadline.Token;
const string operation = nameof(PreAdjustment_DetectAsync); const string operation = nameof(PreAdjustment_DetectAsync);
try try
@@ -1678,6 +1745,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slot, int slot,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "PreAdjustment_PreparationAsync");
token = deadline == null ? token : deadline.Token;
const string operation = nameof(PreAdjustment_PreparationAsync); const string operation = nameof(PreAdjustment_PreparationAsync);
try try
@@ -1758,6 +1827,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slot, int slot,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "PreAdjustment_AmplitudeTestAsync");
token = deadline == null ? token : deadline.Token;
const string operation = nameof(PreAdjustment_AmplitudeTestAsync); const string operation = nameof(PreAdjustment_AmplitudeTestAsync);
try try
@@ -1838,6 +1909,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slot, int slot,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "PreAdjustment_TemperatureCalibrationAsync");
token = deadline == null ? token : deadline.Token;
const string operation = nameof(PreAdjustment_TemperatureCalibrationAsync); const string operation = nameof(PreAdjustment_TemperatureCalibrationAsync);
try try
@@ -1945,6 +2018,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slot, int slot,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "PreAdjustment_OffsetTestAsync");
token = deadline == null ? token : deadline.Token;
const string operation = nameof(PreAdjustment_OffsetTestAsync); const string operation = nameof(PreAdjustment_OffsetTestAsync);
try try
@@ -2025,6 +2100,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
int slot, int slot,
CancellationToken token = default) CancellationToken token = default)
{ {
using var deadline = GenesisRequestTimeout.CreateDeadline(token, "PreAdjustment_CompletionAsync");
token = deadline == null ? token : deadline.Token;
const string operation = nameof(PreAdjustment_CompletionAsync); const string operation = nameof(PreAdjustment_CompletionAsync);
try try
@@ -1407,22 +1407,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication
} }
public string WriteQ3Calibration(TestMethodCfg cfg, Test test, WaterMeter wm) public string WriteQ3Calibration(TestMethodCfg cfg, Test test, WaterMeter wm)
{
return WriteQ3Calibration(cfg, test, wm, test);
}
/// <param name="test">The preceding Q3 measurement test whose result receives the factors.</param>
/// <param name="validationTest">The current Write Q3 Calibration activity; its error limits validate the factors.</param>
public string WriteQ3Calibration(TestMethodCfg cfg, Test test, WaterMeter wm, Test validationTest)
{ {
if (genesisHead != null && genesisHead.DebugLevel == DebugMode.Simulate) if (genesisHead != null && genesisHead.DebugLevel == DebugMode.Simulate)
{ {
if (test == null || wm == null) return "Missing Genesis meter or test"; if (test == null || wm == null) return "Missing Genesis meter or test";
double errorLimitLo; if (!genesisHead.CalculateQ3Calibration() || !genesisHead.Q3CalibValid)
double errorLimitHi;
GenesisSmartReader.TryGetEffectiveQ3CalibrationErrorLimits(validationTest == null ? 0 : validationTest.ErrLimLo,
validationTest == null ? 0 : validationTest.ErrLimHi, out errorLimitLo, out errorLimitHi);
if (!genesisHead.CalculateQ3CalibrationWithErrorLimits(errorLimitLo, errorLimitHi) || !genesisHead.Q3CalibValid)
return "Q3 simulation requires valid measurement data"; return "Q3 simulation requires valid measurement data";
var result = ProcessData.BatchRslts.GetTestRslt(test.Name, test.Part); var result = ProcessData.BatchRslts.GetTestRslt(test.Name, test.Part);
StoreCalibrationValuesResults(result, genesisHead, wm); StoreCalibrationValuesResults(result, genesisHead, wm);
@@ -1445,7 +1434,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication
return "Missing Test"; return "Missing Test";
} }
return Task.Run(() => WriteQ3Calibration_Async(genesisHead, cfg, test, wm, validationTest)) return Task.Run(() => WriteQ3Calibration_Async(genesisHead, cfg, test, wm))
.GetAwaiter() .GetAwaiter()
.GetResult(); .GetResult();
} }
@@ -1454,7 +1443,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication
private async Task<string> WriteQ3Calibration_Async(GenesisSmartReader genesisSmartReader, private async Task<string> WriteQ3Calibration_Async(GenesisSmartReader genesisSmartReader,
TestMethodCfg cfg, TestMethodCfg cfg,
Test test, WaterMeter wm, Test validationTest) Test test, WaterMeter wm)
{ {
try try
{ {
@@ -1500,16 +1489,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication
CancellationToken token = default; CancellationToken token = default;
Test effectiveValidationTest = validationTest ?? test; bool areInitialisedData = genesisSmartReader.CalculateQ3Calibration();
double errorLimitLo;
double errorLimitHi;
bool procedureLimitsConfigured = GenesisSmartReader.TryGetEffectiveQ3CalibrationErrorLimits(
effectiveValidationTest == null ? 0 : effectiveValidationTest.ErrLimLo,
effectiveValidationTest == null ? 0 : effectiveValidationTest.ErrLimHi,
out errorLimitLo, out errorLimitHi);
log.Info($"WriteQ3Calibration_Async( Slot: {genesisSmartReader.GetSlotNr}) - Q3 factor validation limits: {errorLimitLo} .. {errorLimitHi}% ({(procedureLimitsConfigured ? "Write Q3 Calibration activity" : "default")})");
bool areInitialisedData = genesisSmartReader.CalculateQ3CalibrationWithErrorLimits(errorLimitLo, errorLimitHi);
if (!areInitialisedData) if (!areInitialisedData)
{ {
log.Error("WriteQ3Calibration_Async() - CalculateQ3Calibration Initialised Data not valid"); log.Error("WriteQ3Calibration_Async() - CalculateQ3Calibration Initialised Data not valid");
@@ -1521,7 +1501,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication
if (!genesisSmartReader.Q3CalibValid) if (!genesisSmartReader.Q3CalibValid)
{ {
log.Error("WriteQ3Calibration_Async() - Q3Channel not valid"); log.Error("WriteQ3Calibration_Async() - Q3Channel not valid");
return $"Q3Channel not valid ({errorLimitLo} .. {errorLimitHi}%)"; return "Q3Channel not valid";
} }
//Get Activity Status //Get Activity Status
@@ -2076,4 +2056,4 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication
} }
} }
} }
@@ -3928,9 +3928,6 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
} }
#endregion #endregion
/// <summary>Compatibility limit used when the Write Q3 Calibration activity has no error limits configured.</summary>
public const double DefaultQ3CalibrationFactorErrorLimit = 5.0;
private double[] q3CalibInitial = {Double.NaN,Double.NaN,Double.NaN}; private double[] q3CalibInitial = {Double.NaN,Double.NaN,Double.NaN};
private bool[] isChQ3CalibValid = { false,false,false}; private bool[] isChQ3CalibValid = { false,false,false};
private double[] q3CalibCh = {Double.NaN,Double.NaN,Double.NaN}; private double[] q3CalibCh = {Double.NaN,Double.NaN,Double.NaN};
@@ -3982,17 +3979,6 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
public bool CalculateQ3Calibration() public bool CalculateQ3Calibration()
{
return CalculateQ3CalibrationWithErrorLimits(-DefaultQ3CalibrationFactorErrorLimit,
DefaultQ3CalibrationFactorErrorLimit);
}
/// <summary>
/// Calculates Q3 factors using the limits configured on the procedure activity that writes them.
/// A pair for which the high limit is not greater than the low limit (the normal unset 0 / 0
/// value) falls back to the historical +/- 5 % validation.
/// </summary>
public bool CalculateQ3CalibrationWithErrorLimits(double errorLimitLo, double errorLimitHi)
{ {
if (Double.IsNaN(refVolume) || Double.IsInfinity(refVolume) || refVolume <= 0 || Double.IsNaN(refTime) || Double.IsInfinity(refTime) || refTime <= 0) if (Double.IsNaN(refVolume) || Double.IsInfinity(refVolume) || refVolume <= 0 || Double.IsNaN(refTime) || Double.IsInfinity(refTime) || refTime <= 0)
{ {
@@ -4000,24 +3986,13 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
return false; return false;
} }
CalculateQ3Calibration(refVolume, refTime, errorLimitLo, errorLimitHi); CalculateQ3Calibration( refVolume, refTime);
return true; return true;
} }
public void CalculateQ3Calibration(double refVolume, double refTime) public void CalculateQ3Calibration(double refVolume, double refTime)
{ {
CalculateQ3Calibration(refVolume, refTime, -DefaultQ3CalibrationFactorErrorLimit, GetQ3Calibration(refVolume, refTime, q3CalibInitial, ref isChQ3CalibValid,ref q3DiffPercentageCalibCh, ref q3CalibCh);
DefaultQ3CalibrationFactorErrorLimit);
}
public void CalculateQ3Calibration(double refVolume, double refTime, double errorLimitLo, double errorLimitHi)
{
double effectiveErrorLimitLo;
double effectiveErrorLimitHi;
TryGetEffectiveQ3CalibrationErrorLimits(errorLimitLo, errorLimitHi,
out effectiveErrorLimitLo, out effectiveErrorLimitHi);
GetQ3Calibration(refVolume, refTime, q3CalibInitial, effectiveErrorLimitLo,
effectiveErrorLimitHi, ref isChQ3CalibValid, ref q3DiffPercentageCalibCh, ref q3CalibCh);
} }
public void GetQ3Calibration(double refVolume, double refTime, double[] initCalibFactor, ref bool[] isChQ3CalibValid, ref double[] q3CalibCh) public void GetQ3Calibration(double refVolume, double refTime, double[] initCalibFactor, ref bool[] isChQ3CalibValid, ref double[] q3CalibCh)
@@ -4027,36 +4002,6 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
} }
public void GetQ3Calibration(double refVolume, double refTime, double[] initCalibFactor, ref bool[] isChQ3CalibValid, ref double[] calibDiffPercent, ref double[] q3CalibCh) public void GetQ3Calibration(double refVolume, double refTime, double[] initCalibFactor, ref bool[] isChQ3CalibValid, ref double[] calibDiffPercent, ref double[] q3CalibCh)
{
GetQ3Calibration(refVolume, refTime, initCalibFactor,
-DefaultQ3CalibrationFactorErrorLimit, DefaultQ3CalibrationFactorErrorLimit,
ref isChQ3CalibValid, ref calibDiffPercent, ref q3CalibCh);
}
/// <summary>
/// Resolves the error limits used to validate a calculated Q3 factor. TBF stores an unset
/// limit pair as equal values (normally 0 / 0), so only an ordered finite pair is considered set.
/// </summary>
public static bool TryGetEffectiveQ3CalibrationErrorLimits(double errorLimitLo, double errorLimitHi,
out double effectiveErrorLimitLo, out double effectiveErrorLimitHi)
{
if (!Double.IsNaN(errorLimitLo) && !Double.IsInfinity(errorLimitLo) &&
!Double.IsNaN(errorLimitHi) && !Double.IsInfinity(errorLimitHi) &&
errorLimitHi > errorLimitLo)
{
effectiveErrorLimitLo = errorLimitLo;
effectiveErrorLimitHi = errorLimitHi;
return true;
}
effectiveErrorLimitLo = -DefaultQ3CalibrationFactorErrorLimit;
effectiveErrorLimitHi = DefaultQ3CalibrationFactorErrorLimit;
return false;
}
public void GetQ3Calibration(double refVolume, double refTime, double[] initCalibFactor,
double errorLimitLo, double errorLimitHi, ref bool[] isChQ3CalibValid,
ref double[] calibDiffPercent, ref double[] q3CalibCh)
{ {
log.Debug("=== Q3 CALIBRATION START ==="); log.Debug("=== Q3 CALIBRATION START ===");
@@ -4076,7 +4021,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
} }
} }
log.Debug($"Inputs: refVolume={refVolume}, refTime={refTime}, initCalibFactors={string.Join(",", initCalibFactor)}, errorLimits={errorLimitLo}..{errorLimitHi}%"); log.Debug($"Inputs: refVolume={refVolume}, refTime={refTime}, initCalibFactors={string.Join(",", initCalibFactor)}");
if (_rawStartEndByChannel == null) if (_rawStartEndByChannel == null)
{ {
@@ -4165,16 +4110,10 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
} }
q3CalibCh[iChannel] = (refVolume / recalculatedDeltaVolume) * initCalibFactor[iChannel]; q3CalibCh[iChannel] = (refVolume / recalculatedDeltaVolume) * initCalibFactor[iChannel];
// Procedure error limits are an ordered range, e.g. -2 .. +2, so validation double diffPercent = Math.Abs((initCalibFactor[iChannel] - q3CalibCh[iChannel] ) / initCalibFactor[iChannel]) * 100.0;
// must retain the direction of the factor change. Keep the persisted difference isChQ3CalibValid[iChannel] = diffPercent <= 5.0 && !double.IsNaN(q3CalibCh[iChannel]) && !double.IsInfinity(q3CalibCh[iChannel]) && q3CalibCh[iChannel] >= 1 && q3CalibCh[iChannel] <= ushort.MaxValue;
// absolute for compatibility with the existing calibration-result fields.
double signedDiffPercent = ((q3CalibCh[iChannel] - initCalibFactor[iChannel]) / initCalibFactor[iChannel]) * 100.0;
double diffPercent = Math.Abs(signedDiffPercent);
isChQ3CalibValid[iChannel] = signedDiffPercent >= errorLimitLo && signedDiffPercent <= errorLimitHi &&
!double.IsNaN(q3CalibCh[iChannel]) && !double.IsInfinity(q3CalibCh[iChannel]) &&
q3CalibCh[iChannel] >= 1 && q3CalibCh[iChannel] <= ushort.MaxValue;
calibDiffPercent[iChannel] = diffPercent; calibDiffPercent[iChannel] = diffPercent;
log.Debug($"Calculated Q3Calib Ch[{iChannel}] ={q3CalibCh[iChannel]} SignedDiffPercent={signedDiffPercent}% DiffPercent={diffPercent}% isValid[{isChQ3CalibValid[iChannel]}] IninitCalibFactor={initCalibFactor}"); log.Debug($"Calculated Q3Calib Ch[{iChannel}] ={q3CalibCh[iChannel]} DiffPercent={diffPercent}% isValid[{isChQ3CalibValid[iChannel]}] IninitCalibFactor={initCalibFactor}");
} }
log.Debug("=== Q3 CALIBRATION END ==="); log.Debug("=== Q3 CALIBRATION END ===");
@@ -1,4 +1,4 @@
using TBF.Rig.TestMethods.iPerlCommunication; using TBF.Rig.TestMethods.iPerlCommunication;
/// ///
/// Copyright (c) 2015-2023 Sensus Slovensko a.s. /// Copyright (c) 2015-2023 Sensus Slovensko a.s.
/// ///
@@ -765,6 +765,8 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
CommErr error; CommErr error;
string resultStr = string.Empty; string resultStr = string.Empty;
using var requestTimeout = TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.Begin(cfg.CommTimeout);
/// ///
/// RFID communication activity call /// RFID communication activity call
@@ -1088,9 +1090,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
BeforeTest = currentTest; BeforeTest = currentTest;
} }
// Keep the Q3 measurement result on the preceding test, but validate against the var gciFullLoginResult = iHead.OptoHeadTest.WriteQ3Calibration(cfg, BeforeTest, wm);
// limits configured on the current "Write Q3 Calibration Slot" activity.
var gciFullLoginResult = iHead.OptoHeadTest.WriteQ3Calibration(cfg, BeforeTest, wm, currentTest);
if (!string.IsNullOrEmpty(gciFullLoginResult) && if (!string.IsNullOrEmpty(gciFullLoginResult) &&
gciFullLoginResult.Equals(TBF.Rig.RegisterReaders.GenesisRegReader.communication.OptoHeadTest.ResultOk)) gciFullLoginResult.Equals(TBF.Rig.RegisterReaders.GenesisRegReader.communication.OptoHeadTest.ResultOk))
{ {
@@ -1,4 +1,4 @@
/// ///
/// Copyright (c) 2015-2022 Sensus Slovensko a.s. /// Copyright (c) 2015-2022 Sensus Slovensko a.s.
/// ///
@@ -58,6 +58,9 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
testMethodCfg.UseWebService = tmpCfg.UseWebService; testMethodCfg.UseWebService = tmpCfg.UseWebService;
testMethodCfg.BaseUrl = tmpCfg.BaseUrl; testMethodCfg.BaseUrl = tmpCfg.BaseUrl;
testMethodCfg.RelativeUrl = tmpCfg.RelativeUrl; testMethodCfg.RelativeUrl = tmpCfg.RelativeUrl;
log.InfoFormat("Genesis runtime configuration applied: Name={0}, CommTimeout={1}ms, MaxRetries={2}, DelayBetweenRetries={3}ms, ErrorsToStop={4}",
Name, testMethodCfg.CommTimeout, testMethodCfg.MaxCommRetries,
testMethodCfg.DelayBetweenRetries, testMethodCfg.IperlCheckErrorsToStop);
} }
} }
}; };
@@ -80,6 +83,12 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
: base(cfg) : base(cfg)
{ {
testMethodCfg = cfg as TestMethodCfg; testMethodCfg = cfg as TestMethodCfg;
if (testMethodCfg != null)
log.InfoFormat("Genesis component configuration loaded: Name={0}, CommTimeout={1}ms, MaxRetries={2}, DelayBetweenRetries={3}ms, NrThreads={4}, ErrorsToStop={5}",
testMethodCfg.Name, testMethodCfg.CommTimeout, testMethodCfg.MaxCommRetries,
testMethodCfg.DelayBetweenRetries, testMethodCfg.NrThreads, testMethodCfg.IperlCheckErrorsToStop);
else
log.Error("Genesis component received an incompatible or null configuration.");
CreateMilestonesAndConditions(); CreateMilestonesAndConditions();
} }
@@ -1,4 +1,4 @@
/// ///
/// Copyright (c) 2015-2020 Sensus Slovensko a.s. /// Copyright (c) 2015-2020 Sensus Slovensko a.s.
/// ///
@@ -12,15 +12,21 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
{ {
public partial class TestMethodCfgCtrl : Configs.ConfigCtrlUtils, IComponentCfgCtrl public partial class TestMethodCfgCtrl : Configs.ConfigCtrlUtils, IComponentCfgCtrl
{ {
public bool ShowMore { get { return false; } } private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(TestMethodCfgCtrl));
iPerlCommunication.TestMethodCfg config; public bool ShowMore { get { return false; } }
TestMethodCfg config;
public IComponentCfg Config public IComponentCfg Config
{ {
get { return config as IComponentCfg; } get { return config as IComponentCfg; }
set set
{ {
config = value as iPerlCommunication.TestMethodCfg; config = value as TestMethodCfg;
if (config == null)
log.ErrorFormat("Genesis Properties configuration rejected: expected {0}, received {1}", typeof(TestMethodCfg).FullName, value == null ? "null" : value.GetType().FullName);
else
LogConfiguration("loaded");
Redraw(); Redraw();
} }
} }
@@ -28,6 +34,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
public TestMethodCfgCtrl() public TestMethodCfgCtrl()
{ {
InitializeComponent(); InitializeComponent();
InitializeSettingToolTips();
dfltQ2corrFactorsGroupBox.Visible = false; dfltQ2corrFactorsGroupBox.Visible = false;
useWebServiceGroupBox.Visible = false; useWebServiceGroupBox.Visible = false;
var factorHint = new System.Windows.Forms.Label var factorHint = new System.Windows.Forms.Label
@@ -50,7 +57,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
void Redraw() void Redraw()
{ {
if (config == null) return; /// Control was not loaded, settings were not changed if (config == null) return; /// Control was not loaded, settings were not changed
classNameLabel.Text = config.Factory.ClassName; classNameLabel.Text = config.Factory == null ? "TestMethods.GenesisCommunication" : config.Factory.ClassName;
nameTextBox.Text = config.Name; nameTextBox.Text = config.Name;
commTimeoutTextBox.Text = config.CommTimeout.ToString(); commTimeoutTextBox.Text = config.CommTimeout.ToString();
maxCommRetriesTextBox.Text = config.MaxCommRetries.ToString(); maxCommRetriesTextBox.Text = config.MaxCommRetries.ToString();
@@ -58,22 +65,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
nrThreadsTextBox.Text = config.NrThreads.ToString(); nrThreadsTextBox.Text = config.NrThreads.ToString();
iperlCheckErrorsToStopTextBox.Text = config.IperlCheckErrorsToStop.ToString(); iperlCheckErrorsToStopTextBox.Text = config.IperlCheckErrorsToStop.ToString();
textBox15rl.Text = config.DfltQ2c_15_rl.ToString();
textBox15lr.Text = config.DfltQ2c_15_lr.ToString();
textBox20rl.Text = config.DfltQ2c_20_rl.ToString();
textBox20lr.Text = config.DfltQ2c_20_lr.ToString();
textBox25_63rl.Text = config.DfltQ2c_25_63_rl.ToString();
textBox25_63lr.Text = config.DfltQ2c_25_63_lr.ToString();
textBox25_10rl.Text = config.DfltQ2c_25_10_rl.ToString();
textBox25_10lr.Text = config.DfltQ2c_25_10_lr.ToString();
textBox32rl.Text = config.DfltQ2c_32_rl.ToString();
textBox32lr.Text = config.DfltQ2c_32_lr.ToString();
textBox40rl.Text = config.DfltQ2c_40_rl.ToString();
textBox40lr.Text = config.DfltQ2c_40_lr.ToString();
useWebServiceCheckBox.Checked = config.UseWebService;
baseUrlTextBox.Text = config.BaseUrl;
relativeUrlTextBox.Text = config.RelativeUrl;
} }
public void Unlock() public void Unlock()
@@ -85,28 +77,18 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
nrThreadsTextBox.Enabled = true; nrThreadsTextBox.Enabled = true;
iperlCheckErrorsToStopTextBox.Enabled = true; iperlCheckErrorsToStopTextBox.Enabled = true;
textBox15rl.Enabled = true;
textBox15lr.Enabled = true;
textBox20rl.Enabled = true;
textBox20lr.Enabled = true;
textBox25_63rl.Enabled = true;
textBox25_63lr.Enabled = true;
textBox25_10rl.Enabled = true;
textBox25_10lr.Enabled = true;
textBox32rl.Enabled = true;
textBox32lr.Enabled = true;
textBox40rl.Enabled = true;
textBox40lr.Enabled = true;
useWebServiceCheckBox.Enabled = true;
ManageCheckGroupBox(useWebServiceCheckBox, useWebServiceGroupBox);
baseUrlTextBox.Enabled = useWebServiceCheckBox.Enabled;
relativeUrlTextBox.Enabled = useWebServiceCheckBox.Enabled;
} }
public CfgUpdateFlags VerifyCfg(ref string message) public CfgUpdateFlags VerifyCfg(ref string message)
{ {
CfgUpdateFlags flags = CfgUpdateFlags.None; CfgUpdateFlags flags = CfgUpdateFlags.None;
if (config == null)
{
message += Environment.NewLine + "Genesis configuration is not loaded.";
log.Error("Genesis Properties validation failed: configuration is not loaded.");
return CfgUpdateFlags.Error;
}
int dummy; int dummy;
if (!int.TryParse(commTimeoutTextBox.Text, out dummy) || dummy < 500 || dummy > 5000) if (!int.TryParse(commTimeoutTextBox.Text, out dummy) || dummy < 500 || dummy > 5000)
@@ -122,79 +104,21 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
if (!int.TryParse(delayBetweenRetriesTextBox.Text, out dummy) || dummy < 0 || dummy > 5000) if (!int.TryParse(delayBetweenRetriesTextBox.Text, out dummy) || dummy < 0 || dummy > 5000)
{ {
flags |= CfgUpdateFlags.Error; flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Comm. timeout' should be in range 0 .. 5000"; message += Environment.NewLine + "'Delay between retries' should be in range 0 .. 5000";
} }
if (!int.TryParse(nrThreadsTextBox.Text, out dummy) || (dummy < 1 || dummy > 10)) if (!int.TryParse(nrThreadsTextBox.Text, out dummy) || (dummy < 1 || dummy > 10))
{ {
flags |= CfgUpdateFlags.Error; flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Nr. threads' should be in range 1 .. 10"; message += Environment.NewLine + "'Nr. threads' should be in range 1 .. 10";
} }
if (!int.TryParse(iperlCheckErrorsToStopTextBox.Text, out dummy) || ((dummy < 1) && (dummy > 40))) if (!int.TryParse(iperlCheckErrorsToStopTextBox.Text, out dummy) || ((dummy < 1) || (dummy > 40)))
{ {
flags |= CfgUpdateFlags.Error; flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + string.Format(Strings.Invalid_0, iperlCheckErrorsToStopLabel.Text); message += Environment.NewLine + string.Format(Strings.Invalid_0, iperlCheckErrorsToStopLabel.Text);
} }
if (!int.TryParse(textBox15rl.Text, out dummy) || dummy < -50 || dummy > 50) if ((flags & CfgUpdateFlags.Error) != 0)
{ log.WarnFormat("Genesis Properties validation failed: {0}", message);
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN15 RL should be in range -50 .. 50";
}
if (!int.TryParse(textBox15lr.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN15 LR should be in range -50 .. 50";
}
if (!int.TryParse(textBox20rl.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN20 RL should be in range -50 .. 50";
}
if (!int.TryParse(textBox20lr.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN20 LR should be in range -50 .. 50";
}
if (!int.TryParse(textBox25_63rl.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN25 Q3 6.3 RL should be in range -50 .. 50";
}
if (!int.TryParse(textBox25_63lr.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN25 Q3 6.3 LR should be in range -50 .. 50";
}
if (!int.TryParse(textBox25_10rl.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN25 Q3 10 RL should be in range -50 .. 50";
}
if (!int.TryParse(textBox25_10lr.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN25 Q3 10 LR should be in range -50 .. 50";
}
if (!int.TryParse(textBox32rl.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN32 RL should be in range -50 .. 50";
}
if (!int.TryParse(textBox32lr.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN32 LR should be in range -50 .. 50";
}
if (!int.TryParse(textBox40rl.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN40 RL should be in range -50 .. 50";
}
if (!int.TryParse(textBox40lr.Text, out dummy) || dummy < -50 || dummy > 50)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Default Q2 correction factor DN40 LR should be in range -50 .. 50";
}
return flags; return flags;
} }
@@ -203,7 +127,8 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
{ {
CfgUpdateFlags flags = CfgUpdateFlags.None; CfgUpdateFlags flags = CfgUpdateFlags.None;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed string message = string.Empty;
if ((VerifyCfg(ref message) & CfgUpdateFlags.Error) != 0) return CfgUpdateFlags.Error;
if (config.Name != nameTextBox.Text) if (config.Name != nameTextBox.Text)
{ {
@@ -211,26 +136,11 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
flags |= (CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd); flags |= (CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd);
} }
var CommTimeout = config.CommTimeout;; var CommTimeout = config.CommTimeout;
var MaxCommRetries = config.MaxCommRetries; var MaxCommRetries = config.MaxCommRetries;
var DelayBetweenRetries = config.DelayBetweenRetries; var DelayBetweenRetries = config.DelayBetweenRetries;
var NrThreads = config.NrThreads; var NrThreads = config.NrThreads;
var IperlCheckErrorsToStop = config.IperlCheckErrorsToStop; var IperlCheckErrorsToStop = config.IperlCheckErrorsToStop;
var DfltQ2c_15_rl = config.DfltQ2c_15_rl;
var DfltQ2c_15_lr = config.DfltQ2c_15_lr;
var DfltQ2c_20_rl = config.DfltQ2c_20_rl;
var DfltQ2c_20_lr = config.DfltQ2c_20_lr;
var DfltQ2c_25_63_rl = config.DfltQ2c_25_63_rl;
var DfltQ2c_25_63_lr = config.DfltQ2c_25_63_lr;
var DfltQ2c_25_10_rl = config.DfltQ2c_25_10_rl;
var DfltQ2c_25_10_lr = config.DfltQ2c_25_10_lr;
var DfltQ2c_32_rl = config.DfltQ2c_32_rl;
var DfltQ2c_32_lr = config.DfltQ2c_32_lr;
var DfltQ2c_40_rl = config.DfltQ2c_40_rl;
var DfltQ2c_40_lr = config.DfltQ2c_40_lr;
var UseWebService = config.UseWebService;
var BaseUrl = config.BaseUrl;
var RelativeUrl = config.RelativeUrl;
flags |= UpdateDifferent(ref CommTimeout, commTimeoutTextBox.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange); flags |= UpdateDifferent(ref CommTimeout, commTimeoutTextBox.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref MaxCommRetries, maxCommRetriesTextBox.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange); flags |= UpdateDifferent(ref MaxCommRetries, maxCommRetriesTextBox.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
@@ -238,22 +148,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
flags |= UpdateDifferent(ref NrThreads, nrThreadsTextBox.Text, CfgUpdateFlags.RestartRqrd | CfgUpdateFlags.AnyChange); flags |= UpdateDifferent(ref NrThreads, nrThreadsTextBox.Text, CfgUpdateFlags.RestartRqrd | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref IperlCheckErrorsToStop, iperlCheckErrorsToStopTextBox.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange); flags |= UpdateDifferent(ref IperlCheckErrorsToStop, iperlCheckErrorsToStopTextBox.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_15_rl, textBox15rl.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_15_lr, textBox15lr.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_20_rl, textBox20rl.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_20_lr, textBox20lr.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_25_63_rl, textBox25_63rl.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_25_63_lr, textBox25_63lr.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_25_10_rl, textBox25_10rl.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_25_10_lr, textBox25_10lr.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_32_rl, textBox32rl.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_32_lr, textBox32lr.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_40_rl, textBox40rl.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref DfltQ2c_40_lr, textBox40lr.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref UseWebService, useWebServiceCheckBox.Checked, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref BaseUrl, baseUrlTextBox.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
flags |= UpdateDifferent(ref RelativeUrl, relativeUrlTextBox.Text, CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.AnyChange);
config.CommTimeout = CommTimeout; config.CommTimeout = CommTimeout;
@@ -261,30 +156,44 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
config.DelayBetweenRetries = DelayBetweenRetries; config.DelayBetweenRetries = DelayBetweenRetries;
config.NrThreads = NrThreads; config.NrThreads = NrThreads;
config.IperlCheckErrorsToStop = IperlCheckErrorsToStop; config.IperlCheckErrorsToStop = IperlCheckErrorsToStop;
config.DfltQ2c_15_rl = DfltQ2c_15_rl;
config.DfltQ2c_15_lr = DfltQ2c_15_lr;
config.DfltQ2c_20_rl = DfltQ2c_20_rl;
config.DfltQ2c_20_lr = DfltQ2c_20_lr;
config.DfltQ2c_25_63_rl = DfltQ2c_25_63_rl;
config.DfltQ2c_25_63_lr = DfltQ2c_25_63_lr;
config.DfltQ2c_25_10_rl = DfltQ2c_25_10_rl;
config.DfltQ2c_25_10_lr = DfltQ2c_25_10_lr;
config.DfltQ2c_32_rl = DfltQ2c_32_rl;
config.DfltQ2c_32_lr = DfltQ2c_32_lr;
config.DfltQ2c_40_rl = DfltQ2c_40_rl;
config.DfltQ2c_40_lr = DfltQ2c_40_lr;
config.UseWebService = UseWebService;
config.BaseUrl = BaseUrl;
config.RelativeUrl = RelativeUrl;
LogConfiguration("updated; flags=" + flags);
if ((flags & CfgUpdateFlags.InvokeCfgChange) != 0) if ((flags & CfgUpdateFlags.InvokeCfgChange) != 0)
{ {
iPerlCommunication.TestMethod.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.CfgChange, config)); TestMethod.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.CfgChange, config));
} }
return flags; return flags;
} }
private ToolTip settingsToolTip;
private void InitializeSettingToolTips()
{
if (components == null) components = new System.ComponentModel.Container();
settingsToolTip = new ToolTip(components) { AutoPopDelay = 30000, InitialDelay = 400, ReshowDelay = 100, ShowAlways = true };
settingsToolTip.SetToolTip(nameTextBox, Strings.ResourceManager.GetString("GenesisTooltip_nameTextBox", Strings.Culture));
settingsToolTip.SetToolTip(nameLabel, settingsToolTip.GetToolTip(nameTextBox));
settingsToolTip.SetToolTip(commTimeoutTextBox, Strings.ResourceManager.GetString("GenesisTooltip_commTimeoutTextBox", Strings.Culture));
settingsToolTip.SetToolTip(commTimeoutLabel, settingsToolTip.GetToolTip(commTimeoutTextBox));
settingsToolTip.SetToolTip(maxCommRetriesTextBox, Strings.ResourceManager.GetString("GenesisTooltip_maxCommRetriesTextBox", Strings.Culture));
settingsToolTip.SetToolTip(maxNrRetriesLabel, settingsToolTip.GetToolTip(maxCommRetriesTextBox));
settingsToolTip.SetToolTip(delayBetweenRetriesTextBox, Strings.ResourceManager.GetString("GenesisTooltip_delayBetweenRetriesTextBox", Strings.Culture));
settingsToolTip.SetToolTip(delayBetweenRetriesLabel, settingsToolTip.GetToolTip(delayBetweenRetriesTextBox));
settingsToolTip.SetToolTip(nrThreadsTextBox, Strings.ResourceManager.GetString("GenesisTooltip_nrThreadsTextBox", Strings.Culture));
settingsToolTip.SetToolTip(nrThreadsLabel, settingsToolTip.GetToolTip(nrThreadsTextBox));
settingsToolTip.SetToolTip(iperlCheckErrorsToStopTextBox, Strings.ResourceManager.GetString("GenesisTooltip_iperlCheckErrorsToStopTextBox", Strings.Culture));
settingsToolTip.SetToolTip(iperlCheckErrorsToStopLabel, settingsToolTip.GetToolTip(iperlCheckErrorsToStopTextBox));
}
private void LogConfiguration(string action)
{
log.InfoFormat("Genesis Properties {0}: Name={1}, CommTimeout={2}ms, MaxRetries={3}, DelayBetweenRetries={4}ms, NrThreads={5}, ErrorsToStop={6}; Q3 source=WaterMeterData.Text1-Text3; legacy fields preserved",
action, config.Name, config.CommTimeout, config.MaxCommRetries,
config.DelayBetweenRetries, config.NrThreads, config.IperlCheckErrorsToStop);
}
private void ManageCheckGroupBox(CheckBox chk, GroupBox grp) private void ManageCheckGroupBox(CheckBox chk, GroupBox grp)
{ {
/// Make sure the CheckBox isn't in the GroupBox. This will only happen the first time. /// Make sure the CheckBox isn't in the GroupBox. This will only happen the first time.
+189
View File
@@ -165,6 +165,195 @@ namespace TBFTests
// ReSharper restore SqlResolve // ReSharper restore SqlResolve
[TestMethod]
public void GenesisPropertiesLoadUpdateAndPreserveLegacySettings()
{
var cfg = new TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfg(new TBF.Rig.TestMethods.GenesisCommunication.Factory());
cfg.Name = "Genesis test";
cfg.DfltQ2c_15_rl = 17969;
cfg.UseWebService = true;
cfg.BaseUrl = "legacy-base";
cfg.RelativeUrl = "legacy-path";
// Exercise the existing XML contract before opening Properties.
using (var writer = new StringWriter())
{
TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfg.Serializer.Serialize(writer, cfg);
using (var reader = new StringReader(writer.ToString()))
cfg = (TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfg)TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfg.Serializer.Deserialize(reader);
}
// Name is stored in the component entity, outside the XML payload.
cfg.Name = "Genesis test";
using (var ctrl = new TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfgCtrl())
{
ctrl.Config = cfg;
Assert.AreSame(cfg, ctrl.Config);
Assert.AreEqual("Genesis test", ctrl.Controls.Find("nameTextBox", true)[0].Text);
Assert.AreEqual("1800", ctrl.Controls.Find("commTimeoutTextBox", true)[0].Text);
string message = "";
Assert.AreEqual(Common.CfgUpdateFlags.None, ctrl.VerifyCfg(ref message), message);
ctrl.Unlock();
ctrl.Controls.Find("commTimeoutTextBox", true)[0].Text = "2300";
bool notified = false;
EventHandler<TBF.Rig.CfgChangeArgs> handler = (sender, args) => { notified = true; };
TBF.Rig.TestMethods.GenesisCommunication.TestMethod.CfgChangeHandler += handler;
try { ctrl.UpdateCfg(); }
finally { TBF.Rig.TestMethods.GenesisCommunication.TestMethod.CfgChangeHandler -= handler; }
Assert.IsTrue(notified);
Assert.AreEqual(2300, cfg.CommTimeout);
Assert.AreEqual(17969, cfg.DfltQ2c_15_rl);
Assert.IsTrue(cfg.UseWebService);
Assert.AreEqual("legacy-base", cfg.BaseUrl);
Assert.AreEqual("legacy-path", cfg.RelativeUrl);
ctrl.Controls.Find("iperlCheckErrorsToStopTextBox", true)[0].Text = "41";
ctrl.Controls.Find("commTimeoutTextBox", true)[0].Text = "2400";
Assert.AreNotEqual(0, (int)(ctrl.UpdateCfg() & Common.CfgUpdateFlags.Error));
Assert.AreEqual(2300, cfg.CommTimeout);
}
}
[TestMethod]
public void GenesisPropertiesValidateAllNumericBoundariesAndExposeTooltips()
{
var cfg = new TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfg(new TBF.Rig.TestMethods.GenesisCommunication.Factory());
using (var ctrl = new TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfgCtrl())
{
ctrl.Config = cfg;
ctrl.Unlock();
var tips = (System.Windows.Forms.ToolTip)typeof(TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfgCtrl)
.GetField("settingsToolTip", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(ctrl);
var names = new[] { "commTimeoutTextBox", "maxCommRetriesTextBox", "delayBetweenRetriesTextBox", "nrThreadsTextBox", "iperlCheckErrorsToStopTextBox" };
var minima = new[] { 500, 1, 0, 1, 1 };
var maxima = new[] { 5000, 10, 5000, 10, 40 };
for (int i = 0; i < names.Length; i++)
{
var field = ctrl.Controls.Find(names[i], true)[0];
string original = field.Text;
Assert.IsFalse(string.IsNullOrWhiteSpace(tips.GetToolTip(field)));
Assert.IsTrue(tips.GetToolTip(field).Split('\n').Length >= 6);
foreach (var value in new[] { minima[i], maxima[i] })
{
field.Text = value.ToString();
string message = "";
Assert.AreEqual(Common.CfgUpdateFlags.None, ctrl.VerifyCfg(ref message), names[i] + message);
}
foreach (var value in new[] { (minima[i] - 1).ToString(), (maxima[i] + 1).ToString(), "bad", "", "1.5" })
{
field.Text = value;
string message = "";
Assert.AreNotEqual(0, (int)(ctrl.VerifyCfg(ref message) & Common.CfgUpdateFlags.Error), names[i] + ":" + value);
}
field.Text = original;
}
ctrl.Controls.Find("commTimeoutTextBox", true)[0].Text = "2500";
ctrl.Controls.Find("maxCommRetriesTextBox", true)[0].Text = "6";
ctrl.Controls.Find("delayBetweenRetriesTextBox", true)[0].Text = "300";
ctrl.Controls.Find("nrThreadsTextBox", true)[0].Text = "3";
ctrl.Controls.Find("iperlCheckErrorsToStopTextBox", true)[0].Text = "2";
var flags = ctrl.UpdateCfg();
Assert.AreNotEqual(0, (int)(flags & Common.CfgUpdateFlags.RestartRqrd));
Assert.AreEqual(2500, cfg.CommTimeout);
Assert.AreEqual(6, cfg.MaxCommRetries);
Assert.AreEqual(300, cfg.DelayBetweenRetries);
Assert.AreEqual(3, cfg.NrThreads);
Assert.AreEqual(2, cfg.IperlCheckErrorsToStop);
StringAssert.Contains(tips.GetToolTip(ctrl.Controls.Find("commTimeoutTextBox", true)[0]), "Genesis");
}
}
[TestMethod]
public void GenesisRuntimeReceivesLiveSettingsButKeepsRestartOnlyThreadCount()
{
var factory = new TBF.Rig.TestMethods.GenesisCommunication.Factory();
var runtimeCfg = new TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfg(factory);
var editedCfg = new TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfg(factory);
var component = new TBF.Rig.TestMethods.GenesisCommunication.TestMethod(runtimeCfg);
var eventField = typeof(TBF.Rig.TestMethods.GenesisCommunication.TestMethod).GetField("CfgChangeHandler",
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
var originalHandlers = eventField.GetValue(null);
try
{
component.StartChangeHandler();
using (var ctrl = new TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfgCtrl())
{
ctrl.Config = editedCfg;
ctrl.Unlock();
ctrl.Controls.Find("commTimeoutTextBox", true)[0].Text = "2500";
ctrl.Controls.Find("maxCommRetriesTextBox", true)[0].Text = "6";
ctrl.Controls.Find("delayBetweenRetriesTextBox", true)[0].Text = "300";
ctrl.Controls.Find("iperlCheckErrorsToStopTextBox", true)[0].Text = "2";
ctrl.Controls.Find("nrThreadsTextBox", true)[0].Text = "3";
ctrl.UpdateCfg();
Assert.AreEqual(2500, runtimeCfg.CommTimeout);
Assert.AreEqual(6, runtimeCfg.MaxCommRetries);
Assert.AreEqual(300, runtimeCfg.DelayBetweenRetries);
Assert.AreEqual(2, runtimeCfg.IperlCheckErrorsToStop);
Assert.AreEqual(10, runtimeCfg.NrThreads);
Assert.AreEqual(3, editedCfg.NrThreads);
}
}
finally { eventField.SetValue(null, originalHandlers); }
}
[TestMethod]
public void GenesisTooltipsFollowUiCultureAndFallbackToEnglish()
{
var original = System.Threading.Thread.CurrentThread.CurrentUICulture;
try
{
var cultures = new[] { "en-US", "sk-SK", "cs-CZ", "de-DE", "fr-FR" };
var headings = new[] { "Default:", "Prednastaven\u00e1 hodnota:", "V\u00fdchoz\u00ed hodnota:", "Standardwert:", "Default:" };
for (int i = 0; i < cultures.Length; i++)
{
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(cultures[i]);
using (var ctrl = new TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfgCtrl())
{
var tips = (System.Windows.Forms.ToolTip)typeof(TBF.Rig.TestMethods.GenesisCommunication.TestMethodCfgCtrl)
.GetField("settingsToolTip", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(ctrl);
foreach (var field in new[] { "nameTextBox", "commTimeoutTextBox", "maxCommRetriesTextBox", "delayBetweenRetriesTextBox", "nrThreadsTextBox", "iperlCheckErrorsToStopTextBox" })
StringAssert.Contains(tips.GetToolTip(ctrl.Controls.Find(field, true)[0]), headings[i], cultures[i] + field);
var timeout = tips.GetToolTip(ctrl.Controls.Find("commTimeoutTextBox", true)[0]);
StringAssert.Contains(timeout, "500\u20135000 ms");
StringAssert.Contains(timeout, "1800 ms");
Assert.AreEqual(timeout, tips.GetToolTip(ctrl.Controls.Find("commTimeoutLabel", true)[0]));
}
}
}
finally { System.Threading.Thread.CurrentThread.CurrentUICulture = original; }
}
[TestMethod]
public async System.Threading.Tasks.Task GenesisTimeoutChangesCancellationDeadlineAndPreservesLegacyCallers()
{
Assert.IsNull(TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.CreateDeadline(System.Threading.CancellationToken.None, "legacy"));
using (TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.Begin(5000))
using (var longDeadline = TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.CreateDeadline(System.Threading.CancellationToken.None, "long-test"))
{
using (TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.Begin(500))
using (var shortDeadline = TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.CreateDeadline(System.Threading.CancellationToken.None, "short-test"))
{
try
{
await System.Threading.Tasks.Task.Delay(3000, shortDeadline.Token);
Assert.Fail("Expected the configured 500 ms deadline to cancel the operation.");
}
catch (System.OperationCanceledException) { Assert.IsTrue(shortDeadline.IsCancellationRequested); }
Assert.IsFalse(longDeadline.IsCancellationRequested);
}
using (var restored = TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.CreateDeadline(System.Threading.CancellationToken.None, "restored-test"))
{
await System.Threading.Tasks.Task.Delay(700);
Assert.IsFalse(restored.IsCancellationRequested);
}
using (var caller = new System.Threading.CancellationTokenSource())
using (var linked = TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.CreateDeadline(caller.Token, "caller-test"))
{
caller.Cancel();
Assert.IsTrue(linked.IsCancellationRequested);
}
}
Assert.IsNull(TBF.Rig.BridgeComponents.GciBridge.GenesisRequestTimeout.CreateDeadline(System.Threading.CancellationToken.None, "legacy-after"));
}
private static void Execute(SQLiteConnection connection, string sql) private static void Execute(SQLiteConnection connection, string sql)
{ {
using (var command = connection.CreateCommand()) { command.CommandText = sql; command.ExecuteNonQuery(); } using (var command = connection.CreateCommand()) { command.CommandText = sql; command.ExecuteNonQuery(); }
@@ -1,263 +0,0 @@
using System;
using System.Globalization;
using System.Text;
using System.Linq;
using System.Reflection;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader
{
// Golden frames have CRCs generated independently with Python binascii.crc_hqx.
// No ports, databases, GCI engine or production-code changes are required.
[TestClass]
[TestCategory("GenesisReadRegression")]
public class GenesisReadDataRegressionTests
{
[DataTestMethod]
[DataRow("@h 1 0 10000000 FFFFFF00 00000400 00100000 00000000 00008000 00400000 00800000 FFFFF000 0C 00018000 8D2C", 1, 1024, 0.001024)]
[DataRow("@h 1 0 10000000 FFFFFF00 00000400 00100000 00000200 00008000 00400000 00800000 FFFFF000 0C 00018000 7BAC", 1, 512, 0.002048)]
[DataRow("@h 1 0 10000000 FFFFFF00 00000400 00100000 00000400 00008000 00400000 00800000 FFFFF000 0C 00018000 700D", 1, 1024, 0.001024)]
[DataRow("@h 1 0 10000000 FFFFFF00 00000400 00100000 00000800 00008000 00400000 00800000 FFFFF000 0C 00018000 674F", 1, 2048, 0.000512)]
[DataRow("@h 2 0 10000000 FFFFFF00 00000400 00100000 00000000 00008000 00400000 00800000 FFFFF000 0C 00018000 3F43", 2, 1024, 0.001024)]
[DataRow("@h 2 0 10000000 FFFFFF00 00000400 00100000 00000200 00008000 00400000 00800000 FFFFF000 0C 00018000 C9C3", 2, 512, 0.002048)]
[DataRow("@h 2 0 10000000 FFFFFF00 00000400 00100000 00000400 00008000 00400000 00800000 FFFFF000 0C 00018000 C262", 2, 1024, 0.001024)]
[DataRow("@h 2 0 10000000 FFFFFF00 00000400 00100000 00000800 00008000 00400000 00800000 FFFFF000 0C 00018000 D520", 2, 2048, 0.000512)]
[DataRow("@h 3 0 10000000 FFFFFF00 00000400 00100000 00000000 00008000 00400000 00800000 FFFFF000 0C 00018000 A179", 3, 1024, 0.001024)]
[DataRow("@h 3 0 10000000 FFFFFF00 00000400 00100000 00000200 00008000 00400000 00800000 FFFFF000 0C 00018000 57F9", 3, 512, 0.002048)]
[DataRow("@h 3 0 10000000 FFFFFF00 00000400 00100000 00000400 00008000 00400000 00800000 FFFFF000 0C 00018000 5C58", 3, 1024, 0.001024)]
[DataRow("@h 3 0 10000000 FFFFFF00 00000400 00100000 00000800 00008000 00400000 00800000 FFFFF000 0C 00018000 4B1A", 3, 2048, 0.000512)]
public void ProtocolH_GoldenFramesPreserveChannelUnitsAndScale(string frame, int channel, int scale, double volume)
{
var decoder = new StreamingDecoder();
Assert.IsTrue(decoder.DecodeMsg(frame));
var data = decoder.DataCalib;
Assert.IsNotNull(data);
Assert.IsTrue(data.IsValid);
Assert.AreEqual(channel, data.Channel);
Assert.AreEqual(scale, data.VolumeScaleRawPerMl);
Assert.AreEqual(volume, data.VolumeCm, 1e-12);
Assert.AreEqual(1048576d, data.AccuVolumeRaw);
Assert.AreEqual(1024d, data.DeltaVolumeRaw);
Assert.AreEqual(volume / 1024, data.DeltaVolumeQm, 1e-15);
Assert.AreEqual(1.5, data.TimeS, 1e-12);
Assert.AreEqual(.5, data.SampleIntervalS, 1e-12);
Assert.AreEqual(.001, data.AmplitudeUpV, 1e-12);
Assert.AreEqual(.002, data.AmplitudeDownV, 1e-12);
Assert.AreEqual(-1d, data.TemperatureDegC, 1e-12);
Assert.AreEqual(65536d, data.OverflowTimeS);
Assert.IsNull(decoder.DataFlowTest);
double previousVolume = double.NaN, previousTime = double.NaN;
var telegram = new OptoTelegramRaw();
telegram.UpdateFromSmart(data, 17, 2.5f, ref previousVolume, ref previousTime);
Assert.AreEqual(channel - 1, telegram.iChannel);
Assert.AreEqual(volume * 1000, telegram.VolumeRawExt, 1e-9);
Assert.AreEqual(1.5, telegram.TimestampExt, 1e-12);
Assert.AreEqual(17, telegram.Counter);
Assert.AreEqual(2.5f, telegram.RefFlow);
}
[DataTestMethod]
[DataRow("@h 1 0 00000000 00000000 00000400 00100000 00000400 00008000 00400000 00800000 00000000 0C 00018000 0A39", 0, 0, 0)]
[DataRow("@h 1 0 7FFFFFFF 7FFFFFFF 00000400 00100000 00000400 00008000 00400000 00800000 7FFFFFFF 0C 00018000 89B0", 2147483647, 2147483647, 2147483647)]
[DataRow("@h 1 0 80000000 80000000 00000400 00100000 00000400 00008000 00400000 00800000 80000000 0C 00018000 3E06", -2147483648, -2147483648, -2147483648)]
[DataRow("@h 1 0 FFFFFFFF FFFFFFFF 00000400 00100000 00000400 00008000 00400000 00800000 FFFFFFFF 0C 00018000 6870", -1, -1, -1)]
public void ProtocolH_SignedFieldsPreserveTwosComplement(string frame, int total, int delta, int temperature)
{
var decoder = new StreamingDecoder();
Assert.IsTrue(decoder.DecodeMsg(frame));
var data = decoder.DataCalib;
Assert.AreEqual(total, data.RawTotalTimeOfFlight);
Assert.AreEqual(delta, data.RawDeltaTimeOfFlight);
Assert.AreEqual(total / 274877906944d, data.TotalTimeOfFlightS, 1e-15);
Assert.AreEqual(delta / 274877906944d, data.DeltaTimeOfFlightS, 1e-15);
Assert.AreEqual(temperature / 4096d, data.TemperatureDegC, 1e-10);
}
[DataTestMethod]
[DataRow("@f 00000000 00000000 F603", 0.0, 0.0)]
[DataRow("@f 7FFFFFFF FFFFFFFF 3996", 2147.483647, 65535.99998474121)]
[DataRow("@f FFFFFFFF 00010000 21AD", -1e-06, 1.0)]
[DataRow("@f 80000000 00008000 0541", -2147.483648, 0.5)]
public void ProtocolF_PreservesSignedVolumeAndUnsignedTime(string frame, double volume, double time)
{
var decoder = new StreamingDecoder();
Assert.IsTrue(decoder.DecodeMsg(frame));
Assert.IsNotNull(decoder.DataFlowTest);
Assert.IsTrue(decoder.DataFlowTest.IsValid);
Assert.AreEqual(volume, decoder.DataFlowTest.VolumeCm, 1e-9);
Assert.AreEqual(time, decoder.DataFlowTest.TimeS, 1e-12);
Assert.IsNull(decoder.DataCalib);
}
[DataTestMethod]
[DataRow(null)]
[DataRow("")]
[DataRow("garbage")]
[DataRow("@h")]
[DataRow("@h 1")]
[DataRow("@h 1 invalid")]
[DataRow("@f 00000001 00010000 ZZZZ")]
[DataRow("@h\t1\t0\t10000000\tFFFFFF00\t00000400\t00100000\t00000400\t00008000\t00400000\t00800000\tFFFFF000\t0C\t00018000\t700D")]
[DataRow("@h 1 0 10000000 FFFFFF00 00000400 00100000 00000400 00008000 00400000 00800000 FFFFF000 0C 00018000 700D ")]
[DataRow("@he 1 0 10000000 FFFFFF00 00000400 00100000 00000400 00008000 00400000 00800000 FFFFF000 0C 00018000 700D")]
public void InvalidOrUnsupportedInputProducesNoMeasurement(string frame)
{
var decoder = new StreamingDecoder();
decoder.DecodeMsg(frame);
Assert.IsNull(decoder.DataCalib);
Assert.IsNull(decoder.DataFlowTest);
}
[DataTestMethod]
[DataRow(1)]
[DataRow(2)]
[DataRow(3)]
[DataRow(4)]
[DataRow(5)]
[DataRow(6)]
[DataRow(7)]
[DataRow(8)]
[DataRow(9)]
[DataRow(10)]
[DataRow(11)]
[DataRow(12)]
[DataRow(13)]
public void AlteringAnyCalibrationFieldWithoutUpdatingCrcRejectsRecord(int field)
{
var words = Golden.Split(' ');
words[field] = words[field] == "0" ? "1" : "0";
var decoder = new StreamingDecoder();
Assert.IsFalse(decoder.DecodeMsg(string.Join(" ", words)));
Assert.IsNull(decoder.DataCalib);
}
[DataTestMethod]
[DataRow("en-US")]
[DataRow("sk-SK")]
[DataRow("de-DE")]
public void HexDecodingIsIndependentOfCulture(string culture)
{
var previous = System.Threading.Thread.CurrentThread.CurrentCulture;
try
{
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
var decoder = new StreamingDecoder();
Assert.IsTrue(decoder.DecodeMsg(Golden));
Assert.AreEqual(.001024, decoder.DataCalib.VolumeCm, 1e-12);
Assert.AreEqual(-1d, decoder.DataCalib.TemperatureDegC);
}
finally { System.Threading.Thread.CurrentThread.CurrentCulture = previous; }
}
[TestMethod]
public void DiagnosticModeRetainsBadCrcButMarksRecordInvalid()
{
var corrupted = Golden.Substring(0, Golden.Length - 4) + "0000";
var decoder = new StreamingDecoder(false);
Assert.IsFalse(decoder.DecodeMsg(corrupted));
Assert.IsNotNull(decoder.DataCalib);
Assert.IsFalse(decoder.DataCalib.IsValid);
Assert.AreEqual(.001024, decoder.DataCalib.VolumeCm, 1e-12);
}
[TestMethod]
public void CrcMatchesIndependentCcittFalseCheckVector()
{
Assert.AreEqual((ushort)0x29B1, Crc16Ccitt.CalculateMsb1021(Encoding.ASCII.GetBytes("123456789")));
Assert.AreEqual((ushort)0xFFFF, Crc16Ccitt.CalculateMsb1021(new byte[0]));
}
private static void InitializeReaderForTest(GenesisSmartReader reader)
{
const int channelCount = 3;
SetPrivateField(reader, "volumeRawExtLast", new double[channelCount]);
SetPrivateField(reader, "timestampExtLast", new double[channelCount]);
SetPrivateField(reader, "lastTimestamp", new double[channelCount]);
SetPrivateField(reader, "timestampSec", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "timestampSec0", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "lastVolumeRaw", new double[channelCount]);
SetPrivateField(reader, "volumeLtr", Enumerable.Repeat(double.NaN, channelCount).ToArray());
SetPrivateField(reader, "volumeLtr0", Enumerable.Repeat(double.NaN, channelCount).ToArray());
var optoData = new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize];
for (int i = 0; i < optoData.Length; i++)
optoData[i] = new OptoTelegramRaw();
SetPrivateField(reader, "optoData", optoData);
SetPrivateField(reader, "optoDataCount", 0);
SetPrivateField(reader, "toBeFlushed", new OptoTelegramRaw());
SetPrivateField(reader, "flowDirectionDetection", new FlowDirectionDetection());
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
SetPrivateField(reader, "synchronized", false);
SetPrivateField(reader, "synchronized2", false);
SetPrivateField(reader, "partOfTelegram", string.Empty);
SetPrivateField(reader, "startDataProcessing", true);
reader.StopQueueData = false;
reader.TestStartTelegramIx = 0;
reader.TestEndTelegramIx = 0;
}
private static void SetPrivateField(object target, string name, object value)
{
var field = target.GetType().GetField(name, BindingFlags.Instance | BindingFlags.NonPublic);
Assert.IsNotNull(field, name);
field.SetValue(target, value);
}
private static T ReadField<T>(object target, string name)
{
return (T)target.GetType().GetField(name, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(target);
}
[TestMethod]
public void ProcessOptoLinePreservesPayloadAndCounterThroughReaderPipeline()
{
var reader = new GenesisSmartReader(new TBF.Rig.RegisterReaders.GenesisRegReader.GenesisCfg(new TBF.Rig.RegisterReaders.GenesisRegReader.Factory()));
InitializeReaderForTest(reader);
bool complete;
reader.ProcessOptoLine(Golden, DataStreamState.ProcessAndSave, out complete);
Assert.IsFalse(complete);
Assert.AreEqual(1, ReadField<int>(reader, "optoDataCount"));
var rows = ReadField<OptoTelegramRaw[]>(reader, "optoData");
Assert.AreEqual(0, rows[0].iChannel);
Assert.AreEqual(1.024, rows[0].VolumeRawExt, 1e-9);
Assert.AreEqual(1.5, rows[0].TimestampExt, 1e-12);
Assert.AreEqual(0, rows[0].Counter);
reader.ProcessOptoLine("invalid telegram", DataStreamState.ProcessAndSave, out complete);
Assert.AreEqual(1, ReadField<int>(reader, "optoDataCount"));
reader.ProcessOptoLine(Golden, DataStreamState.ProcessAndSave, out complete);
Assert.AreEqual(2, ReadField<int>(reader, "optoDataCount"));
Assert.AreEqual(1, rows[1].Counter);
Assert.AreEqual(rows[0].VolumeRawExt, rows[1].VolumeRawExt, 1e-9);
}
[DataTestMethod]
[DataRow("stop")]
[DataRow("queue")]
[DataRow("processing")]
public void ReaderStopGatesPreventMeasurementsFromBeingAppended(string gate)
{
var reader = new GenesisSmartReader(new TBF.Rig.RegisterReaders.GenesisRegReader.GenesisCfg(new TBF.Rig.RegisterReaders.GenesisRegReader.Factory()));
InitializeReaderForTest(reader);
if (gate == "stop") SetPrivateField(reader, "_isStopping", true);
if (gate == "queue") reader.StopQueueData = true;
if (gate == "processing") SetPrivateField(reader, "startDataProcessing", false);
bool complete;
reader.ProcessOptoLine(Golden, DataStreamState.ProcessAndSave, out complete);
Assert.AreEqual(0, ReadField<int>(reader, "optoDataCount"));
Assert.IsFalse(complete);
}
private const string Golden = "@h 1 0 10000000 FFFFFF00 00000400 00100000 00000400 00008000 00400000 00800000 FFFFF000 0C 00018000 700D";
}
}
@@ -236,40 +236,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
Assert.AreEqual(15625.0, calib[1], 0.001); Assert.AreEqual(15625.0, calib[1], 0.001);
Assert.AreEqual(15625.0, calib[2], 0.001); Assert.AreEqual(15625.0, calib[2], 0.001);
} }
[TestMethod]
public void GetQ3Calibration_ShouldUseWriteActivityLimits_AndFallbackWhenTheyAreUnset()
{
double limitLo;
double limitHi;
Assert.IsFalse(GenesisSmartReader.TryGetEffectiveQ3CalibrationErrorLimits(0.0, 0.0, out limitLo, out limitHi));
Assert.AreEqual(-5.0, limitLo);
Assert.AreEqual(5.0, limitHi);
Assert.IsTrue(GenesisSmartReader.TryGetEffectiveQ3CalibrationErrorLimits(-2.0, 2.0, out limitLo, out limitHi));
Assert.AreEqual(-2.0, limitLo);
Assert.AreEqual(2.0, limitHi);
var sut = new GenesisSmartReader();
// At refTime 120 s, this gives a calculated factor 3 % above the initial factor.
var raw = CreateKnownStartEndData(
(100.0, 100.0 + (200.0 / 1.03 / 12.0), 10.0, 20.0),
(200.0, 200.0 + (200.0 / 1.03 / 12.0), 10.0, 20.0),
(300.0, 300.0 + (200.0 / 1.03 / 12.0), 10.0, 20.0));
SetPrivateField(sut, "_rawStartEndByChannel", raw);
SetPrivateField(sut, "_recalculatedStartEndByChannel", raw);
SetPrivateField(sut, "optoDataCount", 2);
sut.TestStartTelegramIx = 0;
sut.TestEndTelegramIx = 1;
var valid = new bool[3];
var differences = new double[3];
var factors = new double[3];
sut.GetQ3Calibration(200.0, 120.0, ValidInitFactors, -2.0, 2.0,
ref valid, ref differences, ref factors);
CollectionAssert.AreEqual(new[] { false, false, false }, valid);
Assert.IsTrue(differences.All(x => x > 2.9 && x < 3.1));
}
[TestMethod] [TestMethod]
public void CalculateQ3Calibration_ShouldComputeExpectedChannelValues_FromSimulationData() public void CalculateQ3Calibration_ShouldComputeExpectedChannelValues_FromSimulationData()
@@ -458,4 +424,4 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
Assert.Fail($"Field '{fieldName}' not found."); Assert.Fail($"Field '{fieldName}' not found.");
} }
} }
} }
+1 -1
View File
@@ -231,7 +231,7 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
<ItemGroup><Compile Include="GenesisRecoveryTests.cs" /><Compile Include="GenesisParallelSchedulingTests.cs" /></ItemGroup> <ItemGroup><Compile Include="GenesisRecoveryTests.cs" /><Compile Include="GenesisParallelSchedulingTests.cs" /><Compile Include="Rig\RegisterReaders\GenesisRegReader\GenesisReadDataRegressionTests.cs" /></ItemGroup>
<!-- Project dependencies may copy an older SQLite interop DLL with a newer timestamp. --> <!-- Project dependencies may copy an older SQLite interop DLL with a newer timestamp. -->
<Target Name="EnsureMatchingSQLiteInterop" AfterTargets="Build"> <Target Name="EnsureMatchingSQLiteInterop" AfterTargets="Build">
<Copy SourceFiles="@(SQLiteInteropFiles)" <Copy SourceFiles="@(SQLiteInteropFiles)"