857 lines
28 KiB
C#
857 lines
28 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using GenesisCordonelInterface.Core.Threading;
|
|
using NLog;
|
|
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
|
|
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
|
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
|
|
|
|
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");
|
|
|
|
private readonly MeterBatch _meterBatch = new MeterBatch();
|
|
|
|
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 class WorkerDebugStatus
|
|
{
|
|
public int Slot { get; set; }
|
|
public string Name { get; set; }
|
|
public int QueueLength { get; set; }
|
|
public bool IsBusy { get; set; }
|
|
public string CurrentOperation { get; set; }
|
|
public string LastError { get; set; }
|
|
public DateTime LastActivity { get; set; }
|
|
}
|
|
|
|
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
|
|
{
|
|
return _workers
|
|
.Select(x => new WorkerDebugStatus
|
|
{
|
|
Slot = x.Key,
|
|
Name = x.Value.Name,
|
|
QueueLength = x.Value.QueueLength,
|
|
IsBusy = x.Value.IsBusy,
|
|
CurrentOperation = x.Value.CurrentOperation,
|
|
LastError = x.Value.LastError,
|
|
LastActivity = x.Value.LastActivity
|
|
})
|
|
.OrderBy(x => x.Slot)
|
|
.ToList();
|
|
}
|
|
#endregion
|
|
|
|
#region MeterBatch Debug
|
|
public class MeterBatchDebugStatus
|
|
{
|
|
public int Slot { get; set; }
|
|
public bool Selected { get; set; }
|
|
public string PcbId { get; set; }
|
|
public bool IsLoggedOn { get; set; }
|
|
public string RequestPort { get; set; }
|
|
public string StreamingPort { get; set; }
|
|
public string FwVersion { get; set; }
|
|
public string InterfaceVersion { get; set; }
|
|
}
|
|
|
|
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
|
|
{
|
|
return _meterBatch.ListOfMeters
|
|
.OfType<GenesisMeter>()
|
|
.Select(m => new MeterBatchDebugStatus
|
|
{
|
|
Slot = m.Slot,
|
|
Selected = IsSlotSelected(m.Slot),
|
|
PcbId = m.PcbId,
|
|
IsLoggedOn = m.IsLoggedOn,
|
|
RequestPort = m.RequestPort?.GetPortName(),
|
|
StreamingPort = m.StreamingPort?.GetPortName(),
|
|
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 GetMeter(int slot)
|
|
{
|
|
var meter = _meterBatch.ListOfMeters
|
|
.OfType<GenesisMeter>()
|
|
.FirstOrDefault(m => m.Slot == slot);
|
|
|
|
if (meter == null)
|
|
throw new InvalidOperationException($"Meter for slot {slot} not initialized.");
|
|
|
|
return meter;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region INIT
|
|
|
|
public class InitResult
|
|
{
|
|
public bool Success { get; set; }
|
|
public int Slot { get; set; }
|
|
public string ErrorMessage { get; set; }
|
|
}
|
|
|
|
public Task<InitResult> InitOneMeterFromExternAsync(
|
|
int slot,
|
|
ConfigSource cfg,
|
|
PasswordSource pwd,
|
|
PortConfig? req,
|
|
PortConfig? str,
|
|
CancellationToken token = default)
|
|
{
|
|
return GetWorker(slot).RunAsync(() =>
|
|
InitOneMeterFromExtern(slot, cfg, pwd, req, str), token);
|
|
}
|
|
|
|
public InitResult InitOneMeterFromExtern(
|
|
int slot,
|
|
ConfigSource cfg,
|
|
PasswordSource pwd,
|
|
PortConfig? req,
|
|
PortConfig? str)
|
|
{
|
|
const string operation = nameof(InitOneMeterFromExtern);
|
|
|
|
try
|
|
{
|
|
LogInfo(operation,
|
|
$"Start. Slot={slot}, ConfigSource={cfg}, PasswordSource={pwd}, " +
|
|
$"RequestPort={(req.HasValue ? req.Value.PortName.ToString() : "<empty>")}, " +
|
|
$"StreamingPort={(str.HasValue ? str.Value.PortName.ToString() : "<empty>")}");
|
|
|
|
var meter = new GenesisMeter
|
|
{
|
|
useConfigSource = cfg,
|
|
usePasswordSource = pwd
|
|
};
|
|
|
|
meter.SetupFromExternConfig(slot, req, str, true);
|
|
|
|
_meterBatch.AddMeter(meter);
|
|
|
|
LogInfo(operation, $"Success. Slot={slot}, MeterBatchCount={_meterBatch.ListOfMeters.Count}");
|
|
|
|
return new InitResult { Success = true, Slot = slot };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogError(operation, ex);
|
|
|
|
return new InitResult
|
|
{
|
|
Success = false,
|
|
Slot = slot,
|
|
ErrorMessage = ex.Message
|
|
};
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PORT DETECTION
|
|
|
|
public class PortDetectionResult
|
|
{
|
|
public bool Success { get; set; }
|
|
public int Slot { get; set; }
|
|
public string PortName { get; set; }
|
|
public string PcbId { get; set; }
|
|
public string ErrorMessage { get; set; }
|
|
}
|
|
|
|
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 CONNECT
|
|
|
|
public class ConnectResult
|
|
{
|
|
public bool Success { get; set; }
|
|
public int Slot { get; set; }
|
|
public string PcbId { get; set; }
|
|
public bool IsLoggedOn { get; set; }
|
|
|
|
public string FwVersion { get; set; }
|
|
public string InterfaceVersion { get; set; }
|
|
public bool InterfaceSupportsFwVersion { get; set; }
|
|
|
|
public List<RegisterSnapshot> Registers { get; set; } = new List<RegisterSnapshot>();
|
|
|
|
public string ErrorMessage { get; set; }
|
|
}
|
|
|
|
public class RegisterSnapshot
|
|
{
|
|
public string Name { get; set; }
|
|
public string Type { get; set; }
|
|
public string RawValue { get; set; }
|
|
public string Min { get; set; }
|
|
public string Max { get; set; }
|
|
public string Description { get; set; }
|
|
public string Version { get; set; }
|
|
public string IsAvailable { get; set; }
|
|
public string Privilege { get; set; }
|
|
}
|
|
|
|
public Task<ConnectResult> ConnectOneMeterAsync(int slot, CancellationToken token = default)
|
|
{
|
|
return GetWorker(slot).RunAsync(() => ConnectOneMeter(slot), token);
|
|
}
|
|
|
|
public ConnectResult ConnectOneMeter(int slot)
|
|
{
|
|
const string operation = nameof(ConnectOneMeter);
|
|
|
|
try
|
|
{
|
|
LogInfo(operation, $"Start. Slot={slot}");
|
|
|
|
var meter = GetMeter(slot);
|
|
|
|
_meterBatch.MetersLogin();
|
|
|
|
if (!meter.IsLoggedOn)
|
|
{
|
|
LogInfo(operation, $"Failed. Slot={slot}, Reason=Login failed.");
|
|
|
|
return new ConnectResult
|
|
{
|
|
Success = false,
|
|
Slot = slot,
|
|
ErrorMessage = "Login failed."
|
|
};
|
|
}
|
|
|
|
var result = new ConnectResult
|
|
{
|
|
Success = true,
|
|
Slot = slot,
|
|
PcbId = meter.PcbId,
|
|
IsLoggedOn = true,
|
|
FwVersion = meter.FwVersion,
|
|
InterfaceVersion = meter.InterfaceInfo?.InterfaceVersion,
|
|
InterfaceSupportsFwVersion = meter.InterfaceSupportsFwVersion,
|
|
Registers = BuildRegisters(meter)
|
|
};
|
|
|
|
LogInfo(operation,
|
|
$"Success. Slot={slot}, PcbId={result.PcbId ?? "<empty>"}, " +
|
|
$"FwVersion={result.FwVersion ?? "<empty>"}, InterfaceVersion={result.InterfaceVersion ?? "<empty>"}, " +
|
|
$"Registers={result.Registers.Count}");
|
|
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogError(operation, ex);
|
|
|
|
return new ConnectResult
|
|
{
|
|
Success = false,
|
|
Slot = slot,
|
|
ErrorMessage = ex.Message
|
|
};
|
|
}
|
|
}
|
|
|
|
private List<RegisterSnapshot> BuildRegisters(GenesisMeter meter)
|
|
{
|
|
var list = new List<RegisterSnapshot>();
|
|
|
|
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 RegisterSnapshot
|
|
{
|
|
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 PCB
|
|
|
|
public Task<string> GetPcbIdAsync(int slot, CancellationToken token = default(CancellationToken))
|
|
{
|
|
return GetWorker(slot).RunAsync(() => GetPcbId(slot), token, "GetPcbId");
|
|
}
|
|
|
|
public string GetPcbId(int slot)
|
|
{
|
|
const string operation = nameof(GetPcbId);
|
|
|
|
try
|
|
{
|
|
LogInfo(operation, $"Start. Slot={slot}");
|
|
|
|
using (var mb = new MeterBatch())
|
|
using (var meter = new GenesisMeter())
|
|
{
|
|
meter.SetupFromConfigFile(slot, false);
|
|
mb.AddMeter(meter);
|
|
meter.Logout();
|
|
|
|
string pcbId = meter.GetPcbId();
|
|
|
|
LogInfo(operation, $"Finish. Slot={slot}, PcbId={pcbId ?? "<empty>"}");
|
|
|
|
return pcbId;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogError(operation, ex);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region REGISTER READ
|
|
|
|
public class RegisterReadResult
|
|
{
|
|
public bool Success { get; set; }
|
|
public string RegisterName { get; set; }
|
|
public string RawHex { get; set; }
|
|
public string ErrorMessage { get; set; }
|
|
}
|
|
|
|
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 = GetMeter(slot);
|
|
EnsureConnected(meter);
|
|
|
|
var raw = meter.ReadRegister(name);
|
|
|
|
var result = new RegisterReadResult
|
|
{
|
|
Success = true,
|
|
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 class RegisterWriteResult
|
|
{
|
|
public bool Success { get; set; }
|
|
public string RegisterName { get; set; }
|
|
public object WrittenValue { get; set; }
|
|
public bool StoreToDevice { get; set; }
|
|
public bool RefreshSystemState { get; set; }
|
|
public string ErrorMessage { get; set; }
|
|
}
|
|
|
|
public Task<RegisterWriteResult> WriteRegisterAsync(
|
|
int slot,
|
|
string registerName,
|
|
object value,
|
|
bool storeToDevice = false,
|
|
bool refreshSystemState = false,
|
|
CancellationToken token = default(CancellationToken))
|
|
{
|
|
return GetWorker(slot).RunAsync(
|
|
() => WriteRegister(slot, registerName, value, storeToDevice, refreshSystemState),
|
|
token,
|
|
"WriteRegister");
|
|
}
|
|
|
|
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 = GetMeter(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 DISCONNECT
|
|
|
|
public Task DisconnectAsync(int slot, CancellationToken token = default)
|
|
{
|
|
return GetWorker(slot).RunAsync(() => Disconnect(slot), token);
|
|
}
|
|
|
|
public void Disconnect(int slot)
|
|
{
|
|
const string operation = nameof(Disconnect);
|
|
|
|
try
|
|
{
|
|
LogInfo(operation, $"Start. Slot={slot}");
|
|
|
|
var meter = GetMeter(slot);
|
|
|
|
if (meter.IsLoggedOn)
|
|
meter.Logout();
|
|
|
|
meter.DisposeMeter();
|
|
|
|
LogInfo(operation, $"Success. Slot={slot}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogError(operation, ex);
|
|
}
|
|
finally
|
|
{
|
|
if (_workers.TryRemove(slot, out var worker))
|
|
{
|
|
worker.Dispose();
|
|
LogInfo(operation, $"Worker disposed. Slot={slot}");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PASSWORD
|
|
|
|
public Task<bool> SetMeterPasswordAsync(
|
|
int slot,
|
|
string password,
|
|
CancellationToken token = default(CancellationToken))
|
|
{
|
|
return GetWorker(slot).RunAsync(
|
|
() => SetMeterPassword(slot, password),
|
|
token,
|
|
"SetMeterPassword");
|
|
}
|
|
|
|
public bool SetMeterPassword(int slot, string password)
|
|
{
|
|
const string operation = nameof(SetMeterPassword);
|
|
|
|
if (string.IsNullOrWhiteSpace(password))
|
|
throw new ArgumentException("Password cannot be empty.", nameof(password));
|
|
|
|
LogInfo(operation, $"Start. Slot={slot}");
|
|
|
|
var result = WriteRegister(
|
|
slot,
|
|
"SECURITY_Password",
|
|
password,
|
|
true,
|
|
true);
|
|
|
|
LogInfo(operation, $"Finish. Slot={slot}, Success={result.Success}");
|
|
|
|
return result.Success;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------
|
|
#endregion
|
|
}
|
|
} |