Compare commits

...

4 Commits

24 changed files with 2859 additions and 357 deletions

View File

@ -1,9 +1,15 @@
using GenesisCordonelInterface.Core.Threading;
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes;
using CordonelPreadjustmentUi.Processes.Actions;
using CordonelPreadjustmentUi.Processes.Itinerary;
using GenesisCordonelInterface.Core.Threading;
using GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI;
using NLog;
using NLog.Fluent;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Linq;
using System.Reflection;
@ -16,7 +22,9 @@ using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
using Xylem.Common.Hardware.WaterMeter.WaterMeterRegisters;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using static GenesisCordonelInterface.API.PublicModels;
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
using static Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Register;
@ -30,8 +38,14 @@ namespace GenesisCordonelInterface.API
//private static readonly Lazy<ILogger> Logger = new Lazy<ILogger>(() => LogManager.GetLogger("GCI"));
private static readonly NLog.ILogger Logger = NLog.LogManager.GetLogger("GenesisCordonelInterface");
//GenesisToolBox
private readonly MeterBatch _meterBatch = new MeterBatch();
//Preadjustment
public PreAdjustmentSettingsContainer _settings = new PreAdjustmentSettingsContainer();
public List<MeterStateControl> _meterControls = new List<MeterStateControl>();
public List<MeterStateControl> _tempMeterControls = new List<MeterStateControl>();
// Protects all access to _meterBatch.ListOfMeters
private readonly object _meterBatchLock = new object();
private static readonly object _setupGenesisMeterLock = new object();
@ -75,16 +89,19 @@ namespace GenesisCordonelInterface.API
#endregion
#region ================================== MeterBatch Debug ==================================
/// <summary>
///
/// </summary>
/// <returns></returns>
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
{
List<GenesisMeter> meters;
List<ZeroFlowGenesisMeter> meters;
//just snapshot of list under lock
lock (_meterBatchLock)
{
meters = _meterBatch.ListOfMeters
.OfType<GenesisMeter>()
.OfType<ZeroFlowGenesisMeter>()
.ToList();
}
@ -152,7 +169,7 @@ namespace GenesisCordonelInterface.API
lock (_meterBatchLock)
{
return _meterBatch.ListOfMeters
.OfType<GenesisMeter>()
.OfType<ZeroFlowGenesisMeter>()
.FirstOrDefault(m => m.Slot == slot);
}
}
@ -193,7 +210,7 @@ namespace GenesisCordonelInterface.API
{
lock (_meterBatchLock)
{
foreach (var meter in _meterBatch.ListOfMeters.OfType<GenesisMeter>())
foreach (var meter in _meterBatch.ListOfMeters.OfType<ZeroFlowGenesisMeter>())
{
meter.DisposeMeter();
}
@ -228,6 +245,16 @@ namespace GenesisCordonelInterface.API
return string.IsNullOrWhiteSpace(port) ? "<empty>" : port;
}
public ConcurrentDictionary<RegisterDefinition, Byte[]> GetRegistersDicForSlot(int slot)
{
var meter = GetMeterThreadSafe(slot);
if (meter == null)
throw new Exception($"Slot {slot} not initialized.");
return meter.GetRegistersDic();
}
#endregion
#region ================================== INIT/UPDATE ==================================
@ -345,7 +372,7 @@ namespace GenesisCordonelInterface.API
PortConfig? req,
PortConfig? str)
{
GenesisMeter meter = new GenesisMeter();
ZeroFlowGenesisMeter meter = new ZeroFlowGenesisMeter(0, 3, !(_meterControls is TempMeterStateControl), useForPreadjustmentUI: false);
meter.Slot = slot;
@ -354,7 +381,8 @@ namespace GenesisCordonelInterface.API
meter.requestPortConfig = req;
meter.streamingPortConfig = str;
//meter.SetupGenesisMeter(slot, req, str, true);
meter.onlineOperation = false;
lock (_meterBatchLock)
{
_meterBatch.AddMeter2(meter);
@ -942,9 +970,17 @@ namespace GenesisCordonelInterface.API
var meter = GetMeterThreadSafe(slot);
meter.Logout();
bool logoutSuccess = meter.Logout();
meter.Disconnect();
if (meter.IsLoggedOn)
{
meter.MarkLoggedOut();
meter.LastLogoutStatus = logoutSuccess
? "Successfully logged out"
: "Logout failed, but connection was closed";
}
bool success = !meter.IsConnected && !meter.IsLoggedOn;
if (success)
@ -1370,5 +1406,812 @@ namespace GenesisCordonelInterface.API
}
#endregion
// GCI to PreadjustmentUI - MANUAL - Laatzen GUI
#region ================================== PreAdjustmentUI form call ==================================
private FrmCordonelPreadjustmentUI _preadjustmentForm;
private readonly object _formLock = new object();
public event EventHandler PreadjustmentFormClosedByUser;
/// <summary>
/// Shows singleton instance of preadjustment form.
///
/// Behavior:
///
/// ShowPreadjustmentForm()
/// ↓
/// create form instance if necessary
/// ↓
/// user works with form
/// ↓
/// user clicks X
/// ↓
/// FormClosing
/// ↓
/// Cancel closing
/// ↓
/// Hide()
/// ↓
/// PreadjustmentFormClosedByUser
/// ↓
/// external workflow continues
///
/// Notes:
/// - form instance is reused
/// - form is hidden instead of disposed
/// - event notification is non-blocking
/// - repeated calls bring existing form to front
/// - actual disposal happens only during application shutdown
///
/// Typical usage:
///
/// _bridge.PreadjustmentFormClosedByUser += (s,e)=>
/// {
/// ContinueWorkflow();
/// };
///
/// _bridge.ShowPreadjustmentForm(this);
///
/// </summary>
public void ShowPreadjustmentForm(IWin32Window owner)
{
lock (_formLock)
{
// Create form only if it does not exist
// or has already been disposed
if (_preadjustmentForm == null || _preadjustmentForm.IsDisposed)
{
_preadjustmentForm = new FrmCordonelPreadjustmentUI(_meterBatch);
_preadjustmentForm.FormClosing += (s, e) =>
{
// Hide the form instead of destroying it
// when user clicks the close button
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
_preadjustmentForm.Hide();
// Notify outside code that the form was closed by user
PreadjustmentFormClosedByUser?.Invoke(
this,
EventArgs.Empty);
}
};
}
// If already visible, bring it to front
if (_preadjustmentForm.Visible)
{
_preadjustmentForm.Activate();
_preadjustmentForm.BringToFront();
return;
}
// Show existing form instance
_preadjustmentForm.Show(owner);
}
}
#endregion
#region ================================== Create context for PreAdjustmentUI ==================================
/*public interface IGciToolContext
{
IReadOnlyList<int> GetEnabledSlots();
string GetPcbId(int slot);
string GetSerialNumber(int slot);
bool Login(int slot);
bool Logout(int slot);
bool ReadRegister(int slot, int address, out string value);
bool WriteRegister(int slot, int address, string value);
void LogInfo(string message);
void LogError(string message);
event EventHandler<GciMeterChangedEventArgs> MeterChanged;
}*/
#endregion
// GCI to PreadjustmentUI - AUTOMATIC - STANDALONE - Laatzen GUI
#region ================================== PreAdjustmentUI DETECT process ==================================
public Task<PreadjustmentDetectResult> Preadjustment_DetectAsync(
CancellationToken token = default)
{
return Task.Run(() => Preadjustment_DetectCore(token), token);
}
private PreadjustmentDetectResult Preadjustment_DetectCore(
CancellationToken token)
{
const string operation = nameof(Preadjustment_DetectCore);
try
{
MeterBatch globalMeterBatch = new MeterBatch();
MeterBatch thermoMeterBatch = new MeterBatch();
if (_settings == null)
throw new ArgumentNullException(nameof(_settings));
if (_meterControls == null)
throw new ArgumentNullException(nameof(_meterControls));
if (_tempMeterControls == null)
_tempMeterControls = new List<MeterStateControl>();
if (globalMeterBatch.ListOfMeters.Any())
globalMeterBatch.RemoveAllMeters();
if (thermoMeterBatch.ListOfMeters.Any())
thermoMeterBatch.RemoveAllMeters();
globalMeterBatch = _meterBatch;
if (!_settings.GetTempUseTempFlansh())
_tempMeterControls.Clear();
var allMeterControls = new List<MeterStateControl>();
allMeterControls.AddRange(_meterControls);
allMeterControls.AddRange(_tempMeterControls);
CreateZeroFlowMeters(
globalMeterBatch,
thermoMeterBatch,
allMeterControls,
token);
SetEnableOpeningState(allMeterControls);
if (!_meterControls.Any(a => a.EnableOpening))
{
return new PreadjustmentDetectResult
{
Success = false,
DetectedMeterCount = 0,
DetectedThermometerCount = _tempMeterControls.Count(t => t.IsEnabled),
ErrorMessage = "No enabled meter found."
};
}
SetUnknownStatus(allMeterControls);
CheckTemperatureMeters(
_settings,
_tempMeterControls,
token);
return new PreadjustmentDetectResult
{
Success = true,
DetectedMeterCount = _meterControls.Count(m => m.IsEnabled),
DetectedThermometerCount = _tempMeterControls.Count(t => t.IsEnabled)
};
}
catch (Exception ex)
{
return new PreadjustmentDetectResult
{
Success = false,
ErrorMessage = ex.Message
};
}
}
private void CreateZeroFlowMeters(
MeterBatch globalMeterBatch,
MeterBatch thermoMeterBatch,
List<MeterStateControl> allMeterControls,
CancellationToken token)
{
foreach (var meterStateCtl in allMeterControls)
{
token.ThrowIfCancellationRequested();
if (!(meterStateCtl.IsEnabled ||
meterStateCtl is TempMeterStateControl))
{
continue;
}
if (meterStateCtl.Slot == -1)
continue;
ZeroFlowGenesisMeter currentMeter = new ZeroFlowGenesisMeter(meterStateCtl.Slot, 3, !(meterStateCtl is TempMeterStateControl));
var convertedMeter = GetMeterThreadSafe(meterStateCtl.Slot);
if (convertedMeter != null)
{
convertedMeter.CopySafeStateTo(currentMeter);
}
currentMeter.LogOnEnable = true;
currentMeter.LoginFailed = false;
currentMeter.PreparationFailed = false;
currentMeter.AmplitudeFailed = false;
currentMeter.ZeroFlowOffsetFailed = false;
currentMeter.CompletionFailed = false;
currentMeter.Ok = false;
currentMeter.EmptyPipeCheckEnable = false;
currentMeter.EmptyPipeCheckFailed = false;
if (meterStateCtl is TempMeterStateControl)
{
thermoMeterBatch.AddMeter(currentMeter);
meterStateCtl.IsEnabled = true;
}
else
{
if (currentMeter.useConfigSource != ConfigSource.InterfaceInputConfig)
globalMeterBatch.AddMeter(currentMeter);
else
globalMeterBatch.AddMeter2(currentMeter);
}
meterStateCtl.Meter = currentMeter;
}
}
private void SetEnableOpeningState(
List<MeterStateControl> allMeterControls)
{
foreach (var meterState in allMeterControls)
{
meterState.EnableOpening =
meterState.IsEnabled;
}
}
private void SetUnknownStatus(
List<MeterStateControl> allMeterControls)
{
foreach (var meterState in allMeterControls)
{
meterState.SetToUnknownStatus =
!meterState.EnableOpening;
}
}
private void CheckNormalMeters(
List<MeterStateControl> meterControls,
CancellationToken token)
{
foreach (var meterCtl in meterControls)
{
token.ThrowIfCancellationRequested();
if (meterCtl != null && meterCtl.IsEnabled)
{
meterCtl.Ok =
meterCtl.Meter.CheckRequestPort() &&
meterCtl.Meter.CheckStreamingPort();
}
}
}
private void CheckTemperatureMeters(
PreAdjustmentSettingsContainer settings,
List<MeterStateControl> tempMeterControls,
CancellationToken token)
{
if (settings.GetTempUseManualInput())
return;
foreach (var meterCtl in tempMeterControls)
{
token.ThrowIfCancellationRequested();
if (meterCtl != null && meterCtl.IsEnabled)
{
meterCtl.Ok =
meterCtl.Meter.CheckStreamingPort();
}
}
}
#endregion
#region ================================== PreAdjustmentUI PREPARATION process ==================================
public Task<PreadjustmentProcessResult> Preadjustment_PreparationAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(
() => Preadjustment_Preparation(slot, pp),
token);
}
/// <summary>
/// Executes standalone preparation process.
/// </summary>
public PreadjustmentProcessResult Preadjustment_Preparation(
int slot,
ProcessProgress pp)
{
const string operation = nameof(Preadjustment_Preparation);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
EnsureConnected(meter);
BaseProcess process =
CreatePreparationProcess(pp);
bool success =
ExecuteProcess(process, pp);
var result =
new PreadjustmentProcessResult
{
Success = success,
Slot = slot,
ProcessName = process.ProcessName,
ErrorMessage = success
? null
: process.FailedMessage
};
LogInfo(operation, result.ToString());
return result;
}
catch (Exception ex)
{
LogError(operation, ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Preparation",
ErrorMessage = ex.Message
};
}
}
/// <summary>
/// Creates preparation process instance based on current configuration.
/// </summary>
/// <param name="pp">Current process progress context</param>
/// <returns>Configured preparation process</returns>
private BaseProcess CreatePreparationProcess(ProcessProgress pp)
{
if (pp.Setting.NumberOfPaths == 1)
{
return new SPPreparationProcess(
"Preparation Single",
PreAdjustmentControl.StatusPanelItems.Prepare,
PreAdjustmentControl.PredefinedMessages.WaitUntilPreparationFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.PreparationFailed(pp.Setting.Culture),
60);
}
return new PreparationProcess(
"Preparation",
PreAdjustmentControl.StatusPanelItems.Prepare,
PreAdjustmentControl.PredefinedMessages.WaitUntilPreparationFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.PreparationFailed(pp.Setting.Culture),
60);
}
#endregion
#region ================================== PreAdjustmentUI AMPLITUDE TEST process ==================================
public Task<PreadjustmentProcessResult> Preadjustment_AmplitudeTestAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(
() => Preadjustment_AmplitudeTest(slot, pp),
token);
}
public PreadjustmentProcessResult Preadjustment_AmplitudeTest(
int slot,
ProcessProgress pp)
{
const string operation = nameof(Preadjustment_AmplitudeTest);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
EnsureConnected(meter);
if (pp.Setting.TempOnly)
{
return new PreadjustmentProcessResult
{
Success = true,
Slot = slot,
ProcessName = "Amplitude Test"
};
}
BaseProcess process = CreateAmplitudeTestProcess(pp);
bool success = ExecuteProcess(process, pp);
LogInfo(operation, $"Finished. Slot={slot}, Success={success}");
return new PreadjustmentProcessResult
{
Success = success,
Slot = slot,
ProcessName = process.ProcessName,
ErrorMessage = success ? null : process.FailedMessage
};
}
catch (Exception ex)
{
LogError(operation, ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Amplitude Test",
ErrorMessage = ex.Message
};
}
}
/// <summary>
/// Creates amplitude test process instance based on current configuration.
/// </summary>
/// <param name="pp">Current process progress context</param>
/// <returns>Configured amplitude test process</returns>
private BaseProcess CreateAmplitudeTestProcess(ProcessProgress pp)
{
if (pp.Setting.NumberOfPaths == 1)
{
return new SPAmplitudeTestProcess(
"Amplitude Test Single",
PreAdjustmentControl.StatusPanelItems.Amplitude,
PreAdjustmentControl.PredefinedMessages.WaitUntilAmplitudeTestFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.AmplitudeFailed(pp.Setting.Culture),
4 * 60);
}
return new AmplitudeTestProcess(
"Amplitude Test",
PreAdjustmentControl.StatusPanelItems.Amplitude,
PreAdjustmentControl.PredefinedMessages.WaitUntilAmplitudeTestFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.AmplitudeFailed(pp.Setting.Culture),
4 * 60);
}
#endregion
#region ================================== PreAdjustmentUI TEMPERATURE CALIBRATION process ==================================
public Task<PreadjustmentProcessResult> Preadjustment_TemperatureCalibrationAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(
() => Preadjustment_TemperatureCalibration(slot, pp),
token);
}
public PreadjustmentProcessResult Preadjustment_TemperatureCalibration(
int slot,
ProcessProgress pp)
{
const string operation =
nameof(Preadjustment_TemperatureCalibration);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter =
GetMeterThreadSafe(slot);
EnsureConnected(meter);
BaseProcess process =
CreateTemperatureCalibrationProcess(pp);
bool success =
ExecuteProcess(process, pp);
var result =
new PreadjustmentProcessResult
{
Success = success,
Slot = slot,
ProcessName = process.ProcessName,
ErrorMessage = success
? null
: process.FailedMessage
};
LogInfo(operation, result.ToString());
return result;
}
catch (Exception ex)
{
LogError(operation, ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Temperature Calibration",
ErrorMessage = ex.Message
};
}
}
/// <summary>
/// Creates temperature calibration process instance based on current configuration.
/// </summary>
/// <param name="pp">Current process progress context</param>
/// <returns>Configured temperature calibration process</returns>
private BaseProcess CreateTemperatureCalibrationProcess(ProcessProgress pp)
{
if (pp.Setting.NumberOfPaths == 1)
{
return new SPTemperatureCalibrationProcess(
"Temperature Calibration Single",
PreAdjustmentControl.StatusPanelItems.TempCal,
PreAdjustmentControl.PredefinedMessages.WaitUntilTemperatureCalibrationFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.TempCalFailed(pp.Setting.Culture),
2 * 60);
}
return new TemperatureCalibrationProcess(
"Temperature Calibration",
PreAdjustmentControl.StatusPanelItems.TempCal,
PreAdjustmentControl.PredefinedMessages.WaitUntilTemperatureCalibrationFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.TempCalFailed(pp.Setting.Culture),
2 * 60);
}
#endregion
#region ================================== PreAdjustmentUI OFFSET TEST process ==================================
public Task<PreadjustmentProcessResult> Preadjustment_OffsetTestAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(
() => Preadjustment_OffsetTest(slot, pp),
token);
}
public PreadjustmentProcessResult Preadjustment_OffsetTest(
int slot,
ProcessProgress pp)
{
const string operation =
nameof(Preadjustment_OffsetTest);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter =
GetMeterThreadSafe(slot);
EnsureConnected(meter);
if (pp.Setting.TempOnly)
{
return new PreadjustmentProcessResult
{
Success = true,
Slot = slot,
ProcessName = "Offset Test"
};
}
BaseProcess process =
CreateOffsetTestProcess(pp);
bool success =
ExecuteProcess(process, pp);
var result =
new PreadjustmentProcessResult
{
Success = success,
Slot = slot,
ProcessName = process.ProcessName,
ErrorMessage = success
? null
: process.FailedMessage
};
LogInfo(operation, result.ToString());
return result;
}
catch (Exception ex)
{
LogError(operation, ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Offset Test",
ErrorMessage = ex.Message
};
}
}
/// <summary>
/// Creates offset test process instance based on current configuration.
/// </summary>
/// <param name="pp">Current process progress context</param>
/// <returns>Configured offset test process</returns>
private BaseProcess CreateOffsetTestProcess(ProcessProgress pp)
{
if (pp.Setting.NumberOfPaths == 1)
{
return new SPOffsetTestProcess(
"Offset Test Single",
PreAdjustmentControl.StatusPanelItems.Offset,
PreAdjustmentControl.PredefinedMessages.WaitUntilZeroflowOffsetTestFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.ZeroflowOffsetTestFailed(pp.Setting.Culture),
18 * 60);
}
return new OffsetTestProcess(
"Offset Test",
PreAdjustmentControl.StatusPanelItems.Offset,
PreAdjustmentControl.PredefinedMessages.WaitUntilZeroflowOffsetTestFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.ZeroflowOffsetTestFailed(pp.Setting.Culture),
18 * 60);
}
#endregion
#region ================================== PreAdjustmentUI COMPLETION process ==================================
public Task<PreadjustmentProcessResult> Preadjustment_CompletionAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(
() => Preadjustment_Completion(slot, pp),
token);
}
public PreadjustmentProcessResult Preadjustment_Completion(
int slot,
ProcessProgress pp)
{
const string operation =
nameof(Preadjustment_Completion);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter =
GetMeterThreadSafe(slot);
EnsureConnected(meter);
BaseProcess process =
CreateCompletionProcess(pp);
bool success =
ExecuteProcess(process, pp);
var result =
new PreadjustmentProcessResult
{
Success = success,
Slot = slot,
ProcessName = process.ProcessName,
ErrorMessage = success
? null
: process.FailedMessage
};
LogInfo(operation, result.ToString());
return result;
}
catch (Exception ex)
{
LogError(operation, ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Completion",
ErrorMessage = ex.Message
};
}
}
/// <summary>
/// Creates completion process instance based on current configuration.
/// </summary>
/// <param name="pp">Current process progress context</param>
/// <returns>Configured completion process</returns>
private BaseProcess CreateCompletionProcess(ProcessProgress pp)
{
if (pp.Setting.NumberOfPaths == 1)
{
return new SPCompletionProcess(
"Completion Single",
PreAdjustmentControl.StatusPanelItems.Completion,
PreAdjustmentControl.PredefinedMessages.WaitUntilCompletionFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.CompletionFailed(pp.Setting.Culture),
1 * 60);
}
return new CompletionProcess(
"Completion",
PreAdjustmentControl.StatusPanelItems.Completion,
PreAdjustmentControl.PredefinedMessages.WaitUntilCompletionFinished(pp.Setting.Culture),
PreAdjustmentControl.PredefinedMessages.CompletionFailed(pp.Setting.Culture),
1 * 60);
}
#endregion
#region ================================== PreAdjustmentUI ExecuteProcess ==================================
private bool ExecuteProcess(
BaseProcess process,
ProcessProgress pp)
{
pp.IsBusy = true;
process.StartProcess(
pp,
_meterControls,
_tempMeterControls);
while (pp.IsBusy && !pp.StopSequence)
{
Thread.Sleep(50);
}
foreach (var meterCtrl in _meterControls)
{
if (meterCtrl.IsEnabled && meterCtrl.Failed)
{
pp.GenerateReturnNote(process.FailedMessage, meterCtrl);
meterCtrl.IsEnabled = false;
}
}
if (pp.StopSequence)
return false;
return _meterControls.Any(m => m.IsEnabled && !m.Failed);
}
#endregion
}
}

