tbf/GenesisCordonelInterface/API/InterfaceGCIToLaatzen.cs

2217 lines
74 KiB
C#

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;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
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;
namespace GenesisCordonelInterface.API
{
public class InterfaceGCIToLaatzen
{
#region Fields
//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();
private readonly ConcurrentDictionary<int, ApiWorker> _workers = new ConcurrentDictionary<int, ApiWorker>();
private readonly ConcurrentDictionary<int, bool> _selectedSlots = new ConcurrentDictionary<int, bool>();
#endregion
#region ================================== Worker ==================================
private ApiWorker GetWorker(int slot)
{
if (slot <= 0)
throw new ArgumentOutOfRangeException(nameof(slot));
return _workers.GetOrAdd(slot, s => new ApiWorker($"GCI Worker Slot {s}"));
}
#endregion
#region ================================== Worker Debug ==================================
public List<PublicModels.WorkerDebugStatus> GetWorkerDebugStatuses()
{
return _workers
.OrderBy(x => x.Key)
.Select(x => new PublicModels.WorkerDebugStatus
{
Slot = x.Key,
Name = x.Value.Name,
QueueLength = /*x.Value.IsDisposed ? 0 : */x.Value.QueueLength,
IsBusy = !x.Value.IsDisposed && x.Value.IsBusy,
CurrentOperation = x.Value.IsDisposed ? null : x.Value.CurrentOperation,
LastError = x.Value.LastError,
LastActivity = x.Value.LastActivity
})
.ToList();
}
#endregion
#region ================================== MeterBatch Debug ==================================
/// <summary>
///
/// </summary>
/// <returns></returns>
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
{
List<ZeroFlowGenesisMeter> meters;
//just snapshot of list under lock
lock (_meterBatchLock)
{
meters = _meterBatch.ListOfMeters
.OfType<ZeroFlowGenesisMeter>()
.ToList();
}
return meters
.Select(m => new MeterBatchDebugStatus
{
Slot = m.Slot,
Selected = IsSlotSelected(m.Slot),
PcbId = m.PcbId,
IsConnected = m.IsConnected,
IsLoggedOn = m.IsLoggedOn,
RequestPort = m.requestPortConfig.HasValue
? m.requestPortConfig.Value.PortName
: "",
StreamingPort = m.streamingPortConfig.HasValue
? m.streamingPortConfig.Value.PortName
: "",
RequestPortType = m.requestPortConfig.HasValue
? m.requestPortConfig.Value.Type
: "",
FwVersion = m.FwVersion,
InterfaceVersion = m.InterfaceInfo?.InterfaceVersion
})
.OrderBy(x => x.Slot)
.ToList();
}
public void SetSlotSelected(int slot, bool selected)
{
if (slot <= 0)
throw new ArgumentOutOfRangeException(nameof(slot));
Logger.Debug(
"[{0}] SetSlotSelected: slot={1}, selected={2}",
InterfaceName,
slot,
selected);
_selectedSlots[slot] = selected;
}
public bool IsSlotSelected(int slot)
{
return _selectedSlots.TryGetValue(slot, out bool selected) && selected;
}
public List<int> GetSelectedSlots()
{
return _selectedSlots
.Where(x => x.Value)
.Select(x => x.Key)
.OrderBy(x => x)
.ToList();
}
#endregion
#region ================================== Helpers ==================================
private GenesisMeter GetMeterThreadSafe(int slot)
{
lock (_meterBatchLock)
{
return _meterBatch.ListOfMeters
.OfType<ZeroFlowGenesisMeter>()
.FirstOrDefault(m => m.Slot == slot);
}
}
private bool RemoveMeterThreadSafe(GenesisMeter meter)
{
if (meter == null)
return false;
lock (_meterBatchLock)
{
return _meterBatch.ListOfMeters.Remove(meter);
}
}
private void RemoveMetersThreadSafe()
{
lock (_meterBatchLock)
{
_meterBatch.ListOfMeters.Clear();
}
}
private void RemoveWorkersThreadSafe()
{
foreach (var pair in _workers.ToList())
{
ApiWorker worker;
if (_workers.TryRemove(pair.Key, out worker))
{
worker.Dispose();
}
}
}
private void DisposeMetersThreadSafe()
{
lock (_meterBatchLock)
{
foreach (var meter in _meterBatch.ListOfMeters.OfType<ZeroFlowGenesisMeter>())
{
meter.DisposeMeter();
}
}
}
private void EnsureConnected(GenesisMeter meter)
{
if (!meter.IsLoggedOn)
throw new InvalidOperationException("Meter is not connected.");
}
private string ToHex(byte[] data)
{
return data == null ? "" : BitConverter.ToString(data).Replace("-", " ");
}
private const string InterfaceName = "InterfaceGCIToLaatzen";
private void LogInfo(string operation, string message)
{
Logger.Info("[{0}] {1}: {2}", InterfaceName, operation, message);
}
private void LogError(string operation, Exception ex)
{
Logger.Error(ex, "[{0}] {1} failed: {2}", InterfaceName, operation, ex.Message);
}
private string SafePort(string port)
{
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 ==================================
public Task<PublicModels.GciInitSlotResult> InitSlotAsync(
int slot,
ConfigSource cfg,
PasswordSource pwd,
PortConfig? req,
PortConfig? str,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => InitSlot(slot, cfg, pwd, req, str), token);
}
public PublicModels.GciInitSlotResult InitSlot(
int slot,
ConfigSource cfg,
PasswordSource pwd,
PortConfig? req,
PortConfig? str)
{
const string operation = nameof(InitSlot);
try
{
LogInfo(operation,
string.Format(
"Start. Slot={0}, ConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}",
slot,
cfg,
pwd,
req.HasValue ? req.Value.PortName : "NA",
str.HasValue ? str.Value.PortName : "NA"));
var existingMeter = GetMeterThreadSafe(slot);
if (existingMeter != null)
{
UpdateMeterForSlot(existingMeter, slot, cfg, pwd, req, str);
return SlotAlreadyExists(slot, existingMeter);
}
var meter = CreateMeterForSlot(slot, cfg, pwd, req, str);
//AddMeter(slot, meter);
return SlotCreated(slot, meter);
}
catch (Exception ex)
{
LogError(operation, ex);
return SlotFailed(slot, ex.Message);
}
}
public Task<PublicModels.GciInitSlotResult> UpdateSlotAsync(
int slot,
ConfigSource cfg,
PasswordSource pwd,
PortConfig? req,
PortConfig? str,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => UpdateSlot(slot, cfg, pwd, req, str), token);
}
public PublicModels.GciInitSlotResult UpdateSlot(
int slot,
ConfigSource cfg,
PasswordSource pwd,
PortConfig? req,
PortConfig? str)
{
const string operation = nameof(UpdateSlot);
try
{
LogInfo(operation,
string.Format(
"Start. Slot={0}, ConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}",
slot,
cfg,
pwd,
req.HasValue ? req.Value.PortName : "NA",
str.HasValue ? str.Value.PortName : "NA"));
var meter = GetMeterThreadSafe(slot);
if (meter == null)
{
return SlotFailed(slot, "Meter does not exist for this slot.");
}
if (meter.IsConnected)
{
return SlotFailed(slot, "Slot update is not allowed while meter is connected. Disconnect the meter first.");
}
UpdateMeterForSlot(meter, slot, cfg, pwd, req, str);
return SlotUpdated(slot, meter);
}
catch (Exception ex)
{
LogError(operation, ex);
return SlotFailed(slot, ex.Message);
}
}
private GenesisMeter CreateMeterForSlot(
int slot,
ConfigSource cfg,
PasswordSource pwd,
PortConfig? req,
PortConfig? str)
{
ZeroFlowGenesisMeter meter = new ZeroFlowGenesisMeter(0, 3, !(_meterControls is TempMeterStateControl), useForPreadjustmentUI: false);
meter.Slot = slot;
meter.useConfigSource = cfg;
meter.usePasswordSource = pwd;
meter.requestPortConfig = req;
meter.streamingPortConfig = str;
meter.onlineOperation = false;
lock (_meterBatchLock)
{
_meterBatch.AddMeter2(meter);
}
return meter;
}
private void UpdateMeterForSlot(
GenesisMeter meter,
int slot,
ConfigSource cfg,
PasswordSource pwd,
PortConfig? req,
PortConfig? str)
{
if (meter == null)
throw new ArgumentNullException(nameof(meter));
meter.Slot = slot;
meter.useConfigSource = cfg;
meter.usePasswordSource = pwd;
meter.requestPortConfig = req;
meter.streamingPortConfig = str;
//meter.SetupGenesisMeter(slot, req, str, true);
}
private PublicModels.GciInitSlotResult SlotCreated(int slot, GenesisMeter meter)
{
return new PublicModels.GciInitSlotResult
{
SlotId = slot,
Success = true,
Message = "Meter initialized.",
PcbId = meter != null ? meter.SerialNumber : null,
Created = true,
Updated = false,
AlreadyExists = false
};
}
private PublicModels.GciInitSlotResult SlotUpdated(int slot, GenesisMeter meter)
{
return new PublicModels.GciInitSlotResult
{
SlotId = slot,
Success = true,
Message = "Meter updated.",
PcbId = meter != null ? meter.SerialNumber : null,
Created = false,
Updated = true,
AlreadyExists = false
};
}
private PublicModels.GciInitSlotResult SlotAlreadyExists(int slot, GenesisMeter meter)
{
return new PublicModels.GciInitSlotResult
{
SlotId = slot,
Success = true,
Message = "Meter already exists. Meter config was only updated",
PcbId = meter != null ? meter.SerialNumber : null,
Created = false,
Updated = false,
AlreadyExists = true
};
}
private PublicModels.GciInitSlotResult SlotFailed(int slot, string message)
{
return new PublicModels.GciInitSlotResult
{
SlotId = slot,
Success = false,
Message = message,
PcbId = null,
Created = false,
Updated = false,
AlreadyExists = false
};
}
#endregion
#region ================================== Slot INFO ==================================
public Task<PublicModels.GciSlotInfo> GetOneMeterInfo(
int slot,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => GetOneMeterInfo(slot), token);
}
public PublicModels.GciSlotInfo GetOneMeterInfo(int slot)
{
const string operation = nameof(GetOneMeterInfo);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
if (meter == null)
{
return new PublicModels.GciSlotInfo
{
SlotId = slot,
Success = true,
Exists = false,
IsConnected = false,
IsLoggedOn = false,
Message = "Slot is empty."
};
}
return new PublicModels.GciSlotInfo
{
SlotId = slot,
Success = true,
Exists = true,
IsConnected = meter.IsConnected,
IsLoggedOn = meter.IsLoggedOn,
Message = "Slot found.",
PcbId = meter.PcbId,
Password = meter.Password,
ConfigSource = ModelsMapping.MapConfigSourceBack(meter.useConfigSource),
PasswordSource = ModelsMapping.MapPasswordSourceBack(meter.usePasswordSource),
RequestPort = ModelsMapping.MapPortBack(meter.requestPortConfig),
StreamingPort = ModelsMapping.MapPortBack(meter.streamingPortConfig)
};
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciSlotInfo
{
SlotId = slot,
Success = false,
Exists = false,
IsConnected = false,
IsLoggedOn = false,
Message = ex.Message
};
}
}
public Task<PublicModels.GciAllSlotsInfo> GetAllMetersInfo(
CancellationToken token = default)
{
return Task.Run(() => GetAllMetersInfo(), token);
}
public PublicModels.GciAllSlotsInfo GetAllMetersInfo()
{
const string operation = nameof(GetAllMetersInfo);
try
{
LogInfo(operation, "Start.");
lock (_meterBatchLock)
{
var result = new PublicModels.GciAllSlotsInfo
{
Success = true,
Message = "Slots read.",
Slots = _meterBatch.ListOfMeters
.OfType<GenesisMeter>()
.Select(meter => new PublicModels.GciSlotInfo
{
SlotId = meter.Slot,
Success = true,
Exists = true,
Message = "Slot found.",
PcbId = meter.PcbId,
ConfigSource = ModelsMapping.MapConfigSourceBack(meter.useConfigSource),
PasswordSource = ModelsMapping.MapPasswordSourceBack(meter.usePasswordSource),
RequestPort = ModelsMapping.MapPortBack(meter.requestPortConfig),
StreamingPort = ModelsMapping.MapPortBack(meter.streamingPortConfig)
})
.ToList()
};
return result;
}
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciAllSlotsInfo
{
Success = false,
Message = ex.Message,
Slots = new List<PublicModels.GciSlotInfo>()
};
}
}
public Task<PublicModels.GciCleanSlotResult> CleanSlotAsync(
int slot,
CancellationToken token = default)
{
return Task.Run(() => CleanSlot(slot), token);
}
#endregion
#region ================================== CLEAN ==================================
public PublicModels.GciCleanSlotResult CleanSlot(int slot)
{
const string operation = nameof(CleanSlot);
try
{
LogInfo(operation, string.Format("Start. Slot={0}", slot));
var meter = GetMeterThreadSafe(slot);
if (meter != null)
{
meter.DisposeMeter();
RemoveMeterThreadSafe(meter);
}
ApiWorker worker;
if (_workers.TryRemove(slot, out worker))
{
worker.Dispose();
}
return new PublicModels.GciCleanSlotResult
{
SlotId = slot,
Success = true,
Message = string.Format("Slot {0} cleaned.", slot)
};
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciCleanSlotResult
{
SlotId = slot,
Success = false,
Message = ex.Message
};
}
}
public Task<PublicModels.GciCleanAllSlotsResult> CleanAllSlotsAsync(
CancellationToken token = default)
{
return Task.Run(() => CleanAllSlots(), token);
}
public PublicModels.GciCleanAllSlotsResult CleanAllSlots()
{
const string operation = nameof(CleanAllSlots);
try
{
LogInfo(operation, "Start.");
DisposeMetersThreadSafe();
RemoveMetersThreadSafe();
RemoveWorkersThreadSafe();
return new PublicModels.GciCleanAllSlotsResult
{
Success = true,
Message = "Slots cleaned."
};
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciCleanAllSlotsResult
{
Success = false,
Message = ex.Message
};
}
}
#endregion
#region ================================== PORT DETECTION ==================================
public Task<PortDetectionResult> DetectStreamingPortAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
return GetWorker(slot).RunAsync(() => DetectStreamingPort(slot), token);
}
public PortDetectionResult DetectStreamingPort(int slot)
{
const string operation = nameof(DetectStreamingPort);
if (slot <= 0)
throw new ArgumentOutOfRangeException(nameof(slot), "Slot number must be greater than zero.");
try
{
LogInfo(operation, $"Start. Slot={slot}");
using (var mb = new MeterBatch())
using (var meter = new GenesisMeter())
{
mb.AddMeter(meter);
var rawData = new ConcurrentBag<string>();
meter.StreamingPort.OnRawRecordReceived += (o, rawMsg) =>
{
var data = (string)rawMsg.GetData();
rawData.Add(data);
};
Thread.Sleep(500);
bool success = rawData.Any();
string portName = meter.StreamingPort.GetPortName();
LogInfo(operation,
$"Finish. Slot={slot}, Success={success}, Port={SafePort(portName)}, RawRecords={rawData.Count}");
return new PortDetectionResult
{
Success = success,
Slot = slot,
PortName = portName,
ErrorMessage = success ? null : "No streaming data received."
};
}
}
catch (Exception ex)
{
LogError(operation, ex);
throw;
}
}
public Task<PortDetectionResult> DetectRequestPortAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
return GetWorker(slot).RunAsync(() => DetectRequestPort(slot), token);
}
public PortDetectionResult DetectRequestPort(int slot)
{
const string operation = nameof(DetectRequestPort);
if (slot <= 0)
throw new ArgumentOutOfRangeException(nameof(slot), "Slot number must be greater than zero.");
try
{
LogInfo(operation, $"Start. Slot={slot}");
using (var mb = new MeterBatch())
using (var meter = new GenesisMeter())
{
mb.AddMeter(meter);
meter.Logout();
string pcbId = meter.GetPcbId();
bool success = !string.IsNullOrEmpty(pcbId);
string portName = meter.RequestPort.GetPortName();
LogInfo(operation,
$"Finish. Slot={slot}, Success={success}, Port={SafePort(portName)}, PcbId={pcbId ?? "<empty>"}");
return new PortDetectionResult
{
Success = success,
Slot = slot,
PortName = portName,
PcbId = pcbId,
ErrorMessage = success ? null : "PCB ID was empty."
};
}
}
catch (Exception ex)
{
LogError(operation, ex);
throw;
}
}
#endregion
#region ================================== LOGIN ==================================
public Task<PublicModels.GciLoginResult> LoginOneSlotAsync(int slot, CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => LoginOneSlot(slot), token);
}
public PublicModels.GciLoginResult LoginOneSlot(int slot)
{
const string operation = nameof(LoginOneSlot);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
if (!meter.IsLoggedOn)
{
meter.Login();
if (meter.IsLoggedOn)
{
LogInfo(operation, $"New Slot {slot} login success.");
return new PublicModels.GciLoginResult
{
Success = true,
SlotId = slot,
IsLoggedOn = true,
Message = "Logged on now"
};
}
LogInfo(operation, $"Failed Slot {slot} login.");
return new PublicModels.GciLoginResult
{
Success = false,
SlotId = slot,
IsLoggedOn = false,
Message = string.IsNullOrWhiteSpace(meter.LastLoginError)
? "Login failed"
: meter.LastLoginError
};
}
var result = new PublicModels.GciLoginResult
{
Success = true,
SlotId = slot,
IsLoggedOn = true,
Message = "Already logged on"
};
LogInfo(operation, $"Slot={slot} already logged on.");
return result;
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciLoginResult
{
Success = false,
SlotId = slot,
IsLoggedOn = false,
Message = ex.Message
};
}
}
#endregion
#region ================================== CONNECT ==================================
public Task<PublicModels.GciConnectResult> ConnectOneSlotAsync(int slot, CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => ConnectOneSlot(slot), token);
}
public PublicModels.GciConnectResult ConnectOneSlot(int slot)
{
const string operation = nameof(ConnectOneSlot);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
if (!meter.IsConnected)
{
meter.SetupGenesisMeter(slot, meter.requestPortConfig, meter.streamingPortConfig, true);
meter.ConnectMeter();
LogInfo(operation, $"New Slot {slot} connection success.");
return new PublicModels.GciConnectResult
{
Success = true,
SlotId = slot,
IsConnected = meter.IsConnected,
Message = meter.IsConnected
? "Connected now"
: meter.LastConnectStatus
};
}
var result = new PublicModels.GciConnectResult
{
Success = true,
SlotId = slot,
IsConnected = true,
Message = "Already connected"
};
LogInfo(operation, $"Slot={slot} already connected.");
return result;
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciConnectResult
{
Success = false,
SlotId = slot,
IsConnected = false,
Message = ex.Message
};
}
}
private List<PublicModels.GciRegisterSnapshot> BuildRegisters(GenesisMeter meter)
{
var list = new List<GciRegisterSnapshot>();
foreach (var item in meter.GetRegistersDic())
{
var from = item.Key.RegisterDetail.Version.First?.ToString() ?? "-";
var to = item.Key.RegisterDetail.Version.Last?.ToString() ?? "-";
list.Add(new GciRegisterSnapshot
{
Name = item.Key.GetIdent(),
Type = item.Key.DataType.Name,
RawValue = BitConverter.ToString(item.Value).Replace("-", " "),
Min = item.Key.Minimum?.ToString(),
Max = item.Key.Maximum?.ToString(),
Description = item.Key.RegisterDetail.Description,
Version = $"from {from} to {to}",
IsAvailable = item.Key.IsAvailable.ToString(),
Privilege = item.Key.RegisterDetail.Privilege.Lvl8.ToString()
});
}
return list;
}
#endregion
#region ================================== DISCONNECT ==================================
public Task<PublicModels.GciDisconnectResult> DisconnectAsync(
int slot,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => Disconnect(slot), token);
}
public PublicModels.GciDisconnectResult Disconnect(int slot)
{
const string operation = nameof(Disconnect);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
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)
{
LogInfo(operation, $"Success. Slot={slot}");
}
else
{
LogInfo(
operation,
$"Disconnect incomplete. Slot={slot}, IsConnected={meter.IsConnected}, IsLoggedOn={meter.IsLoggedOn}");
}
//if (!meter.IsConnected && meter.IsLoggedOn)
// meter.ConnectMeter(); // repair of this situation
return new PublicModels.GciDisconnectResult
{
SlotId = slot,
Success = success,
Message = string.Join(
" | ",
new[]
{
meter.LastLogoutStatus,
meter.LastDisposeStatus,
success
? null
: $"Disconnect state invalid. IsConnected={meter.IsConnected}, IsLoggedOn={meter.IsLoggedOn}"
}
.Where(x => !string.IsNullOrWhiteSpace(x)))
};
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciDisconnectResult
{
SlotId = slot,
Success = false,
Message = ex.Message
};
}
finally
{
if (_workers.TryRemove(slot, out var worker))
{
worker.Dispose();
LogInfo(operation, $"Worker disposed. Slot={slot}");
}
}
}
#endregion
#region ================================== PCB ==================================
public Task<PublicModels.GciGetPcbIdResult> GetPcbIdAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
return GetWorker(slot).RunAsync(() => GetPcbId(slot), token, "GetPcbId");
}
public PublicModels.GciGetPcbIdResult GetPcbId(int slot)
{
const string operation = nameof(GetPcbId);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
//meter.Logout();
string pcbId = meter.GetPcbId();
bool isValid =
!string.IsNullOrWhiteSpace(pcbId) &&
pcbId.Length == 9;
LogInfo(operation, $"Finish. Slot={slot}, PcbId={pcbId ?? "<empty>"}");
return new PublicModels.GciGetPcbIdResult
{
SlotId = slot,
Success = isValid,
PcbId = pcbId,
Message = isValid
? "PCB ID read successfully."
: "Invalid length"
};
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciGetPcbIdResult
{
SlotId = slot,
Success = false,
PcbId = null,
Message = ex.Message
};
}
}
#endregion
#region ================================== REGISTER READ ==================================
public Task<RegisterReadResult> ReadRegisterAsync(int slot, string name, CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => ReadRegister(slot, name), token);
}
public RegisterReadResult ReadRegister(int slot, string name)
{
const string operation = nameof(ReadRegister);
try
{
LogInfo(operation, $"Start. Slot={slot}, Register={name}");
var meter = GetMeterThreadSafe(slot);
EnsureConnected(meter);
var raw = meter.ReadRegister(name);
bool success = raw != null && raw.Length > 0;
var result = new RegisterReadResult
{
Success = success,
RegisterName = name,
RawHex = ToHex(raw)
};
LogInfo(operation, $"Success. Slot={slot}, Register={name}, RawHex={result.RawHex}");
return result;
}
catch (Exception ex)
{
LogError(operation, ex);
return new RegisterReadResult
{
Success = false,
RegisterName = name,
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== REGISTER WRITE ==================================
public Task<RegisterWriteResult> WriteRegisterAsync(
int slot,
string registerName,
object value,
bool storeToDevice = false,
bool refreshSystemState = false,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => WriteRegister(slot, registerName, value, storeToDevice, refreshSystemState), token);
}
public RegisterWriteResult WriteRegister(
int slot,
string registerName,
object value,
bool storeToDevice = false,
bool refreshSystemState = false)
{
const string operation = nameof(WriteRegister);
try
{
LogInfo(operation,
$"Start. Slot={slot}, Register={registerName}, Value={value}, " +
$"StoreToDevice={storeToDevice}, RefreshSystemState={refreshSystemState}");
var meter = GetMeterThreadSafe(slot);
EnsureConnected(meter);
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name cannot be empty.", nameof(registerName));
bool writeOk = meter.WriteRegister(registerName, value);
if (!writeOk)
{
LogInfo(operation, $"Failed. Slot={slot}, Register={registerName}, Reason=Write operation failed.");
return new RegisterWriteResult
{
Success = false,
RegisterName = registerName,
WrittenValue = value,
ErrorMessage = "Write operation failed."
};
}
if (storeToDevice)
{
if (!meter.StoreAllConfigurations())
{
LogInfo(operation, $"Failed. Slot={slot}, Register={registerName}, Reason=StoreAllConfigurations failed.");
return new RegisterWriteResult
{
Success = false,
RegisterName = registerName,
WrittenValue = value,
StoreToDevice = true,
RefreshSystemState = refreshSystemState,
ErrorMessage = "StoreAllConfigurations failed."
};
}
}
if (refreshSystemState)
{
if (!meter.WriteRegister("SENSUSRADIO_SYSTEMSTATE", 0xFF, true, false))
{
LogInfo(operation, $"Failed. Slot={slot}, Register={registerName}, Reason=System state refresh failed.");
return new RegisterWriteResult
{
Success = false,
RegisterName = registerName,
WrittenValue = value,
StoreToDevice = storeToDevice,
RefreshSystemState = true,
ErrorMessage = "System state refresh failed."
};
}
}
LogInfo(operation, $"Success. Slot={slot}, Register={registerName}");
return new RegisterWriteResult
{
Success = true,
RegisterName = registerName,
WrittenValue = value,
StoreToDevice = storeToDevice,
RefreshSystemState = refreshSystemState
};
}
catch (Exception ex)
{
LogError(operation, ex);
return new RegisterWriteResult
{
Success = false,
RegisterName = registerName,
WrittenValue = value,
StoreToDevice = storeToDevice,
RefreshSystemState = refreshSystemState,
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PASSWORD ==================================
public Task<PublicModels.GciSetPasswordResult> SetMeterPasswordAsync(
int slot,
string password,
CancellationToken token = default)
{
return GetWorker(slot).RunAsync(() => SetMeterPassword(slot, password),
token,
"SetMeterPassword");
}
public PublicModels.GciSetPasswordResult SetMeterPassword(
int slot,
string password)
{
const string operation = nameof(SetMeterPassword);
try
{
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("Password cannot be empty.", nameof(password));
LogInfo(operation, $"Start. Slot={slot}");
GenesisMeter meter = GetMeterThreadSafe(slot);
meter?.SetPassword(password);//SetupFromExternConfig();
var result = new PublicModels.GciSetPasswordResult
{
SlotId = slot,
Success = true,
Password = meter.GetPassword_out(),
Message = "Password set successfully."
};
LogInfo(operation, $"Finish. Slot={slot}, Success={result.Success}");
return result;
}
catch (Exception ex)
{
LogError(operation, ex);
return new PublicModels.GciSetPasswordResult
{
SlotId = slot,
Success = false,
Message = ex.Message
};
}
}
#endregion
#region ================================== METER BATCH SETUP ==================================
public void ReloadSlotSetup()
{
const string operation = nameof(ReloadSlotSetup);
try
{
LogInfo(operation, "Start.");
_meterBatch.ListOfMeters.Clear();
_selectedSlots.Clear();
// TODO:
// Load meter batch setup from persistent storage.
// Example:
// _meterBatch.SetupFromConfigFile();
LogInfo(operation, "Success. Meter batch and selected slots cleared.");
}
catch (Exception ex)
{
LogError(operation, ex);
throw;
}
}
public void SaveSlotSetup(List<MeterBatchDebugStatus> data)
{
const string operation = nameof(SaveSlotSetup);
try
{
LogInfo(operation, $"Start. Rows={data?.Count ?? 0}");
if (data == null)
throw new ArgumentNullException(nameof(data));
_meterBatch.ListOfMeters.Clear();
_selectedSlots.Clear();
foreach (var item in data)
{
var meter = new GenesisMeter();
PortConfig? req = string.IsNullOrWhiteSpace(item.RequestPort)
? (PortConfig?)null
: new PortConfig { PortName = item.RequestPort, Type = "Serial" };
PortConfig? str = string.IsNullOrWhiteSpace(item.StreamingPort)
? (PortConfig?)null
: new PortConfig { PortName = item.StreamingPort, Type = "Serial" };
meter.useConfigSource = ConfigSource.InterfaceInputConfig;
meter.SetupFromExternConfig(item.Slot, req, str, true);
_meterBatch.AddMeter(meter);
_selectedSlots[item.Slot] = item.Selected;
LogInfo(
operation,
$"Saved row. Slot={item.Slot}, Selected={item.Selected}, " +
$"RequestPort={SafePort(item.RequestPort)}, StreamingPort={SafePort(item.StreamingPort)}");
}
LogInfo(operation, $"Success. MeterBatchCount={_meterBatch.ListOfMeters.Count}, SelectedSlots={_selectedSlots.Count}");
}
catch (Exception ex)
{
LogError(operation, ex);
throw;
}
}
private List<string> _cachedRegisters;
public List<string> GetAllRegisterNames()
{
if (_cachedRegisters != null)
return _cachedRegisters;
_cachedRegisters = typeof(Register)
.GetNestedTypes(BindingFlags.Public)
.SelectMany(t => t.GetFields(BindingFlags.Public | BindingFlags.Static))
.Where(f => f.FieldType == typeof(string))
.Select(f => f.GetValue(null)?.ToString())
.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct()
.OrderBy(x => x)
.ToList();
return _cachedRegisters;
}
#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
}
}