<Feat>: Add standalone Genesis preadjustment activities, fix configuration control and increase revision to 3.9.3146.101

Cause:

- GenesisCommunication did not expose GCI preadjustment phases as selectable test activities.
- Meter size and temperature inputs required by preadjustment could not be configured.
- The configuration control incorrectly expected the iPerlCommunication configuration type.
- Preadjustment results were not propagated to TBF test and meter results.

Solution:

1. Added standalone preadjustment activities
   - Added Initialization, Detect, Preparation, Amplitude Test, Temperature Calibration, Push Temperature, Offset Test and Completion.
   - Each phase can be selected and executed independently.

2. Added preadjustment parameters
   - Added configurable meter size and temperature.
   - Added validation and supported meter-size selection.

3. Added batch preadjustment execution
   - Resolves enabled Genesis register readers and their slots.
   - Verifies that all readers use the same GCI Bridge instance.
   - Dispatches the selected activity through the corresponding GCI Bridge API.

4. Added result processing
   - Stores execution time and completion status.
   - Updates test and meter results according to the GCI response.

5. Fixed the configuration control
   - Corrected TestMethodCfgCtrl to accept the GenesisCommunication TestMethodCfg type.

6. Increased revision
   - Updated revision to 3.9.3146.101.
This commit is contained in:
2026-09-10 14:08:00 +02:00
parent 0cfde7f06b
commit 1c6f9af2bb
4 changed files with 259 additions and 7 deletions
+2 -2
View File
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
[assembly: AssemblyVersion("3.9.3146.0")]
[assembly: AssemblyFileVersion("3.9.3146.0")]
[assembly: AssemblyVersion("3.9.3146.101")]
[assembly: AssemblyFileVersion("3.9.3146.101")]
@@ -5,16 +5,27 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Threading;
using Common;
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary;
using Config.Entities;
using GenesisCordonelInterface.API;
using log4net;
using RestClient;
using Results.Entities;
using TBF.Resources;
using TBF.Rig.Generic;
using TBF.Rig.Sequences;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
using TBF.Rig.TestMethods.iPerlCommunication;
using TBF.UiBridge;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using GciBridgeType = TBF.Rig.BridgeComponents.GciBridge.GciBridge;
using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
/// Point definition
namespace TBF.Rig.TestMethods.GenesisCommunication
@@ -28,6 +39,14 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
public const string Q2correctionCheckCmd = "Q2 correction check ";
public const string IperlCheckCmd = "iPERL_check ";
public const string SimulateCmd = "simulate ";
public const string PreadjustmentInitializationStr = "Preadjustment Initialization";
public const string PreadjustmentDetectStr = "Preadjustment Detect";
public const string PreadjustmentPreparationStr = "Preadjustment Preparation";
public const string PreadjustmentAmplitudeTestStr = "Preadjustment Amplitude Test";
public const string PreadjustmentTemperatureCalibrationStr = "Preadjustment Temperature Calibration";
public const string PreadjustmentPushTemperatureStr = "Preadjustment Push Temperature";
public const string PreadjustmentOffsetTestStr = "Preadjustment Offset Test";
public const string PreadjustmentCompletionStr = "Preadjustment Completion";
System.Windows.Forms.Form modelessDlg;
@@ -71,6 +90,12 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
processDataLoggingOp = new TBF.Rig.Operations.ProcessDataLoggingOp(processDataLogger, this, false);
iPerlCommunicationParams genesisTestParams = testParams as iPerlCommunicationParams;
if (genesisTestParams != null && IsPreadjustmentActivity(genesisTestParams.Activity))
{
return ExecutePreadjustment(test, method, genesisTestParams);
}
string cmd;
if (testParams.Activity.ToLower().Equals(cmd = GenesisCommunicationForm.GetDefaultQ2CorrectionsStr.ToLower()))
{
@@ -410,6 +435,199 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
return new List<Event> { Event.Done };
}
private static bool IsPreadjustmentActivity(string activity)
{
return string.Equals(activity, PreadjustmentInitializationStr, StringComparison.OrdinalIgnoreCase) ||
string.Equals(activity, PreadjustmentDetectStr, StringComparison.OrdinalIgnoreCase) ||
string.Equals(activity, PreadjustmentPreparationStr, StringComparison.OrdinalIgnoreCase) ||
string.Equals(activity, PreadjustmentAmplitudeTestStr, StringComparison.OrdinalIgnoreCase) ||
string.Equals(activity, PreadjustmentTemperatureCalibrationStr, StringComparison.OrdinalIgnoreCase) ||
string.Equals(activity, PreadjustmentPushTemperatureStr, StringComparison.OrdinalIgnoreCase) ||
string.Equals(activity, PreadjustmentOffsetTestStr, StringComparison.OrdinalIgnoreCase) ||
string.Equals(activity, PreadjustmentCompletionStr, StringComparison.OrdinalIgnoreCase);
}
private IList<Event> ExecutePreadjustment(Test test, TestMethod method, iPerlCommunicationParams testParams)
{
DateTime startTime = DateTime.Now;
bool success = false;
string response = string.Empty;
List<KeyValuePair<int, GenesisSmartReader>> readers = new List<KeyValuePair<int, GenesisSmartReader>>();
TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 0, 30, 0, 30, 0, 0 });
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.JustStarted));
Bridge.OnActivity(this, testParams.Activity);
try
{
readers = GetPreadjustmentReaders(test);
GciBridgeType bridge = GetCommonGciBridge(readers);
success = ExecutePreadjustmentActivity(bridge, readers, testParams, out response);
}
catch (Exception ex)
{
response = ex.Message;
log.Error(string.Format("{0} failed.", testParams.Activity), ex);
}
UpdatePreadjustmentResult(test, method, readers, success, startTime);
if (success)
log.InfoFormat("{0} finished successfully. {1}", testParams.Activity, response);
else
log.ErrorFormat("{0} failed. {1}", testParams.Activity, response);
Bridge.OnActivity(this, string.Format("{0}: {1}", testParams.Activity, response));
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.Completed));
Bridge.OnTestCompleted(this, new TestCompletedEventArgs(test.Name, ProcessData.BatchRslts.GetTestRslt(test.Name, test.Part)));
allResults.Info(TestResult2CsvLine(test.Name, test.Part));
return new List<Event> { Event.Done };
}
private static List<KeyValuePair<int, GenesisSmartReader>> GetPreadjustmentReaders(Test test)
{
var metersPath = StateMachine.GetMetersPath(test);
if (metersPath == null || metersPath.RegisterReaders == null)
throw new InvalidOperationException("No register readers are configured for the test.");
var readers = new List<KeyValuePair<int, GenesisSmartReader>>();
for (int meterPosition = 0; meterPosition < metersPath.RegisterReaders.Length; meterPosition++)
{
GenesisSmartReader reader = metersPath.RegisterReaders[meterPosition] as GenesisSmartReader;
if (reader == null)
continue;
if (meterPosition < ProcessData.BatchRslts.Batch.WaterMeters.Count && ProcessData.BatchRslts.Batch.WaterMeters[meterPosition] != null && ProcessData.BatchRslts.Batch.WaterMeters[meterPosition].Disabled)
continue;
if (reader.GetSlotNr <= 0)
throw new InvalidOperationException(string.Format("Genesis reader '{0}' has invalid slot number {1}.", reader.Name, reader.GetSlotNr));
readers.Add(new KeyValuePair<int, GenesisSmartReader>(meterPosition, reader));
}
if (readers.Count == 0)
throw new InvalidOperationException("No enabled Genesis register reader with a valid slot was found.");
if (readers.Select(item => item.Value.GetSlotNr).Distinct().Count() != readers.Count)
throw new InvalidOperationException("More than one Genesis register reader uses the same GCI slot.");
return readers;
}
private static GciBridgeType GetCommonGciBridge(IEnumerable<KeyValuePair<int, GenesisSmartReader>> readers)
{
GciBridgeType commonBridge = null;
foreach (KeyValuePair<int, GenesisSmartReader> item in readers)
{
GciBridgeType bridge = item.Value.CommInterfaceBridge;
if (bridge == null)
throw new InvalidOperationException(string.Format("Genesis reader '{0}' has no valid GCI Bridge configured.", item.Value.Name));
if (commonBridge != null && !ReferenceEquals(commonBridge, bridge))
throw new InvalidOperationException("Enabled Genesis readers do not use the same GCI Bridge instance.");
commonBridge = bridge;
}
return commonBridge;
}
private static bool ExecutePreadjustmentActivity(GciBridgeType bridge, IList<KeyValuePair<int, GenesisSmartReader>> readers, iPerlCommunicationParams testParams, out string response)
{
if (string.Equals(testParams.Activity, PreadjustmentInitializationStr, StringComparison.OrdinalIgnoreCase))
{
MeterSize meterSize;
if (!Enum.TryParse(testParams.MeterSize, true, out meterSize) || meterSize == MeterSize.NA)
throw new InvalidOperationException(string.Format("Meter size '{0}' is not valid.", testParams.MeterSize));
var progress = new ProcessProgress
{
Setting = new PreAdjustmentSettingsContainer
{
TempOnly = false,
Culture = new CultureInfo("en-US"),
MeterSize = meterSize,
NumberOfPaths = 3,
OffsetTestSettlingTime = 0
},
IsAutomaticMode = true
};
var meterControls = new List<MeterStateControl>();
foreach (KeyValuePair<int, GenesisSmartReader> item in readers)
{
var meterControl = new MeterStateControl(item.Value.GetSlotNr);
meterControl.SetChecked(true);
meterControl.IsEnabled = true;
meterControls.Add(meterControl);
}
PublicModels.PreAdjustmentInitializationResult result = bridge.Preadjustment_Initialization(progress, meterControls);
response = result.ToString();
return result.Success;
}
if (string.Equals(testParams.Activity, PreadjustmentDetectStr, StringComparison.OrdinalIgnoreCase))
{
var selectedSlots = readers.Select(item => new GciPublicModels.MeterBatchDebugStatus { Slot = item.Value.GetSlotNr, Selected = true }).ToList();
PublicModels.PreadjustmentDetectResult result = bridge.PreAdjustment_DetectDirect(selectedSlots, CancellationToken.None);
response = result.ToString();
return result.Success;
}
if (string.Equals(testParams.Activity, PreadjustmentPushTemperatureStr, StringComparison.OrdinalIgnoreCase))
{
// Keep temperature delivery as an explicit sibling GCI API activity. A procedure can run it simultaneously with temperature calibration.
bool pushed = bridge.PreAdjustment_PushTemperature(testParams.Temperature);
response = pushed ? string.Format("Temperature {0} °C was accepted.", testParams.Temperature) : "GCI preadjustment was not initialized; temperature was not accepted.";
return pushed;
}
PublicModels.PreAdjustmentProcessResult processResult;
if (string.Equals(testParams.Activity, PreadjustmentPreparationStr, StringComparison.OrdinalIgnoreCase))
processResult = bridge.PreAdjustment_PreparationDirect();
else if (string.Equals(testParams.Activity, PreadjustmentAmplitudeTestStr, StringComparison.OrdinalIgnoreCase))
processResult = bridge.PreAdjustment_AmplitudeTestDirect();
else if (string.Equals(testParams.Activity, PreadjustmentTemperatureCalibrationStr, StringComparison.OrdinalIgnoreCase))
processResult = bridge.PreAdjustment_TemperatureCalibrationDirect();
else if (string.Equals(testParams.Activity, PreadjustmentOffsetTestStr, StringComparison.OrdinalIgnoreCase))
processResult = bridge.PreAdjustment_OffsetTestDirect();
else if (string.Equals(testParams.Activity, PreadjustmentCompletionStr, StringComparison.OrdinalIgnoreCase))
processResult = bridge.PreAdjustment_CompletionDirect();
else
throw new InvalidOperationException(string.Format("Unsupported preadjustment activity '{0}'.", testParams.Activity));
response = processResult.ToString();
return processResult.Success;
}
private static void UpdatePreadjustmentResult(Test test, TestMethod method, IEnumerable<KeyValuePair<int, GenesisSmartReader>> readers, bool success, DateTime startTime)
{
TestRslt testResult = ProcessData.BatchRslts.GetTestRslt(test.Name, test.Part);
if (testResult == null)
return;
testResult.MethodClass = method.ClassName;
testResult.StartTime = startTime;
testResult.EndTime = DateTime.Now;
testResult.TestTime = Math.Max(0, (int)(testResult.EndTime - startTime).TotalSeconds);
testResult.TestDone = true;
foreach (KeyValuePair<int, GenesisSmartReader> item in readers)
{
MeterTestRslt meterResult = ProcessData.BatchRslts.GetMeterTestRslt(test.Name, item.Key, CompoundMeterId.Single);
if (meterResult == null)
continue;
meterResult.WaterMeter.SerialNr = item.Value.SerialNr;
meterResult.Passed = success;
meterResult.TestDone = true;
}
}
/// <summary>
/// Read default Q2 correction factors from a REST service (= Web service).
/// </summary>
@@ -14,14 +14,14 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
{
public bool ShowMore { get { return false; } }
iPerlCommunication.TestMethodCfg config;
public IComponentCfg Config
TestMethodCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as iPerlCommunication.TestMethodCfg;
Redraw();
config = value as TestMethodCfg;
Redraw();
}
}
@@ -27,12 +27,16 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
/// Communication activity
public bool SimultWithPrevious;
public bool SimultWithNext;
public string MeterSize = "DN50";
public float Temperature = 20.0f;
public override void InitializeAll()
{
Activity = GenesisCommunicationForm.SlotInitializeStr;
SimultWithPrevious = false;
SimultWithNext = false;
MeterSize = "DN50";
Temperature = 20.0f;
}
@@ -41,6 +45,8 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
Strings.Activity,
Strings.Simultaneous_with_previous_step,
Strings.Simultaneous_with_next_step,
"Meter size",
"Temperature [°C]",
};
public override string ParamName(int i) { return paramNames[i]; }
public override int ParamsCount() { return paramNames.Length; }
@@ -65,6 +71,14 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
retVal.Add(GenesisCommunicationForm.WriteSlotQ3CalibrationStr);
retVal.Add(GenesisCommunicationForm.CheckMeterPrepareStr);
retVal.Add(GenesisCommunicationForm.HoldSlotStr);
retVal.Add(GenesisCommunicationSeq.PreadjustmentInitializationStr);
retVal.Add(GenesisCommunicationSeq.PreadjustmentDetectStr);
retVal.Add(GenesisCommunicationSeq.PreadjustmentPreparationStr);
retVal.Add(GenesisCommunicationSeq.PreadjustmentAmplitudeTestStr);
retVal.Add(GenesisCommunicationSeq.PreadjustmentTemperatureCalibrationStr);
retVal.Add(GenesisCommunicationSeq.PreadjustmentPushTemperatureStr);
retVal.Add(GenesisCommunicationSeq.PreadjustmentOffsetTestStr);
retVal.Add(GenesisCommunicationSeq.PreadjustmentCompletionStr);
@@ -126,10 +140,18 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
return retVal;
}
else
else if (i == 1 || i == 2)
{
return new string[] { Strings.yes, Strings.no };
}
else if (i == 3)
{
var meterSizes = new List<string>(System.Enum.GetNames(typeof(Xylem.Common.Logic.ProductionOrderCore.OrderData.MeterSize)));
meterSizes.Remove(Xylem.Common.Logic.ProductionOrderCore.OrderData.MeterSize.NA.ToString());
return meterSizes;
}
return null;
}
public override string ToString(int i)
@@ -139,6 +161,8 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
case 0: return Activity;
case 1: return (SimultWithPrevious ? Strings.yes : Strings.no);
case 2: return (SimultWithNext ? Strings.yes : Strings.no);
case 3: return MeterSize;
case 4: return Temperature.ToString();
default: return string.Empty;
}
}
@@ -150,6 +174,8 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
case 0: Activity = strValue; return CfgUpdateFlags.None;
case 1: SimultWithPrevious = strValue.ToLower().Equals(Strings.yes.ToLower()); return CfgUpdateFlags.None;
case 2: SimultWithNext = strValue.ToLower().Equals(Strings.yes.ToLower()); return CfgUpdateFlags.None;
case 3: MeterSize = strValue; return CfgUpdateFlags.None;
case 4: Temperature = Utils.ParseSFloat(strValue); return CfgUpdateFlags.None;
default: return CfgUpdateFlags.None;
}
}
@@ -157,6 +183,7 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
public bool ValidateParam(int i, string strValue, out string message)
{
message = string.Empty;
float temperature;
switch (i)
{
@@ -166,6 +193,11 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
case 2:
if (strValue.Equals(Strings.yes) || strValue.Equals(Strings.no)) return true;
break;
case 3:
return System.Enum.TryParse(strValue, true, out Xylem.Common.Logic.ProductionOrderCore.OrderData.MeterSize meterSize) && meterSize != Xylem.Common.Logic.ProductionOrderCore.OrderData.MeterSize.NA;
case 4:
if (Utils.TryParseSFloat(strValue, out temperature) && temperature >= -20.0f && temperature <= 100.0f) return true;
break;
default:
message = "Invalid index";
return false;
@@ -180,6 +212,8 @@ namespace TBF.Rig.TestMethods.GenesisCommunication
prms.Activity = this.Activity;
prms.SimultWithPrevious = this.SimultWithPrevious;
prms.SimultWithNext = this.SimultWithNext;
prms.MeterSize = this.MeterSize;
prms.Temperature = this.Temperature;
}
public IParamsProvider Clone()