View File

@ -1,8 +1,12 @@
using System;
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using static GenesisCordonelInterface.API.PublicModels;
namespace GenesisCordonelInterface.API
@ -22,6 +26,8 @@ namespace GenesisCordonelInterface.API
_innerMeterAPI = new InterfaceGCIToLaatzen();
}
//
#region ================================== PORT DETECTION ==================================
public PortDetectionResult DetectStreamingPort(int slot)
@ -424,7 +430,6 @@ namespace GenesisCordonelInterface.API
#endregion
#region ================================== METER BATCH SETUP ==================================
// ----------------------------------------------------
public void ReloadSlotSetup()
{
@ -438,12 +443,63 @@ namespace GenesisCordonelInterface.API
RaiseMeterBatchStatusChanged();
}
// ----------------------------------------------------
#endregion
#region ================================== Register names ==================================
public List<string> GetAllRegisterNames()
{
return _innerMeterAPI.GetAllRegisterNames();
}
#endregion
// Preadjustment
public Task<PreadjustmentDetectResult> Preadjustment_DetectAsync(
CancellationToken token = default)
{
return _innerMeterAPI.Preadjustment_DetectAsync(token);
}
public Task<PreadjustmentProcessResult> Preadjustment_PreparationAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return _innerMeterAPI.Preadjustment_PreparationAsync(slot, pp, token);
}
public Task<PreadjustmentProcessResult> Preadjustment_AmplitudeTestAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return _innerMeterAPI.Preadjustment_AmplitudeTestAsync(slot, pp, token);
}
public Task<PreadjustmentProcessResult> Preadjustment_TemperatureCalibrationAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return _innerMeterAPI.Preadjustment_TemperatureCalibrationAsync(slot, pp, token);
}
public Task<PreadjustmentProcessResult> Preadjustment_OffsetTestAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return _innerMeterAPI.Preadjustment_OffsetTestAsync(slot, pp, token);
}
public Task<PreadjustmentProcessResult> Preadjustment_CompletionAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
return _innerMeterAPI.Preadjustment_CompletionAsync(slot, pp, token);
}
}
}

