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.
This commit is contained in:
Michal Buzik 2026-09-10 21:33:55 +02:00
parent 52bd0d01ce
commit 02de7c6c57
11 changed files with 569 additions and 175 deletions

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>

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>

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>

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>

View File

@ -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

View File

@ -1,4 +1,4 @@
/// ///
/// Copyright (c) 2015-2019 Sensus Metering Systems /// Copyright (c) 2015-2019 Sensus Metering Systems
/// Author: Milan Hanajík /// Author: Milan Hanajík
/// ///
@ -7,8 +7,44 @@ using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBF.Rig.TestMethods.GenesisCommunication namespace TBF.Rig.TestMethods.GenesisCommunication
{ {
/// <summary>Tracks one completion per worker, activity and group.</summary>
public sealed class GenesisWorkerGroupCompletion
{
private readonly int workerCount;
private readonly System.Collections.Generic.HashSet<int> completed = new System.Collections.Generic.HashSet<int>();
private int activity, group;
private bool released;
public GenesisWorkerGroupCompletion(int workerCount)
{
if (workerCount < 1 || workerCount > 10) throw new ArgumentOutOfRangeException(nameof(workerCount));
this.workerCount = workerCount;
}
public void Begin(int activityStep, int groupNumber)
{
lock (completed) { activity = activityStep; group = groupNumber; released = false; completed.Clear(); }
}
public bool Complete(int activityStep, int groupNumber, int worker)
{
lock (completed)
{
if (released || activityStep != activity || groupNumber != group || worker < 0 || worker >= workerCount) return false;
if (!completed.Add(worker) || completed.Count != workerCount) return false;
released = true;
return true;
}
}
public static System.Collections.Generic.IEnumerable<int> BoardIndexes(int worker, int workers, int boards)
{
if (workers < 1 || workers > 10 || worker < 0 || worker >= workers || boards < 0) throw new ArgumentOutOfRangeException();
for (int index = worker; index < boards; index += workers) yield return index;
}
}
public class CommCompletedEventArgs : EventArgs public class CommCompletedEventArgs : EventArgs
{ {
public bool WorkerGroupCompleted;
public int ActivityStep;
public int Group;
public int ThreadId; public int ThreadId;
public int WMNr0; /// 0-based water meter position public int WMNr0; /// 0-based water meter position
public GenesisSmartReader Ihead; public GenesisSmartReader Ihead;

View File

@ -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.
/// ///
@ -204,14 +204,14 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
static IList<iPerlCommunicationParams> multiTestParams; static IList<iPerlCommunicationParams> multiTestParams;
static int currentActivityStep; static volatile int currentActivityStep;
static int currentGroup; /// form -> worker thread (0 = none) static volatile int currentGroup; /// form -> worker thread (0 = none)
static int lastGroup; static int lastGroup;
static int completedCommCount; /// Number of completed communication steps private GenesisWorkerGroupCompletion groupCompletion;
static IList<Thread> workerThreads; static IList<Thread> workerThreads;
static IList<int> muxBrdOrGroup14Nrs; static IList<int> muxBrdOrGroup14Nrs;
static bool stopWorkerThreads; /// form -> worker thread static volatile bool stopWorkerThreads; /// form -> worker thread
/// <summary> Parameterless constructor (without watermeters, threads) </summary> /// <summary> Parameterless constructor (without watermeters, threads) </summary>
@ -384,7 +384,6 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
/// ///
currentActivityStep = 0; currentActivityStep = 0;
currentGroup = 0; currentGroup = 0;
completedCommCount = 0;
stopWorkerThreads = false; stopWorkerThreads = false;
/// group numbers are >=1, lastGroup == 0 means there is no group /// group numbers are >=1, lastGroup == 0 means there is no group
@ -409,6 +408,9 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
if (!muxBrdOrGroup14Nrs.Contains(iPerl.MuxBoardNrOrGroup14)) muxBrdOrGroup14Nrs.Add(iPerl.MuxBoardNrOrGroup14); if (!muxBrdOrGroup14Nrs.Contains(iPerl.MuxBoardNrOrGroup14)) muxBrdOrGroup14Nrs.Add(iPerl.MuxBoardNrOrGroup14);
} }
groupCompletion = new GenesisWorkerGroupCompletion(workerThreads.Count);
groupCompletion.Begin(0, 1);
log.InfoFormat("Genesis workers configured: Threads={0}, Group1Count={1}, LastGroup2={2}", workerThreads.Count, muxBrdOrGroup14Nrs.Count, lastGroup);
log.WarnFormat("nrThreads = {0}", workerThreads.Count); log.WarnFormat("nrThreads = {0}", workerThreads.Count);
} }
@ -666,6 +668,8 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
for (int i = 0; i < multiTestParams.Count; i++ ) for (int i = 0; i < multiTestParams.Count; i++ )
{ {
while (activityStep != currentActivityStep && !stopWorkerThreads) Thread.Sleep(50);
if (stopWorkerThreads) break;
Test currentTest = tests[i]; Test currentTest = tests[i];
iPerlCommunicationParams currentTestParams = multiTestParams[i]; iPerlCommunicationParams currentTestParams = multiTestParams[i];
string currentActivity = currentTestParams.Activity; /// Current activity string currentActivity = currentTestParams.Activity; /// Current activity
@ -732,7 +736,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
#if TURA_SPECIAL #if TURA_SPECIAL
int threadIx = threadID; /// Just one thread for TURA_SPECIAL int threadIx = threadID; /// Just one thread for TURA_SPECIAL
#else #else
for (int threadIx = threadID; threadIx < threadID + 10; threadIx += cfg.NrThreads)// - max threads = 4 => 10 foreach (int threadIx in GenesisWorkerGroupCompletion.BoardIndexes(threadID, workerThreads.Count, muxBrdOrGroup14Nrs.Count))
#endif #endif
{ {
bool wmFound = false; bool wmFound = false;
@ -759,6 +763,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
@ -884,8 +890,11 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
if (stopWorkerThreads) break; if (stopWorkerThreads) break;
} }
if (stopWorkerThreads) break; if (stopWorkerThreads) break;
} /// for (int group log.DebugFormat("Genesis worker group completed: Activity={0}, Group2={1}, Worker={2}", activityStep, group, threadID);
OnCommCompleted(null, new CommCompletedEventArgs(threadID, -1, null, null, string.Empty, CommErr.None)
{ WorkerGroupCompleted = true, ActivityStep = activityStep, Group = group });
} /// for (int group
TBF.UiBridge.Bridge.OnTestProgress(null, new TBF.UiBridge.TestProgressEventArgs(tests[i], Progress.Completed)); TBF.UiBridge.Bridge.OnTestProgress(null, new TBF.UiBridge.TestProgressEventArgs(tests[i], Progress.Completed));
activityStep++; activityStep++;
@ -1608,21 +1617,17 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
} }
} }
#if !TURA_SPECIAL // Per-meter results update the UI only. Advance after every worker
/// // has finished all of its Group 1 assignments for this Group 2.
/// Branch if (!data.WorkerGroupCompleted || !groupCompletion.Complete(data.ActivityStep, data.Group, data.ThreadId)) return;
/// log.InfoFormat("Genesis group completed: Activity={0}, Group2={1}, Workers={2}", data.ActivityStep, data.Group, workerThreads.Count);
lock (this)
{
if (++completedCommCount < 4) return;
completedCommCount = 0;
}
#endif
if (currentGroup < lastGroup) if (currentGroup < lastGroup)
{ {
/// Go to the next step / next group /// Go to the next step / next group
currentGroup++; currentGroup++;
groupCompletion.Begin(currentActivityStep, currentGroup);
} }
else if (currentActivityStep + 1 < multiTestParams.Count) else if (currentActivityStep + 1 < multiTestParams.Count)
{ {
@ -1630,16 +1635,17 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
currentActivityStep++; currentActivityStep++;
activityLabel.Text = multiTestParams[currentActivityStep].Activity; activityLabel.Text = multiTestParams[currentActivityStep].Activity;
currentGroup++; currentGroup++;
groupCompletion.Begin(currentActivityStep, currentGroup);
} }
else else
{ {
log.Debug("Wait for finish workerThreads 4 seconds"); log.Debug("Wait for finish workerThreads 4 seconds");
Thread lastThread = workerThreads[data.ThreadId]; Thread[] finishingThreads = workerThreads.ToArray();
Task.Run(() => Task.Run(() =>
{ {
lastThread.Join(2000); foreach (var thread in finishingThreads) thread.Join();
if (!CanUseUi()) return; if (!CanUseUi()) return;

View File

@ -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();
} }

View File

@ -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.

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(); }

View File

@ -231,7 +231,7 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
<ItemGroup><Compile Include="GenesisRecoveryTests.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)"