View File

@ -447,6 +447,42 @@ namespace GenesisCordonelInterface.API
Message);
}
}
public class PreadjustmentProcessResult
{
public bool Success { get; set; }
public int Slot { get; set; }
public string ProcessName { get; set; }
public string ErrorMessage { get; set; }
/// <inheritdoc />
public override string ToString()
{
return
$"Success={Success}, " +
$"Slot={Slot}, " +
$"Process={ProcessName}, " +
$"Error={ErrorMessage ?? "None"}";
}
}
public class PreadjustmentDetectResult
{
public bool Success { get; set; }
public int DetectedMeterCount { get; set; }
public int DetectedThermometerCount { get; set; }
public string ErrorMessage { get; set; }
/// <inheritdoc />
public override string ToString()
{
return
$"Success={Success}, " +
$"Meters={DetectedMeterCount}, " +
$"Thermometers={DetectedThermometerCount}, " +
$"Error={ErrorMessage ?? "None"}";
}
}
#endregion
}
}

View File

@ -25,6 +25,15 @@ namespace GenesisCordonelInterface.Core.Threading
public static class RetryWorker
{
/// <summary>
/// Executes an asynchronous operation with retry and timeout protection.
///
/// Important:
/// The timeout here protects the caller from waiting forever, but it does not forcibly abort
/// the underlying hardware operation. Because hardware/COM calls may continue running after
/// the timeout signal, this method waits for the original action to finish before starting
/// the next retry. This prevents overlapping COM requests on the same device.
/// </summary>
public static async Task<RetryResult<T>> RunWithRetryAsync<T>(
Func<Task<T>> action,
Func<T, bool> isSuccess,
@ -41,21 +50,53 @@ namespace GenesisCordonelInterface.Core.Threading
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
var actionTask = action();
var timeoutTask = Task.Delay(timeoutMs);
Task<T> actionTask = null;
var completedTask = await Task.WhenAny(actionTask, timeoutTask);
if (completedTask == timeoutTask)
try
{
timedOut = true;
log?.Invoke($"{operationName} timeout attempt {attempt}/{maxAttempts}");
}
else
{
lastResult = await actionTask;
log?.Invoke($"{operationName} started. Attempt {attempt}/{maxAttempts}");
if (isSuccess(lastResult))
// Start real operation, for example Connect/GetPcbId/Login.
actionTask = action();
// Start independent timeout timer.
var timeoutTask = Task.Delay(timeoutMs);
// Wait until either operation completes or timeout expires.
var completedTask = await Task.WhenAny(actionTask, timeoutTask)
.ConfigureAwait(false);
if (completedTask == timeoutTask)
{
timedOut = true;
log?.Invoke(
$"{operationName} timed out. Attempt {attempt}/{maxAttempts}. " +
"Waiting for the running operation to finish before retry.");
// Critical part:
// Do NOT immediately start another retry.
// The hardware operation may still be active in ApiWorker.
// Starting another attempt immediately could corrupt communication.
try
{
lastResult = await actionTask.ConfigureAwait(false);
}
catch (Exception ex)
{
log?.Invoke(
$"{operationName} finished after timeout with exception: " +
$"{ex.GetType().Name}: {ex.Message}");
}
}
else
{
// Operation completed before timeout.
lastResult = await actionTask.ConfigureAwait(false);
}
// Decide whether the returned result means success.
if (lastResult != null && isSuccess(lastResult))
{
return new RetryResult<T>
{
@ -63,15 +104,29 @@ namespace GenesisCordonelInterface.Core.Threading
Success = true,
Attempts = attempt,
Duration = DateTime.Now - started,
TimedOut = false
TimedOut = timedOut
};
}
logResult?.Invoke($"{operationName} failed attempt {attempt}/{maxAttempts}", lastResult);
logResult?.Invoke(
$"{operationName} failed. Attempt {attempt}/{maxAttempts}",
lastResult);
}
catch (Exception ex)
{
// Covers exceptions thrown before timeout handling or by action startup.
log?.Invoke(
$"{operationName} exception on attempt {attempt}/{maxAttempts}: " +
$"{ex.GetType().Name}: {ex.Message}");
}
// Delay before next retry, except after the final attempt.
if (attempt < maxAttempts)
await Task.Delay(delayMs);
{
log?.Invoke($"{operationName} waiting {delayMs} ms before retry.");
await Task.Delay(delayMs).ConfigureAwait(false);
}
}
return new RetryResult<T>

View File

@ -1,4 +1,5 @@
using Newtonsoft.Json;
using CordonelPreadjustmentUi;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Drawing;
@ -10,18 +11,26 @@ using System.Windows.Forms;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using CordonelPreadjustmentUi;
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
using System.Linq;//...MF
namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
{
public partial class FrmCordonelPreadjustmentUI : Form
{
private PreAdjustmentControl preadjustCtl;
public PreAdjustmentControl preadjustCtl;//...MF
private PreAdjustmentSettingsContainer mainSettings = new PreAdjustmentSettingsContainer();
public MeterBatch _externMetersBatch;
public FrmCordonelPreadjustmentUI()
{
}
public FrmCordonelPreadjustmentUI(MeterBatch externMetersBatch)
{
_externMetersBatch = externMetersBatch;
InitializeComponent();
}
@ -29,7 +38,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
{
preadjustCtl = new PreAdjustmentControl(mainSettings);
preadjustCtl = new PreAdjustmentControl(mainSettings, _externMetersBatch);
tab_ZeroFlowCal.Controls.Add(preadjustCtl);
@ -71,7 +80,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
{
if (e.TabPage.Name == tab_ZeroFlowCal.Name)
{
try
/*try
{
var _serialConfigFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Genesis", ProgramConfig.SerialConfigFileName);
@ -99,11 +108,81 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
catch (Exception ex)
{
MessageBox.Show($"use default meters because of {ex.Message}");
}*/
try //...MF
{
mainSettings.Meters = new List<int>();
mainSettings.TempMeters = new List<int>();
// ==========================================
// Try loading configuration from GCI meters
// ==========================================
bool loadedFromInterface = false;
if (_externMetersBatch != null &&
_externMetersBatch.ListOfMeters.Any())
{
foreach (GenesisMeter meter in _externMetersBatch.ListOfMeters)
{
// Skip meters that should use file configuration
if (meter.useConfigSource != ConfigSource.InterfaceInputConfig)
continue;
loadedFromInterface = true;
// Split normal and temperature meters
//if (meter.Type == SlotType.TemperatureMeter)
//{
// mainSettings.TempMeters.Add(meter.Slot);
//}
//else
//{
mainSettings.Meters.Add(meter.Slot);
//}
}
}
// ==========================================
// Fallback to configuration file
// ==========================================
if (!loadedFromInterface)
{
var _serialConfigFile =
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"Genesis",
ProgramConfig.SerialConfigFileName);
SlotConfig[] meterConfigList;
using (var tr = new StreamReader(_serialConfigFile))
{
var _fileString = tr.ReadToEnd();
meterConfigList =
JsonConvert.DeserializeObject<SlotConfig[]>(_fileString);
}
foreach (var item in meterConfigList)
{
if (item.Type != SlotType.TemperatureMeter)
{
mainSettings.Meters.Add(item.Slot);
}
else
{
mainSettings.TempMeters.Add(item.Slot);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Use default meters because of {ex.Message}");
}
mainSettings.NumberOfPaths = cB_SinglePath.Checked ? 1 : 3;
mainSettings.LowerTempLimit = (double)nUD_SettingsTempMonitorLowerValue.Value;
@ -156,7 +235,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
/*private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (preadjustCtl != null)
{
@ -177,7 +256,68 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
}
}*/
/// <summary>
/// Raised when user closes the preadjustment window
/// using the window close button (X).
///
/// Note:
/// Form is hidden, not disposed.
/// This event allows external code to continue
/// workflow asynchronously.
/// </summary>
public event EventHandler OnUserClosed;
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
// User clicked X button - hide form only
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
this.Hide();
OnUserClosed?.Invoke(this, EventArgs.Empty);
return;
}
// Real application shutdown / dispose
DisposeResources();
}
/// <summary>
/// Dispose all internal resources that should
/// only be released during real application shutdown.
///
/// Do not call when form is hidden.
/// </summary>
private void DisposeResources()
{
if (preadjustCtl != null)
{
preadjustCtl.CloseConnections();
preadjustCtl.Dispose();
preadjustCtl = null;
}
if (ThermoMeterBatch != null)
{
ThermoMeterBatch.Dispose();
ThermoMeterBatch = null;
}
if (TempMeterStateCtls != null)
{
foreach (var item in TempMeterStateCtls)
{
item.Dispose();
}
TempMeterStateCtls.Clear();
TempMeterStateCtls = null;
}
}
private MeterBatch ThermoMeterBatch = new MeterBatch();
private List<MeterStateControl> TempMeterStateCtls = new List<MeterStateControl>();
private void tmpStart(int slot, bool RaspiMode = false)

View File

@ -1,4 +1,5 @@
using CordonelPreadjustmentUi.Processes;
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes;
using CordonelPreadjustmentUi.Processes.Actions;
using CordonelPreadjustmentUi.Processes.Itinerary;
using System;
@ -6,16 +7,18 @@ using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using Xylem.Common.Ui.CordonelPreadjustmentUi.Parameters;
using CordonelPreadjustmentUi;
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
namespace GenesisCordonelInterface.UI
{
@ -41,11 +44,18 @@ namespace GenesisCordonelInterface.UI
private Boolean abortIndicator = false;
public Boolean AbortIndicator { get { return abortIndicator; } set { abortIndicator = value; } }
public int TestRunNumber;
public PreAdjustmentControl(PreAdjustmentSettingsContainer Settings = null)
public PreAdjustmentControl(PreAdjustmentSettingsContainer Settings = null, MeterBatch externalMetersBatch = null)//...MF
{
InitializeComponent();
SetSettings(Settings);
rTB_ZeroFlowCal.AutoSize = true;
//...MF
// Use external batch only if supplied
if (externalMetersBatch != null)
{
GlobalMeterBatch = externalMetersBatch;
}
}
public void SetSettings(PreAdjustmentSettingsContainer Settings = null)
{
@ -120,6 +130,49 @@ namespace GenesisCordonelInterface.UI
}
cb_Metersize.SelectedItem = setM;
//...MF
// Create UI controls for configured meter slots.
//
// Flow:
//
// settings.Meters
// ↓
// create MeterStateControl
// ↓
// position control in UI
// ↓
// check whether slot exists in externally supplied MeterBatch
// ↓
// automatically enable corresponding checkbox
// ↓
// register UI events
// ↓
// add control into internal collection and group box
//
// Notes:
// - allows external GCI workflow to preselect meters
// - keeps UI synchronized with externally injected MeterBatch
// - slots contained in GlobalMeterBatch are automatically checked
//
foreach (var Meter in settings.Meters)
{
var ctl = new MeterStateControl(Meter);
ctl.Location = new Point(5 + ((tmpI - 1) * ctl.Width), 15);
// Check meter if it exists in external MeterBatch
if (GlobalMeterBatch != null &&
GlobalMeterBatch.ListOfMeters != null &&
GlobalMeterBatch.ListOfMeters.Any(m => m.Slot == Meter))
{
ctl.SetChecked(true);
}
MeterStateCtls.Add(ctl);
gB_Meters.Controls.Add(ctl);
ctl.OnRequestDetails += Ctl_MouseEnter;
tmpI = tmpI + 1;
}
}
}
@ -1694,13 +1747,46 @@ namespace GenesisCordonelInterface.UI
List<IProcess> listOfProgrammParts = new List<IProcess>();
//...MF
bool requiresInternalLoginFlow = true;
requiresInternalLoginFlow =
GlobalMeterBatch.ListOfMeters.All(
m =>
{
var meter = m as GenesisMeter;
return meter == null ||
meter.usePasswordSource !=
PasswordSource.InterfaceInputPassword;
});
listOfProgrammParts.Add(new PrepareTestProcess("PrepareTest", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, string.Empty, string.Empty, 1 * 60));
//...MF
bool requiresInternalConfigFlow = true;
requiresInternalConfigFlow =
GlobalMeterBatch.ListOfMeters.All(
m =>
{
var meter = m as GenesisMeter;
listOfProgrammParts.Add(new LoginProcess("Login", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, PredefinedMessages.WaitUntilLoginFinished(pp.Setting.Culture), PredefinedMessages.LoginFailed(pp.Setting.Culture), 1 * 60));
return meter == null ||
meter.useConfigSource !=
ConfigSource.InterfaceInputConfig;
});
listOfProgrammParts.Add(new FlushProcess("First Flush", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, PredefinedMessages.WaitUntilFlushFinished(pp.Setting.Culture), string.Empty, 2 * 60));
if (requiresInternalConfigFlow)//...MF
{
listOfProgrammParts.Add(new PrepareTestProcess("PrepareTest", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, string.Empty, string.Empty, 1 * 60));
}
if (requiresInternalLoginFlow)//...MF
{
listOfProgrammParts.Add(new LoginProcess("Login", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, PredefinedMessages.WaitUntilLoginFinished(pp.Setting.Culture), PredefinedMessages.LoginFailed(pp.Setting.Culture), 1 * 60));
}
if (requiresInternalConfigFlow)//...MF
{
listOfProgrammParts.Add(new FlushProcess("First Flush", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, PredefinedMessages.WaitUntilFlushFinished(pp.Setting.Culture), string.Empty, 2 * 60));
}
//listOfProgrammParts.Add(new PressureTestProcess("PreussureTest", StatusPanelItems.Prepare, PredefinedMessages.WaitUntilPreparationFinished(pp.Setting.Culture), PredefinedMessages.PreparationFailed(pp.Setting.Culture), 1 * 60));

View File

@ -1,4 +1,7 @@
using GenesisCordonelInterface.API;
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary;
using GenesisCordonelInterface.API;
using GenesisCordonelInterface.Core.Threading;
using log4net;
///
/// Copyright (c) 2015-2021 Sensus Slovensko a.s.
@ -13,6 +16,9 @@ using TBF.Rig.BridgeComponents.GciBridge.UI;
using TBF.Rig.Generic;
using TBF.Rig.Input.DataStorage.UniDataStorageReader;
using TBF.Rig.Input.DataStorage.UniDataStorageReader.Searching.Database;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using static GenesisCordonelInterface.API.PublicModels;
using static TBF.Rig.BridgeComponents.GciBridge.Interfaces.PublicModels;
using GciGUIType = GenesisCordonelInterface.UI.MainView;
using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
@ -20,7 +26,6 @@ using GciType = GenesisCordonelInterface.API.InterfaceOutsideToGCI;
using UdsReaderType = TBF.Rig.Input.DataStorage.UniDataStorageReader.Reader;
using UDSRPublicModels = TBF.Rig.Input.DataStorage.UniDataStorageReader.Interfaces.PublicModels;
using UdsWriterType = TBF.Rig.Output.DataStorage.UniDataStorageWriter.Writer;
using GenesisCordonelInterface.Core.Threading;
namespace TBF.Rig.BridgeComponents.GciBridge
{
@ -47,8 +52,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
Form gciGuiHostForm;
//diag GUI for GciBridge
public UserControl gciBridgeGUI;
public MainForm gciBridgeGuiForm;
public UserControl gciBridgeGUIUserControl;
public UI.MainForm gciBridgeGuiForm;
public GciType gciExternalInterface;
@ -119,7 +124,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (gciBridgeGuiForm != null && !gciBridgeGuiForm.IsDisposed)
return;
gciBridgeGuiForm = new MainForm(this);
gciBridgeGuiForm = new UI.MainForm(this);
gciBridgeGuiForm.Text = "Gci Bridge GUI";
gciBridgeGuiForm.Width = 1300;
gciBridgeGuiForm.Height = 600;
@ -1417,6 +1422,334 @@ namespace TBF.Rig.BridgeComponents.GciBridge
#endregion
// Preadjustment API:
#region ================================== Preadjustment DETECT bridge ==================================
public async Task<PreadjustmentDetectResult> Preadjustment_DetectAsync(
CancellationToken token = default)
{
const string operation = nameof(Preadjustment_DetectAsync);
try
{
EnsureExternalInterface();
log.InfoFormat("{0}: {1} Start.", Name, operation);
PreadjustmentDetectResult result =
await gciExternalInterface
.Preadjustment_DetectAsync(token)
.ConfigureAwait(false);
log.InfoFormat(
"{0}: {1} Finish. {2}",
Name,
operation,
result);
return result;
}
catch (Exception ex)
{
log.Error(
string.Format("{0}: {1} failed.", Name, operation),
ex);
return new PreadjustmentDetectResult
{
Success = false,
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment PREPARATION bridge ==================================
public async Task<PreadjustmentProcessResult> Preadjustment_PreparationAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
const string operation = nameof(Preadjustment_PreparationAsync);
try
{
EnsureExternalInterface();
if (pp == null)
throw new ArgumentNullException(nameof(pp));
log.InfoFormat(
"{0}: {1} Start. Slot={2}",
Name,
operation,
slot);
PreadjustmentProcessResult result =
await gciExternalInterface
.Preadjustment_PreparationAsync(
slot,
pp,
token)
.ConfigureAwait(false);
log.InfoFormat(
"{0}: {1} Finish. {2}",
Name,
operation,
result);
return result;
}
catch (Exception ex)
{
log.Error(
string.Format("{0}: {1} failed.", Name, operation),
ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Preparation",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment AMPLITUDE TEST bridge ==================================
public async Task<PreadjustmentProcessResult> Preadjustment_AmplitudeTestAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
const string operation = nameof(Preadjustment_AmplitudeTestAsync);
try
{
EnsureExternalInterface();
if (pp == null)
throw new ArgumentNullException(nameof(pp));
log.InfoFormat(
"{0}: {1} Start. Slot={2}",
Name,
operation,
slot);
PreadjustmentProcessResult result =
await gciExternalInterface
.Preadjustment_AmplitudeTestAsync(
slot,
pp,
token)
.ConfigureAwait(false);
log.InfoFormat(
"{0}: {1} Finish. {2}",
Name,
operation,
result);
return result;
}
catch (Exception ex)
{
log.Error(
string.Format("{0}: {1} failed.", Name, operation),
ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Amplitude Test",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment TEMPERATURE CALIBRATION bridge ==================================
public async Task<PreadjustmentProcessResult> Preadjustment_TemperatureCalibrationAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
const string operation = nameof(Preadjustment_TemperatureCalibrationAsync);
try
{
EnsureExternalInterface();
if (pp == null)
throw new ArgumentNullException(nameof(pp));
log.InfoFormat(
"{0}: {1} Start. Slot={2}",
Name,
operation,
slot);
PreadjustmentProcessResult result =
await gciExternalInterface
.Preadjustment_TemperatureCalibrationAsync(
slot,
pp,
token)
.ConfigureAwait(false);
log.InfoFormat(
"{0}: {1} Finish. {2}",
Name,
operation,
result);
return result;
}
catch (Exception ex)
{
log.Error(
string.Format("{0}: {1} failed.", Name, operation),
ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Temperature Calibration",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment OFFSET TEST bridge ==================================
public async Task<PreadjustmentProcessResult> Preadjustment_OffsetTestAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
const string operation = nameof(Preadjustment_OffsetTestAsync);
try
{
EnsureExternalInterface();
if (pp == null)
throw new ArgumentNullException(nameof(pp));
log.InfoFormat(
"{0}: {1} Start. Slot={2}",
Name,
operation,
slot);
PreadjustmentProcessResult result =
await gciExternalInterface
.Preadjustment_OffsetTestAsync(
slot,
pp,
token)
.ConfigureAwait(false);
log.InfoFormat(
"{0}: {1} Finish. {2}",
Name,
operation,
result);
return result;
}
catch (Exception ex)
{
log.Error(
string.Format("{0}: {1} failed.", Name, operation),
ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Offset Test",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment COMPLETION bridge ==================================
public async Task<PreadjustmentProcessResult> Preadjustment_CompletionAsync(
int slot,
ProcessProgress pp,
CancellationToken token = default)
{
const string operation = nameof(Preadjustment_CompletionAsync);
try
{
EnsureExternalInterface();
if (pp == null)
throw new ArgumentNullException(nameof(pp));
log.InfoFormat(
"{0}: {1} Start. Slot={2}",
Name,
operation,
slot);
PreadjustmentProcessResult result =
await gciExternalInterface
.Preadjustment_CompletionAsync(
slot,
pp,
token)
.ConfigureAwait(false);
log.InfoFormat(
"{0}: {1} Finish. {2}",
Name,
operation,
result);
return result;
}
catch (Exception ex)
{
log.Error(
string.Format("{0}: {1} failed.", Name, operation),
ex);
return new PreadjustmentProcessResult
{
Success = false,
Slot = slot,
ProcessName = "Completion",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ======================================= Helpers =======================================
private UDSRPublicModels.DataQuery CreatePasswordQuery(string pcbId)
{

View File

@ -15,6 +15,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.Debug
private MainForm _mainform;
private MainView _mainView;
public Grid.MeterGridManager _gridManager;
public event Action GetMeterRegistersClicked;
private bool isRefreshing;
private List<string> comPorts = new List<string>();
@ -36,6 +37,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.Debug
_mainView._gciApi.MeterBatchStatusChanged += Api_MeterBatchStatusChanged; //global event of table
grid.CellContentClick += grid_CellContentClick;
UpdateGrid(_api.GetMeterBatchDebugStatuses());
}
@ -267,8 +271,16 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.Debug
if (columnName == "Selected")
{
bool selected = Convert.ToBoolean(grid.Rows[e.RowIndex].Cells["Selected"].Value);
bool selected =
Convert.ToBoolean(
grid.Rows[e.RowIndex]
.Cells["Selected"]
.Value);
_api.SetSlotSelected(slot, selected);
//SelectedSlotsChanged?.Invoke();
return;
}
@ -302,13 +314,14 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.Debug
if (columnName == "DetectRequest")
{
await DetectRequestPortAsync(slot);
//await DetectRequestPortAsync(slot);
GetMeterRegistersClicked?.Invoke();
return;
}
if (columnName == "DetectStreaming")
{
await DetectStreamingPortAsync(slot);
//await DetectStreamingPortAsync(slot);
return;
}
}

View File

@ -1,4 +1,5 @@
using System;
using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

View File

@ -25,7 +25,7 @@
private System.Windows.Forms.Button btnRegisterStore;
private System.Windows.Forms.Button btnPulseSetup;
private System.Windows.Forms.Button preadjustmentButton;
private System.Windows.Forms.Button btnMetersAction;
private System.Windows.Forms.Button slotsComPortsRegistersActionsViewButton;
private System.Windows.Forms.SplitContainer splitWorkArea;
private System.Windows.Forms.Panel pnlGciViewHost;
@ -43,9 +43,11 @@
this.btnRegisterStore = new System.Windows.Forms.Button();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.groupBox5 = new System.Windows.Forms.GroupBox();
this.btnMetersAction = new System.Windows.Forms.Button();
this.storageActionButton = new System.Windows.Forms.Button();
this.combinedActionButton = new System.Windows.Forms.Button();
this.ScenariousViewButton = new System.Windows.Forms.Button();
this.preadjustmenActionsViewButton = new System.Windows.Forms.Button();
this.slotsComPortsRegistersActionsViewButton = new System.Windows.Forms.Button();
this.uniDataSorageActionsViewButton = new System.Windows.Forms.Button();
this.combinedActionsViewButton = new System.Windows.Forms.Button();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.btnDiagTablesConfigurationButton = new System.Windows.Forms.Button();
this.pnlMain = new System.Windows.Forms.Panel();
@ -104,7 +106,6 @@
//
this.tabPage1.Controls.Add(this.groupBox2);
this.tabPage1.Controls.Add(this.groupBox1);
this.tabPage1.Enabled = false;
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
@ -189,45 +190,66 @@
//
// groupBox5
//
this.groupBox5.Controls.Add(this.btnMetersAction);
this.groupBox5.Controls.Add(this.storageActionButton);
this.groupBox5.Controls.Add(this.combinedActionButton);
this.groupBox5.Controls.Add(this.ScenariousViewButton);
this.groupBox5.Controls.Add(this.preadjustmenActionsViewButton);
this.groupBox5.Controls.Add(this.slotsComPortsRegistersActionsViewButton);
this.groupBox5.Controls.Add(this.uniDataSorageActionsViewButton);
this.groupBox5.Controls.Add(this.combinedActionsViewButton);
this.groupBox5.Location = new System.Drawing.Point(6, 85);
this.groupBox5.Name = "groupBox5";
this.groupBox5.Size = new System.Drawing.Size(166, 131);
this.groupBox5.Size = new System.Drawing.Size(166, 202);
this.groupBox5.TabIndex = 3;
this.groupBox5.TabStop = false;
this.groupBox5.Text = "Interface";
this.groupBox5.Text = "Interface testing";
//
// btnMetersAction
// ScenariousViewButton
//
this.btnMetersAction.Location = new System.Drawing.Point(6, 19);
this.btnMetersAction.Name = "btnMetersAction";
this.btnMetersAction.Size = new System.Drawing.Size(150, 30);
this.btnMetersAction.TabIndex = 1;
this.btnMetersAction.Text = "GenesisCordonelInterface";
this.btnMetersAction.UseVisualStyleBackColor = true;
this.btnMetersAction.Click += new System.EventHandler(this.btnMetersAction_Click);
this.ScenariousViewButton.Location = new System.Drawing.Point(6, 163);
this.ScenariousViewButton.Name = "ScenariousViewButton";
this.ScenariousViewButton.Size = new System.Drawing.Size(150, 30);
this.ScenariousViewButton.TabIndex = 3;
this.ScenariousViewButton.Text = "Scenarious";
this.ScenariousViewButton.UseVisualStyleBackColor = true;
//
// storageActionButton
// preadjustmenActionsViewButton
//
this.storageActionButton.Location = new System.Drawing.Point(6, 55);
this.storageActionButton.Name = "storageActionButton";
this.storageActionButton.Size = new System.Drawing.Size(150, 30);
this.storageActionButton.TabIndex = 0;
this.storageActionButton.Text = "UniDataStorageReader";
this.storageActionButton.UseVisualStyleBackColor = true;
this.storageActionButton.Click += new System.EventHandler(this.button1_Click_1);
this.preadjustmenActionsViewButton.Location = new System.Drawing.Point(6, 127);
this.preadjustmenActionsViewButton.Name = "preadjustmenActionsViewButton";
this.preadjustmenActionsViewButton.Size = new System.Drawing.Size(150, 30);
this.preadjustmenActionsViewButton.TabIndex = 2;
this.preadjustmenActionsViewButton.Text = "Preadjustment";
this.preadjustmenActionsViewButton.UseVisualStyleBackColor = true;
this.preadjustmenActionsViewButton.Click += new System.EventHandler(this.preadjustmenActionsViewButton_Click);
//
// combinedActionButton
// slotsComPortsRegistersActionsViewButton
//
this.combinedActionButton.Location = new System.Drawing.Point(6, 91);
this.combinedActionButton.Name = "combinedActionButton";
this.combinedActionButton.Size = new System.Drawing.Size(150, 30);
this.combinedActionButton.TabIndex = 0;
this.combinedActionButton.Text = "Combined Action";
this.combinedActionButton.UseVisualStyleBackColor = true;
this.combinedActionButton.Click += new System.EventHandler(this.button2_Click);
this.slotsComPortsRegistersActionsViewButton.Location = new System.Drawing.Point(6, 19);
this.slotsComPortsRegistersActionsViewButton.Name = "slotsComPortsRegistersActionsViewButton";
this.slotsComPortsRegistersActionsViewButton.Size = new System.Drawing.Size(150, 30);
this.slotsComPortsRegistersActionsViewButton.TabIndex = 1;
this.slotsComPortsRegistersActionsViewButton.Text = "Slots, ComPorts, Registers";
this.slotsComPortsRegistersActionsViewButton.UseVisualStyleBackColor = true;
this.slotsComPortsRegistersActionsViewButton.Click += new System.EventHandler(this.btnMetersAction_Click);
//
// uniDataSorageActionsViewButton
//
this.uniDataSorageActionsViewButton.Location = new System.Drawing.Point(6, 55);
this.uniDataSorageActionsViewButton.Name = "uniDataSorageActionsViewButton";
this.uniDataSorageActionsViewButton.Size = new System.Drawing.Size(150, 30);
this.uniDataSorageActionsViewButton.TabIndex = 0;
this.uniDataSorageActionsViewButton.Text = "UniDataStorageReader";
this.uniDataSorageActionsViewButton.UseVisualStyleBackColor = true;
this.uniDataSorageActionsViewButton.Click += new System.EventHandler(this.button1_Click_1);
//
// combinedActionsViewButton
//
this.combinedActionsViewButton.Location = new System.Drawing.Point(6, 91);
this.combinedActionsViewButton.Name = "combinedActionsViewButton";
this.combinedActionsViewButton.Size = new System.Drawing.Size(150, 30);
this.combinedActionsViewButton.TabIndex = 0;
this.combinedActionsViewButton.Text = "Combined actions";
this.combinedActionsViewButton.UseVisualStyleBackColor = true;
this.combinedActionsViewButton.Click += new System.EventHandler(this.button2_Click);
//
// groupBox3
//
@ -386,9 +408,11 @@
this.ResumeLayout(false);
}
private System.Windows.Forms.Button storageActionButton;
private System.Windows.Forms.Button uniDataSorageActionsViewButton;
private System.Windows.Forms.GroupBox groupBox5;
private System.Windows.Forms.Button combinedActionButton;
private System.Windows.Forms.Button combinedActionsViewButton;
private System.Windows.Forms.Button btnDiagTablesConfigurationButton;
private System.Windows.Forms.Button preadjustmenActionsViewButton;
private System.Windows.Forms.Button ScenariousViewButton;
}
}

View File

@ -72,58 +72,11 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI
_uiLogFlushTimer.Interval = 250;
_uiLogFlushTimer.Tick += UiLogFlushTimer_Tick;
_uiLogFlushTimer.Start();
preadjustmentButton.Enabled = true;
groupBox2.Enabled = true;
}
/// <summary>
/// Periodically flushes buffered log messages into RichTextBox.
///
/// Runs on the UI thread because WinForms Timer executes on UI thread.
/// Processes messages in batches to reduce UI overhead.
/// </summary>
/// Plynule pridavanie do mema
/*private void UiLogFlushTimer_Tick(object sender, EventArgs e)
{
if (IsDisposed || !IsHandleCreated)
return;
List<string> messages = new List<string>();
lock (_uiLogLock)
{
while (_pendingUiLogs.Count > 0 && messages.Count < 500)
{
messages.Add(_pendingUiLogs.Dequeue());
}
}
if (messages.Count == 0)
return;
rtbMainLog.SuspendLayout();
try
{
foreach (string msg in messages)
{
AppendLogMessage(msg);
}
const int maxTextLength = 200000;
if (rtbMainLog.TextLength > maxTextLength)
{
rtbMainLog.Select(0, rtbMainLog.TextLength - maxTextLength);
rtbMainLog.SelectedText = "";
}
rtbMainLog.SelectionStart = rtbMainLog.TextLength;
rtbMainLog.ScrollToCaret();
}
finally
{
rtbMainLog.ResumeLayout();
}
}*/
private void UiLogFlushTimer_Tick(object sender, EventArgs e)
{
@ -277,153 +230,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI
base.Dispose(disposing);
}
private IWin32Window DialogOwner
{
get
{
Form owner = FindForm();
return owner ?? (IWin32Window)this;
}
}
private void ShowGciView(Control view)
{
pnlGciViewHost.Controls.Clear();
view.Dock = DockStyle.Fill;
pnlGciViewHost.Controls.Add(view);
}
/// <summary>
/// Saves current slot configuration from the grid into backend storage.
/// </summary>
public void SaveSlots()
{
var data = _batchPanel.GetGridData();
_laatzenApi.SaveSlotSetup(data);
}
/// <summary>
/// Switches currently displayed GCI view inside the host panel.
/// </summary>
private void button2_Click(object sender, EventArgs e)
{
SwitchGciView(
"CombinedInterfaceView",
new CombinedInterfaceView(this, _bridge));
}
private void button1_Click_1(object sender, EventArgs e)
{
SwitchGciView(
"StorageAction",
new UniDataStorageReaderInterfaceView(this, _bridge));
}
#region BUTTONS
private void btnMetersAction_Click(object sender, EventArgs e)
{
SwitchGciView(
"SlotsMetersAction",
new GenesisCordonelInterfaceView(this, _bridge, AddSlotRow, SaveSlots));
}
private void btnSetup_Click(object sender, EventArgs e)
{
/*Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: Setup open");
using (FrmSetup frm = new FrmSetup())
{
frm.ShowDialog(DialogOwner);
}
Logger.Trace("FORM: Setup closed.");*/
}
private void btnRegisterStore_Click(object sender, EventArgs e)
{
/*Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: Register Store open.");
using (FrmRegisterStore frm = new FrmRegisterStore())
{
frm.ShowDialog(DialogOwner);
}
Logger.Trace("FORM: Register Store closed.");*/
}
private void btnPulseSetup_Click(object sender, EventArgs e)
{
/*Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: Pulse Setup open.");
using (FrmConfigurations frm = new FrmConfigurations())
{
frm.ShowDialog(DialogOwner);
}
Logger.Trace("FORM: Pulse Setup closed.");*/
}
private void preadjustmentButton_Click(object sender, EventArgs e)
{
/*Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: Preadjustment open.");
using (FrmCordonelPreadjustmentUI frm = new FrmCordonelPreadjustmentUI())
{
frm.ShowDialog(DialogOwner);
}
Logger.Trace("FORM: Preadjustment closed.");*/
}
private void button1_Click(object sender, EventArgs e)
{
Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: GciBridge GUI open.");
using (var frm = new FrmGCIAPI(_gciApi))
{
frm.ShowDialog(this);
}
Logger.Trace("FORM: GciBridge closed.");
}
private void SwitchGciView(string name, Control view)
{
Logger.Trace("FORM: ---------------------------------");
Logger.Trace($"FORM: GciBridge VIEW -> {name} OPEN");
pnlGciViewHost.Controls.Clear();
view.Dock = DockStyle.Fill;
pnlGciViewHost.Controls.Add(view);
view.BringToFront();
Logger.Trace($"FORM: GciBridge VIEW -> {name} LOADED");
}
private void btnMeterInit_Click(object sender, EventArgs e)
{
SwitchGciView(
"MeterInit",
new MeterInitView(_gciApi, AddSlotRow, SaveSlots));
}
/// <summary>
/// Clears the main UI log window.
/// </summary>
public void ClearLog()
{
rtbMainLog.Clear();
Logger.Trace("Log cleared.");
}
#endregion
#region GLOBAL LOGGING to memo in this view
@ -545,11 +351,164 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI
#endregion
private IWin32Window DialogOwner
{
get
{
Form owner = FindForm();
return owner ?? (IWin32Window)this;
}
}
private void ShowGciView(Control view)
{
pnlGciViewHost.Controls.Clear();
view.Dock = DockStyle.Fill;
pnlGciViewHost.Controls.Add(view);
}
/// <summary>
/// Saves current slot configuration from the grid into backend storage.
/// </summary>
public void SaveSlots()
{
var data = _batchPanel.GetGridData();
_laatzenApi.SaveSlotSetup(data);
}
#region BUTTONS
private void btnSetup_Click(object sender, EventArgs e)
{
/*Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: Setup open");
using (FrmSetup frm = new FrmSetup())
{
frm.ShowDialog(DialogOwner);
}
Logger.Trace("FORM: Setup closed.");*/
}
private void btnRegisterStore_Click(object sender, EventArgs e)
{
/*Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: Register Store open.");
using (FrmRegisterStore frm = new FrmRegisterStore())
{
frm.ShowDialog(DialogOwner);
}
Logger.Trace("FORM: Register Store closed.");*/
}
private void btnPulseSetup_Click(object sender, EventArgs e)
{
/*Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: Pulse Setup open.");
using (FrmConfigurations frm = new FrmConfigurations())
{
frm.ShowDialog(DialogOwner);
}
Logger.Trace("FORM: Pulse Setup closed.");*/
}
private void preadjustmentButton_Click(object sender, EventArgs e)
{
Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: Preadjustment open.");
_laatzenApi.ShowPreadjustmentForm(DialogOwner);
Logger.Trace("FORM: Preadjustment shown.");
}
private void button1_Click(object sender, EventArgs e)
{
Logger.Trace("FORM: ---------------------------------");
Logger.Trace("FORM: GciBridge GUI open.");
using (var frm = new FrmGCIAPI(_gciApi))
{
frm.ShowDialog(this);
}
Logger.Trace("FORM: GciBridge closed.");
}
private void SwitchGciView(string name, Control view)
{
Logger.Trace("FORM: ---------------------------------");
Logger.Trace($"FORM: GciBridge VIEW -> {name} OPEN");
pnlGciViewHost.Controls.Clear();
view.Dock = DockStyle.Fill;
pnlGciViewHost.Controls.Add(view);
view.BringToFront();
Logger.Trace($"FORM: GciBridge VIEW -> {name} LOADED");
}
private void btnMeterInit_Click(object sender, EventArgs e)
{
SwitchGciView(
"MeterInit",
new MeterInitView(_gciApi, AddSlotRow, SaveSlots));
}
/// <summary>
/// Clears the main UI log window.
/// </summary>
public void ClearLog()
{
rtbMainLog.Clear();
Logger.Trace("Log cleared.");
}
/// <summary>
/// Switches currently displayed GCI view inside the host panel.
/// </summary>
private void button2_Click(object sender, EventArgs e)
{
SwitchGciView(
"CombinedActionsView",
new CombinedActionsView(this, _bridge));
}
private void button1_Click_1(object sender, EventArgs e)
{
SwitchGciView(
"UniDataSorageActionsView",
new UniDataSorageActionsView(this, _bridge));
}
private void btnMetersAction_Click(object sender, EventArgs e)
{
SwitchGciView(
"SlotsComPortsRegistersActionsView",
new SlotsComPortsRegistersActionsView(this, _bridge, AddSlotRow, SaveSlots));
}
private void btnMeterInit_Click_1(object sender, EventArgs e)
{
SwitchGciView(
"ConfigurationView",
new ConfigurationView(this));
}
private void preadjustmenActionsViewButton_Click(object sender, EventArgs e)
{
SwitchGciView(
"PreadjustmenActionsView",
new PreadjustmentActionsView(this, _bridge, AddSlotRow, SaveSlots));
}
}
#endregion
}

View File

@ -1,6 +1,6 @@
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
partial class CombinedInterfaceView
partial class CombinedActionsView
{
private System.ComponentModel.IContainer components = null;

View File

@ -10,7 +10,7 @@ using TBF.Rig.BridgeComponents.GciBridge.Interfaces;
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
public partial class CombinedInterfaceView : UserControl
public partial class CombinedActionsView : UserControl
{
private readonly MainView _mainView;
private readonly GciBridge _bridge;
@ -19,7 +19,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
private readonly Dictionary<int, string> _pcbBySlot = new Dictionary<int, string>();
private readonly Dictionary<int, string> _passwordBySlot = new Dictionary<int, string>();
public CombinedInterfaceView(MainView mainView, GciBridge bridge)
public CombinedActionsView(MainView mainView, GciBridge bridge)
{
_mainView = mainView ?? throw new ArgumentNullException(nameof(mainView));
_bridge = bridge ?? throw new ArgumentNullException(nameof(bridge));

View File

@ -0,0 +1,250 @@
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
partial class PreadjustmentActionsView
{
private System.ComponentModel.IContainer components = null;
private System.Windows.Forms.GroupBox grpSlots;
private System.Windows.Forms.Button preparationActionButton;
private System.Windows.Forms.Button detectActionButton;
private System.Windows.Forms.Button temperatureCalibrationActionButton;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.TextBox txtLog;
protected override void Dispose(bool disposing)
{
if (disposing && components != null)
components.Dispose();
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.grpSlots = new System.Windows.Forms.GroupBox();
this.completionActionButton = new System.Windows.Forms.Button();
this.offsetTestActionButton = new System.Windows.Forms.Button();
this.preparationActionButton = new System.Windows.Forms.Button();
this.detectActionButton = new System.Windows.Forms.Button();
this.temperatureCalibrationActionButton = new System.Windows.Forms.Button();
this.amplitudeTestActionButton = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.txtLog = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.grpSlots.SuspendLayout();
this.SuspendLayout();
//
// grpSlots
//
this.grpSlots.Controls.Add(this.label7);
this.grpSlots.Controls.Add(this.label6);
this.grpSlots.Controls.Add(this.label5);
this.grpSlots.Controls.Add(this.label4);
this.grpSlots.Controls.Add(this.label3);
this.grpSlots.Controls.Add(this.label2);
this.grpSlots.Controls.Add(this.label1);
this.grpSlots.Controls.Add(this.button1);
this.grpSlots.Controls.Add(this.completionActionButton);
this.grpSlots.Controls.Add(this.offsetTestActionButton);
this.grpSlots.Controls.Add(this.preparationActionButton);
this.grpSlots.Controls.Add(this.detectActionButton);
this.grpSlots.Controls.Add(this.temperatureCalibrationActionButton);
this.grpSlots.Controls.Add(this.amplitudeTestActionButton);
this.grpSlots.Location = new System.Drawing.Point(10, 10);
this.grpSlots.Name = "grpSlots";
this.grpSlots.Size = new System.Drawing.Size(689, 304);
this.grpSlots.TabIndex = 2;
this.grpSlots.TabStop = false;
this.grpSlots.Text = "Slots by selection in the table";
this.grpSlots.Enter += new System.EventHandler(this.grpSlots_Enter);
//
// completionActionButton
//
this.completionActionButton.Location = new System.Drawing.Point(390, 249);
this.completionActionButton.Name = "completionActionButton";
this.completionActionButton.Size = new System.Drawing.Size(150, 28);
this.completionActionButton.TabIndex = 3;
this.completionActionButton.Text = "Completition";
//
// offsetTestActionButton
//
this.offsetTestActionButton.Location = new System.Drawing.Point(334, 215);
this.offsetTestActionButton.Name = "offsetTestActionButton";
this.offsetTestActionButton.Size = new System.Drawing.Size(150, 28);
this.offsetTestActionButton.TabIndex = 5;
this.offsetTestActionButton.Text = "Offset test";
//
// preparationActionButton
//
this.preparationActionButton.Location = new System.Drawing.Point(203, 113);
this.preparationActionButton.Name = "preparationActionButton";
this.preparationActionButton.Size = new System.Drawing.Size(150, 28);
this.preparationActionButton.TabIndex = 0;
this.preparationActionButton.Text = "Preparation";
this.preparationActionButton.Click += new System.EventHandler(this.preparationActionButton_Click);
//
// detectActionButton
//
this.detectActionButton.Location = new System.Drawing.Point(171, 79);
this.detectActionButton.Name = "detectActionButton";
this.detectActionButton.Size = new System.Drawing.Size(150, 28);
this.detectActionButton.TabIndex = 1;
this.detectActionButton.Text = "Detect";
this.detectActionButton.Click += new System.EventHandler(this.detectActionButton_Click);
//
// temperatureCalibrationActionButton
//
this.temperatureCalibrationActionButton.Location = new System.Drawing.Point(285, 181);
this.temperatureCalibrationActionButton.Name = "temperatureCalibrationActionButton";
this.temperatureCalibrationActionButton.Size = new System.Drawing.Size(150, 28);
this.temperatureCalibrationActionButton.TabIndex = 2;
this.temperatureCalibrationActionButton.Text = "Temperature calibration";
this.temperatureCalibrationActionButton.Click += new System.EventHandler(this.amplitudeTestActionButton_Click);
//
// amplitudeTestActionButton
//
this.amplitudeTestActionButton.Location = new System.Drawing.Point(246, 147);
this.amplitudeTestActionButton.Name = "amplitudeTestActionButton";
this.amplitudeTestActionButton.Size = new System.Drawing.Size(150, 28);
this.amplitudeTestActionButton.TabIndex = 18;
this.amplitudeTestActionButton.Text = "Amplitude test";
//
// btnCancel
//
this.btnCancel.Enabled = false;
this.btnCancel.Location = new System.Drawing.Point(519, 320);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(180, 30);
this.btnCancel.TabIndex = 4;
this.btnCancel.Text = "Cancel task";
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// txtLog
//
this.txtLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.txtLog.Location = new System.Drawing.Point(10, 356);
this.txtLog.Multiline = true;
this.txtLog.Name = "txtLog";
this.txtLog.ReadOnly = true;
this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.txtLog.Size = new System.Drawing.Size(689, 232);
this.txtLog.TabIndex = 5;
this.txtLog.WordWrap = false;
//
// button1
//
this.button1.Location = new System.Drawing.Point(17, 28);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(150, 28);
this.button1.TabIndex = 108;
this.button1.Text = "Preadjustment init";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(176, 121);
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(22, 13);
this.label1.TabIndex = 109;
this.label1.Text = "--->";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(219, 155);
this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(22, 13);
this.label2.TabIndex = 110;
this.label2.Text = "--->";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(258, 189);
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(22, 13);
this.label3.TabIndex = 111;
this.label3.Text = "--->";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(307, 223);
this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(22, 13);
this.label4.TabIndex = 112;
this.label4.Text = "--->";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(363, 257);
this.label5.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(22, 13);
this.label5.TabIndex = 113;
this.label5.Text = "--->";
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(14, 87);
this.label6.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(117, 13);
this.label6.TabIndex = 114;
this.label6.Text = "Preadjustment process:";
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(144, 87);
this.label7.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(22, 13);
this.label7.TabIndex = 115;
this.label7.Text = "--->";
//
// PreadjustmentActionsView
//
this.BackColor = System.Drawing.SystemColors.Control;
this.Controls.Add(this.grpSlots);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.txtLog);
this.Name = "PreadjustmentActionsView";
this.Size = new System.Drawing.Size(719, 603);
this.grpSlots.ResumeLayout(false);
this.grpSlots.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.Button completionActionButton;
private System.Windows.Forms.Button offsetTestActionButton;
private System.Windows.Forms.Button amplitudeTestActionButton;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label6;
}
}

View File

@ -0,0 +1,405 @@
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary;
using GenesisCordonelInterface.API;
using GenesisCordonelInterface.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using static GenesisCordonelInterface.API.PublicModels;
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.PreadjustmentMeter;
using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
public partial class PreadjustmentActionsView : UserControl
{
private readonly GciBridge _bridge;
private readonly MainView _mainView;
private CancellationTokenSource _cts;
private readonly Action _addSlotAction;
private readonly Action _saveAction;
public PreadjustmentActionsView(
MainView mainview,
GciBridge bridge,
Action addSlotAction,
Action saveAction)
{
_mainView = mainview ?? throw new ArgumentNullException(nameof(mainview));
_bridge = bridge ?? throw new ArgumentNullException(nameof(bridge));
_addSlotAction = addSlotAction;
_saveAction = saveAction;
InitializeComponent();
LoadRegisterComboBoxes();
}
private void AddSlot()
{
_addSlotAction?.Invoke();
}
private void SaveSlots()
{
_saveAction?.Invoke();
}
private List<GciPublicModels.MeterBatchDebugStatus> GetSelectedSlots()
{
if (_mainView == null || _mainView._batchPanel == null)
throw new Exception("Meter batch grid is not available.");
var slots = _mainView._batchPanel.GetSelectedGridData();
if (slots.Count == 0)
throw new Exception("No selected slots in grid.");
return slots;
}
private void RefreshGrid(int? removedSlot = null)
{
if (removedSlot.HasValue)
{
_mainView?._gciApi.SetSlotSelected(removedSlot.Value, false);
_mainView?._batchPanel?.RemoveSlotRow(removedSlot.Value);
}
if (_bridge?.gciExternalInterface != null)
_bridge.gciExternalInterface.RaiseMeterBatchStatusChanged();
}
private void btnCancel_Click(object sender, EventArgs e)
{
_cts?.Cancel();
Log("Cancel requested.");
}
private async void ExecuteAsync(Func<CancellationToken, Task> action)
{
try
{
SetBusy(true);
_cts = new CancellationTokenSource();
await action(_cts.Token);
}
catch (OperationCanceledException)
{
Log("Operation canceled.");
}
catch (Exception ex)
{
Log("ERROR: " + ex);
MessageBox.Show(
ex.Message,
"GciBridge API call failed",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
finally
{
_cts?.Dispose();
_cts = null;
SetBusy(false);
}
}
private void SetBusy(bool busy)
{
Cursor = busy ? Cursors.WaitCursor : Cursors.Default;
preparationActionButton.Enabled = !busy;
detectActionButton.Enabled = !busy;
temperatureCalibrationActionButton.Enabled = !busy;
offsetTestActionButton.Enabled = !busy;
completionActionButton.Enabled = !busy;
amplitudeTestActionButton.Enabled = !busy;
amplitudeTestActionButton.Enabled = !busy;
temperatureCalibrationActionButton.Enabled = !busy;
offsetTestActionButton.Enabled = !busy;
completionActionButton.Enabled = !busy;
btnCancel.Enabled = busy;
}
private void LogResult(string methodName, object result)
{
Log(methodName + " result:");
Log(result == null ? "<null>" : result.ToString());
}
private void Log(string message)
{
txtLog.AppendText(
DateTime.Now.ToString("HH:mm:ss.fff") +
" " +
message +
Environment.NewLine);
}
private void LoadRegisterComboBoxes()
{
}
private void grpSlots_Enter(object sender, EventArgs e)
{
}
private void preparationActionButton_Click(
object sender,
EventArgs e)
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
_bridge.Preadjustment_PreparationAsync,
"Preparation",
token);
});
}
private void amplitudeTestActionButton_Click(
object sender,
EventArgs e)
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
_bridge.Preadjustment_AmplitudeTestAsync,
"Amplitude Test",
token);
});
}
private void temperatureCalibrationActionButton_Click(
object sender,
EventArgs e)
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
_bridge.Preadjustment_TemperatureCalibrationAsync,
"Temperature Calibration",
token);
});
}
private void offsetTestActionButton_Click(
object sender,
EventArgs e)
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
_bridge.Preadjustment_OffsetTestAsync,
"Offset Test",
token);
});
}
private void completionActionButton_Click(
object sender,
EventArgs e)
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
_bridge.Preadjustment_CompletionAsync,
"Completion",
token);
});
}
private void detectActionButton_Click(
object sender,
EventArgs e)
{
ExecuteAsync(async token =>
{
var selectedSlots =
_mainView._batchPanel.GetSelectedGridData();
if (!selectedSlots.Any())
{
Log("Detect: no selected slots.");
return;
}
_mainView._laatzenApi._settings =
CreatePreAdjustmentSettings(selectedSlots);
_mainView._laatzenApi._meterControls =
CreateMeterControls(selectedSlots);
PreadjustmentDetectResult result =
await _bridge.Preadjustment_DetectAsync(token);
Log(
result.Success
? "Detect completed. " + result
: "Detect failed. " + result);
RefreshGrid();
});
}
private ProcessProgress CreatePreparationProgress()
{
return new ProcessProgress
{
Setting = new PreAdjustmentSettingsContainer
{
TempOnly = false,
Culture =
Thread.CurrentThread.CurrentCulture
},
IsAutomaticMode = true
};
}
private List<MeterStateControl> CreateMeterControls(IEnumerable<GciPublicModels.MeterBatchDebugStatus> slots)
{
var controls =
new List<MeterStateControl>();
foreach (var slot in slots)
{
var ctl = new MeterStateControl(slot.Slot);
ctl.SetChecked(true);
ctl.IsEnabled = true;
controls.Add(ctl);
}
return controls;
}
private PreAdjustmentSettingsContainer CreatePreAdjustmentSettings(IEnumerable<GciPublicModels.MeterBatchDebugStatus> slots)
{
var settings =
new PreAdjustmentSettingsContainer();
settings.Meters =
slots.Select(s => s.Slot).ToList();
settings.TempMeters =
new List<int>();
settings.NumberOfPaths = 2;
settings.TempOnly = false;
settings.Culture =
Thread.CurrentThread.CurrentCulture;
return settings;
}
private void writeRegisterButton_Click(object sender, EventArgs e)
{
/*ExecuteAsync(async token =>
{
string registerName = Convert.ToString(writeRegisterNameComboBox.Text).Trim();
string valueText = writeRegisterValueTextBox.Text.Trim();
if (string.IsNullOrWhiteSpace(registerName))
throw new Exception("Write register name is empty.");
if (string.IsNullOrWhiteSpace(valueText))
throw new Exception("Write register value is empty.");
object value;
if (registerName == "GENESISFLOW_LedMode")
{
value = byte.Parse(valueText);
}
else
{
value = valueText;
}
var tasks = GetSelectedSlots()
.Select(async slot =>
{
//var result = await _bridge.WriteRegisterAsync(slot.Slot, registerName, value, false, false, token);
//var result = await _bridge.WriteRegisterWithRetryAsync(slot.Slot, registerName, value, false, false, token);
var result = Xylem.Common.Ui.CordonelPreadjustmentUi. Processes.WriteRegisterSafe(meter, "Calibration factor1", Register.Genesisflow.CalFactor1, setting.CalFactor1);
Processes
return new
{
Slot = slot.Slot,
Result = result
};
})
.ToList();
var results = await Task.WhenAll(tasks);
foreach (var item in results.OrderBy(x => x.Slot))
{
LogResult(
$"WriteRegisterAsync slot {item.Slot}, register {registerName}, value {value}",
item.Result);
}
RefreshGrid();
});*/
}
private async Task ExecutePreadjustmentForSelectedSlotsAsync(
Func<int, ProcessProgress, CancellationToken, Task<PreadjustmentProcessResult>> action,
string processName,
CancellationToken token)
{
var selectedSlots =
_mainView._batchPanel.GetSelectedGridData();
if (!selectedSlots.Any())
{
Log(processName + ": no selected slots.");
return;
}
var pp =
CreatePreparationProgress();
foreach (var selectedSlot in selectedSlots)
{
PreadjustmentProcessResult result =
await action(
selectedSlot.Slot,
pp,
token);
Log(result.Success
? processName + " completed. " + result
: processName + " failed. " + result);
if (!result.Success)
break;
}
RefreshGrid();
}
}
}

View File

@ -1,6 +1,6 @@
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
partial class GenesisCordonelInterfaceView
partial class SlotsComPortsRegistersActionsView
{
private System.ComponentModel.IContainer components = null;
@ -42,6 +42,7 @@
this.btnGetSlot = new System.Windows.Forms.Button();
this.btnCleanAllSlots = new System.Windows.Forms.Button();
this.btnCleanSlot = new System.Windows.Forms.Button();
this.writeRegisterValueTypeLabel = new System.Windows.Forms.Label();
this.btnCancel = new System.Windows.Forms.Button();
this.txtLog = new System.Windows.Forms.TextBox();
this.grpSlots.SuspendLayout();
@ -49,6 +50,9 @@
//
// grpSlots
//
this.grpSlots.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.grpSlots.Controls.Add(this.writeRegisterNameComboBox);
this.grpSlots.Controls.Add(this.readRegisterNameComboBox);
this.grpSlots.Controls.Add(this.label4);
@ -66,9 +70,10 @@
this.grpSlots.Controls.Add(this.btnGetSlot);
this.grpSlots.Controls.Add(this.btnCleanAllSlots);
this.grpSlots.Controls.Add(this.btnCleanSlot);
this.grpSlots.Controls.Add(this.writeRegisterValueTypeLabel);
this.grpSlots.Location = new System.Drawing.Point(10, 10);
this.grpSlots.Name = "grpSlots";
this.grpSlots.Size = new System.Drawing.Size(689, 304);
this.grpSlots.Size = new System.Drawing.Size(726, 304);
this.grpSlots.TabIndex = 2;
this.grpSlots.TabStop = false;
this.grpSlots.Text = "Slots by selection in the table";
@ -103,7 +108,7 @@
//
this.writeRegisterValueTextBox.Location = new System.Drawing.Point(555, 264);
this.writeRegisterValueTextBox.Name = "writeRegisterValueTextBox";
this.writeRegisterValueTextBox.Size = new System.Drawing.Size(117, 20);
this.writeRegisterValueTextBox.Size = new System.Drawing.Size(85, 20);
this.writeRegisterValueTextBox.TabIndex = 12;
//
// label3
@ -223,6 +228,15 @@
this.btnCleanSlot.Text = "CleanSlot(slot)";
this.btnCleanSlot.Click += new System.EventHandler(this.btnCleanSlot_Click);
//
// writeRegisterValueTypeLabel
//
this.writeRegisterValueTypeLabel.AutoSize = true;
this.writeRegisterValueTypeLabel.Location = new System.Drawing.Point(646, 267);
this.writeRegisterValueTypeLabel.Name = "writeRegisterValueTypeLabel";
this.writeRegisterValueTypeLabel.Size = new System.Drawing.Size(36, 13);
this.writeRegisterValueTypeLabel.TabIndex = 19;
this.writeRegisterValueTypeLabel.Text = "type: -";
//
// btnCancel
//
this.btnCancel.Enabled = false;
@ -243,18 +257,18 @@
this.txtLog.Name = "txtLog";
this.txtLog.ReadOnly = true;
this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.txtLog.Size = new System.Drawing.Size(689, 232);
this.txtLog.Size = new System.Drawing.Size(726, 232);
this.txtLog.TabIndex = 5;
this.txtLog.WordWrap = false;
//
// GenesisCordonelInterfaceView
// SlotsComPortsRegistersActionsView
//
this.BackColor = System.Drawing.SystemColors.Control;
this.Controls.Add(this.grpSlots);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.txtLog);
this.Name = "GenesisCordonelInterfaceView";
this.Size = new System.Drawing.Size(719, 603);
this.Name = "SlotsComPortsRegistersActionsView";
this.Size = new System.Drawing.Size(756, 603);
this.grpSlots.ResumeLayout(false);
this.grpSlots.PerformLayout();
this.ResumeLayout(false);
@ -275,5 +289,6 @@
private System.Windows.Forms.ComboBox readRegisterNameComboBox;
private System.Windows.Forms.Button btnLogin;
private System.Windows.Forms.Button btnCleanSlot;
private System.Windows.Forms.Label writeRegisterValueTypeLabel;
}
}

View File

@ -10,15 +10,17 @@ using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
public partial class GenesisCordonelInterfaceView : UserControl
public partial class SlotsComPortsRegistersActionsView : UserControl
{
private readonly GciBridge _bridge;
private readonly MainView _mainView;
private CancellationTokenSource _cts;
private readonly Action _addSlotAction;
private readonly Action _saveAction;
private readonly Dictionary<string, Type> _registerTypes =
new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public GenesisCordonelInterfaceView(
public SlotsComPortsRegistersActionsView(
MainView mainview,
GciBridge bridge,
Action addSlotAction,
@ -33,6 +35,13 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
InitializeComponent();
LoadRegisterComboBoxes();
_mainView._batchPanel.GetMeterRegistersClicked -= LoadRegisterComboBoxes;
_mainView._batchPanel.GetMeterRegistersClicked += LoadRegisterComboBoxes;
writeRegisterNameComboBox.SelectedIndexChanged -= writeRegisterNameComboBox_SelectedIndexChanged;
writeRegisterNameComboBox.SelectedIndexChanged += writeRegisterNameComboBox_SelectedIndexChanged;
writeRegisterNameComboBox.TextChanged -= writeRegisterNameComboBox_SelectedIndexChanged;
writeRegisterNameComboBox.TextChanged += writeRegisterNameComboBox_SelectedIndexChanged;
}
private void AddSlot()
@ -48,14 +57,14 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
private List<GciPublicModels.MeterBatchDebugStatus> GetSelectedSlots()
{
if (_mainView == null || _mainView._batchPanel == null)
throw new Exception("Meter batch grid is not available.");
{
Log("Meter batch grid is not available.");
var slots = _mainView._batchPanel.GetSelectedGridData();
return new List<GciPublicModels.MeterBatchDebugStatus>();
}
if (slots.Count == 0)
throw new Exception("No selected slots in grid.");
return slots;
return _mainView._batchPanel.GetSelectedGridData()
?? new List<GciPublicModels.MeterBatchDebugStatus>();
}
private void RefreshGrid(int? removedSlot = null)
@ -414,16 +423,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
if (string.IsNullOrWhiteSpace(valueText))
throw new Exception("Write register value is empty.");
object value;
if (registerName == "GENESISFLOW_LedMode")
{
value = byte.Parse(valueText);
}
else
{
value = valueText;
}
object value = ConvertTextToRegisterValue(registerName, valueText);
var tasks = GetSelectedSlots()
.Select(async slot =>
@ -530,18 +530,162 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
private void LoadRegisterComboBoxes()
{
var registers = _bridge.GetAllRegisterNames();
_registerTypes.Clear();
readRegisterNameComboBox.Items.Clear();
writeRegisterNameComboBox.Items.Clear();
readRegisterNameComboBox.Items.AddRange(registers.ToArray());
writeRegisterNameComboBox.Items.AddRange(registers.ToArray());
var selectedSlots = GetSelectedSlots();
if (selectedSlots.Count == 0)
{
Log("No selected slot.");
return;
}
int slot = selectedSlots
.OrderBy(x => x.Slot)
.First()
.Slot;
var registers = _mainView._laatzenApi.GetRegistersDicForSlot(slot);
var uniqueRegisters = registers
.GroupBy(x => x.Key.GetIdent())
.Select(g => g.First())
.OrderBy(x => x.Key.GetIdent())
.ToList();
foreach (var item in uniqueRegisters)
{
string registerName = item.Key.GetIdent();
Type dataType = item.Key.DataType;
_registerTypes[registerName] = dataType;
readRegisterNameComboBox.Items.Add(registerName);
writeRegisterNameComboBox.Items.Add(registerName);
}
Log($"Loaded {uniqueRegisters.Count} unique registers from slot {slot}");
}
private void grpSlots_Enter(object sender, EventArgs e)
{
}
private void writeRegisterNameComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
string registerName = Convert.ToString(writeRegisterNameComboBox.Text).Trim();
if (_registerTypes.TryGetValue(registerName, out var type))
{
writeRegisterValueTypeLabel.Text = $"type: {type.Name}";
}
else
{
writeRegisterValueTypeLabel.Text = "type: -";
}
}
private object ConvertTextToRegisterValue(
string registerName,
string valueText)
{
if (!_registerTypes.TryGetValue(
registerName,
out var targetType))
{
throw new Exception(
$"Unknown register type for register '{registerName}'.");
}
return ConvertTextToType(
valueText,
targetType,
registerName);
}
private object ConvertTextToType(
string valueText,
Type targetType,
string registerName)
{
if (targetType == typeof(string))
return valueText;
if (targetType.IsEnum)
{
return Enum.Parse(
targetType,
valueText,
true);
}
if (targetType == typeof(byte[]))
{
return ParseByteArray(valueText);
}
if (targetType.Name == "Enum8")
{
return byte.Parse(valueText);
}
try
{
return Convert.ChangeType(
valueText,
targetType,
System.Globalization.CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
throw new Exception(
$"Value '{valueText}' cannot be converted to '{targetType.Name}' for register '{registerName}'.",
ex);
}
}
private byte[] ParseByteArray(string valueText)
{
valueText = valueText.Trim();
if (valueText.Contains("-") ||
valueText.Contains(" "))
{
return valueText
.Split(
new[] { '-', ' ', ',', ';' },
StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Replace("0x", ""))
.Select(x => Convert.ToByte(x, 16))
.ToArray();
}
valueText =
valueText.Replace("0x", "");
if (valueText.Length % 2 != 0)
{
throw new Exception(
"Hex string length must be even.");
}
byte[] result =
new byte[valueText.Length / 2];
for (int i = 0; i < result.Length; i++)
{
string hex =
valueText.Substring(i * 2, 2);
result[i] =
Convert.ToByte(hex, 16);
}
return result;
}
}
}

View File

@ -1,12 +1,10 @@
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
partial class UniDataStorageReaderInterfaceView
partial class UniDataSorageActionsView
{
private System.ComponentModel.IContainer components = null;
private System.Windows.Forms.GroupBox grpStorage;
private System.Windows.Forms.Label lblPcbId;
private System.Windows.Forms.TextBox txtPcbId;
private System.Windows.Forms.Button btnGetPasswordByPcb;
private System.Windows.Forms.Button btnCancel;
@ -23,11 +21,11 @@
private void InitializeComponent()
{
this.grpStorage = new System.Windows.Forms.GroupBox();
this.lblPcbId = new System.Windows.Forms.Label();
this.txtPcbId = new System.Windows.Forms.TextBox();
this.btnGetPasswordByPcb = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.txtLog = new System.Windows.Forms.TextBox();
this.lblPcbId = new System.Windows.Forms.Label();
this.txtPcbId = new System.Windows.Forms.TextBox();
this.grpStorage.SuspendLayout();
this.SuspendLayout();
//
@ -38,27 +36,11 @@
this.grpStorage.Controls.Add(this.btnGetPasswordByPcb);
this.grpStorage.Location = new System.Drawing.Point(10, 37);
this.grpStorage.Name = "grpStorage";
this.grpStorage.Size = new System.Drawing.Size(200, 120);
this.grpStorage.Size = new System.Drawing.Size(200, 261);
this.grpStorage.TabIndex = 0;
this.grpStorage.TabStop = false;
this.grpStorage.Text = "UniDataStorageReader for GCI";
//
// lblPcbId
//
this.lblPcbId.AutoSize = true;
this.lblPcbId.Location = new System.Drawing.Point(10, 25);
this.lblPcbId.Name = "lblPcbId";
this.lblPcbId.Size = new System.Drawing.Size(45, 13);
this.lblPcbId.TabIndex = 0;
this.lblPcbId.Text = "PCB ID:";
//
// txtPcbId
//
this.txtPcbId.Location = new System.Drawing.Point(65, 22);
this.txtPcbId.Name = "txtPcbId";
this.txtPcbId.Size = new System.Drawing.Size(120, 20);
this.txtPcbId.TabIndex = 1;
//
// btnGetPasswordByPcb
//
this.btnGetPasswordByPcb.Location = new System.Drawing.Point(10, 55);
@ -91,12 +73,28 @@
this.txtLog.TabIndex = 4;
this.txtLog.WordWrap = false;
//
// UniDataStorageReaderInterfaceView
// lblPcbId
//
this.lblPcbId.AutoSize = true;
this.lblPcbId.Location = new System.Drawing.Point(10, 25);
this.lblPcbId.Name = "lblPcbId";
this.lblPcbId.Size = new System.Drawing.Size(45, 13);
this.lblPcbId.TabIndex = 0;
this.lblPcbId.Text = "PCB ID:";
//
// txtPcbId
//
this.txtPcbId.Location = new System.Drawing.Point(65, 22);
this.txtPcbId.Name = "txtPcbId";
this.txtPcbId.Size = new System.Drawing.Size(120, 20);
this.txtPcbId.TabIndex = 1;
//
// UniDataSorageActionsView
//
this.Controls.Add(this.grpStorage);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.txtLog);
this.Name = "UniDataStorageReaderInterfaceView";
this.Name = "UniDataSorageActionsView";
this.Size = new System.Drawing.Size(740, 370);
this.grpStorage.ResumeLayout(false);
this.grpStorage.PerformLayout();
@ -104,5 +102,8 @@
this.PerformLayout();
}
private System.Windows.Forms.Label lblPcbId;
private System.Windows.Forms.TextBox txtPcbId;
}
}

View File

@ -5,13 +5,13 @@ using System.Windows.Forms;
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
public partial class UniDataStorageReaderInterfaceView : UserControl
public partial class UniDataSorageActionsView : UserControl
{
private readonly MainView _mainView;
private readonly GciBridge _bridge;
private CancellationTokenSource _cts;
public UniDataStorageReaderInterfaceView(MainView mainView, GciBridge bridge)
public UniDataSorageActionsView(MainView mainView, GciBridge bridge)
{
_mainView = mainView ?? throw new ArgumentNullException(nameof(mainView));
_bridge = bridge ?? throw new ArgumentNullException(nameof(bridge));

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -253,17 +253,23 @@
<Compile Include="Rig\BridgeComponents\GciBridge\UI\MainView.Designer.cs">
<DependentUpon>MainView.cs</DependentUpon>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\UniDataStorageReaderInterfaceView.cs">
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\PreadjustmentActionsView.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\UniDataStorageReaderInterfaceView.Designer.cs">
<DependentUpon>UniDataStorageReaderInterfaceView.cs</DependentUpon>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\PreadjustmentActionsView.Designer.cs">
<DependentUpon>PreadjustmentActionsView.cs</DependentUpon>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\CombinedInterfaceView.cs">
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\UniDataSorageActionsView.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\CombinedInterfaceView.Designer.cs">
<DependentUpon>CombinedInterfaceView.cs</DependentUpon>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\UniDataSorageActionsView.Designer.cs">
<DependentUpon>UniDataSorageActionsView.cs</DependentUpon>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\CombinedActionsView.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\CombinedActionsView.Designer.cs">
<DependentUpon>CombinedActionsView.cs</DependentUpon>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\ConfigurationView.cs">
<SubType>UserControl</SubType>
@ -271,11 +277,11 @@
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\ConfigurationView.Designer.cs">
<DependentUpon>ConfigurationView.cs</DependentUpon>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\GenesisCordonelInterfaceView.cs">
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\SlotsComPortsRegistersActionsView.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\GenesisCordonelInterfaceView.Designer.cs">
<DependentUpon>GenesisCordonelInterfaceView.cs</DependentUpon>
<Compile Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\SlotsComPortsRegistersActionsView.Designer.cs">
<DependentUpon>SlotsComPortsRegistersActionsView.cs</DependentUpon>
</Compile>
<Compile Include="Rig\BuiltIn\PumpTandem\Pump.cs" />
<Compile Include="Rig\BuiltIn\PumpTandem\PumpCfg.cs" />
@ -3310,17 +3316,20 @@
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\MainView.resx">
<DependentUpon>MainView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\CombinedInterfaceView.resx">
<DependentUpon>CombinedInterfaceView.cs</DependentUpon>
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\CombinedActionsView.resx">
<DependentUpon>CombinedActionsView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\ConfigurationView.resx">
<DependentUpon>ConfigurationView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\UniDataStorageReaderInterfaceView.resx">
<DependentUpon>UniDataStorageReaderInterfaceView.cs</DependentUpon>
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\PreadjustmentActionsView.resx">
<DependentUpon>PreadjustmentActionsView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\GenesisCordonelInterfaceView.resx">
<DependentUpon>GenesisCordonelInterfaceView.cs</DependentUpon>
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\UniDataSorageActionsView.resx">
<DependentUpon>UniDataSorageActionsView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Rig\BridgeComponents\GciBridge\UI\StaraTuraAPI_GciBridge\SlotsComPortsRegistersActionsView.resx">
<DependentUpon>SlotsComPortsRegistersActionsView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Rig\BuiltIn\PumpTandem\PumpCfgCtrl.resx">
<DependentUpon>PumpCfgCtrl.cs</DependentUpon>
@ -4437,6 +4446,18 @@
<Project>{439D0878-C76E-452B-B17D-209A89E91D36}</Project>
<Name>Dirichlet.Numerics</Name>
</ProjectReference>
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\CordonelPreadjustmentUi\CordonelPreadjustmentUi.csproj">
<Project>{d0c8d887-ed52-40ab-a069-90bce0e801e2}</Project>
<Name>CordonelPreadjustmentUi</Name>
</ProjectReference>
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\Hardware\WaterMeter\Genesis\Registers\Registers.csproj">
<Project>{f4aaf7e6-7333-4284-b735-e32deb076c64}</Project>
<Name>Registers</Name>
</ProjectReference>
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\Logic\ProductionOrderCore\ProductionOrderCore.csproj">
<Project>{6d2777bb-7a88-466d-a49b-3266f9bf0160}</Project>
<Name>ProductionOrderCore</Name>
</ProjectReference>
<ProjectReference Include="..\GemCard\GemCard.csproj">
<Project>{8B10D15A-39DE-4B56-8DD1-710C1EB3A697}</Project>
<Name>GemCard</Name>