tbf/TBF/Rig/BridgeComponents/GciBridge/GciBridge.cs

722 lines
25 KiB
C#

using GenesisCordonelInterface.API;
using log4net;
///
/// Copyright (c) 2015-2021 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TBF.Rig.BridgeComponents.GciBridge.Interfaces;
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 static TBF.Rig.BridgeComponents.GciBridge.Interfaces.PublicModels;
using GciGUIType = GenesisCordonelInterface.UI.MainView;
using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
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;
namespace TBF.Rig.BridgeComponents.GciBridge
{
/// <summary>
/// TBF bridge component for integration with the sibling GCI project.
/// The component can be linked to UniDataStorage reader and writer components.
/// </summary>
public class GciBridge : ComponentBase
{
private static readonly ILog log = LogManager.GetLogger(typeof(GciBridge));
public override string ToString()
{
return string.Format("{0}({1})", ClassName, Cfg.ToString(-1));
}
readonly GciBridgeCfg gciBridgeCfg;
readonly UdsReaderType reader;
readonly UdsWriterType writer;
//diag GUI for GCI
GciGUIType gciGUI;
Form gciGuiHostForm;
//diag GUI for GciBridge
public UserControl gciBridgeGUI;
public MainForm gciBridgeGuiForm;
public GciType gciExternalInterface;
public bool HasReader { get { return reader != null; } }
public bool HasWriter { get { return writer != null; } }
public bool IsGuiInitialized { get { return gciGUI != null; } }
public bool IsExternalInitialized { get { return gciExternalInterface != null; } }
public GciType GciExternalInterface { get { return gciExternalInterface; } }
public GciBridgeCfg GciBridgeCfg { get { return gciBridgeCfg; } }
public UdsReaderType GetReader()
{
return reader;
}
public UdsWriterType GetWriter()
{
return writer;
}
public GciBridge() { }
public GciBridge(IComponentCfg cfg, IList<IComponent> components)
: base(cfg)
{
gciBridgeCfg = cfg as GciBridgeCfg;
if (gciBridgeCfg == null) throw new Exception("Invalid GciBridgeCfg.");
if (!string.IsNullOrEmpty(gciBridgeCfg.ReaderName))
{
reader = TbfComponents.FindComponent(gciBridgeCfg.ReaderName, components) as UdsReaderType;
if (reader == null) throw new Exception("Cannot find reader component '" + gciBridgeCfg.ReaderName + "'");
}
if (!string.IsNullOrEmpty(gciBridgeCfg.WriterName))
{
writer = TbfComponents.FindComponent(gciBridgeCfg.WriterName, components) as UdsWriterType;
if (writer == null) throw new Exception("Cannot find writer component '" + gciBridgeCfg.WriterName + "'");
}
Initialize();
}
public override void Initialize()
{
if (gciBridgeCfg.EnableExternalAccess)
{
TryInitializeExternalInterface();
}
if (gciBridgeCfg.EnableGuiAccess)
{
TryInitializeGciGui();
if (gciBridgeCfg.ShowGuiOnInitialize)
{
ShowGciGui();
}
}
log.FatalFormat("{0} initialized: {1}", Name, this);
}
void TryInitializeGciBridgeGui()
{
try
{
if (gciBridgeGuiForm != null && !gciBridgeGuiForm.IsDisposed)
return;
gciBridgeGuiForm = new MainForm(this);
gciBridgeGuiForm.Text = "Gci Bridge GUI";
gciBridgeGuiForm.Width = 1300;
gciBridgeGuiForm.Height = 600;
gciBridgeGuiForm.StartPosition = FormStartPosition.CenterScreen;
/*gciBridgeGUI = new MainView(this, gciBridgeGuiForm);
gciBridgeGUI.Dock = DockStyle.Fill;
gciBridgeGuiForm.Controls.Add(gciBridgeGUI);
gciBridgeGuiForm.FormClosed += (s, e) =>
{
gciBridgeGUI = null;
gciBridgeGuiForm = null;
};*/
log.InfoFormat("{0}: GciBridge GUI view initialized.", Name);
}
catch (Exception ex)
{
log.Error("Failed to initialize GciBridge GUI view.", ex);
}
}
void TryInitializeGciGui()
{
try
{
if (gciGuiHostForm != null && !gciGuiHostForm.IsDisposed)
return;
gciGUI = new GciGUIType();
gciGUI.Dock = DockStyle.Fill;
gciGuiHostForm = new Form();
gciGuiHostForm.Text = "Genesis Cordonel Interface";
gciGuiHostForm.Width = 1300;
gciGuiHostForm.Height = 600;
gciGuiHostForm.StartPosition = FormStartPosition.CenterScreen;
gciGuiHostForm.Controls.Add(gciGUI);
log.InfoFormat("{0}: GCI GUI view initialized.", Name);
}
catch (Exception ex)
{
log.Error("Failed to initialize GCI GUI view.", ex);
}
}
void TryInitializeExternalInterface()
{
try
{
if (gciExternalInterface != null)
return;
gciExternalInterface = new GciType();
log.InfoFormat("{0}: GCI external interface initialized.", Name);
}
catch (Exception ex)
{
log.Error("Failed to initialize GCI external interface.", ex);
}
}
public void ShowGciGui()
{
if (!gciBridgeCfg.EnableGuiAccess) return;
if (gciGuiHostForm == null || gciGuiHostForm.IsDisposed)
{
TryInitializeGciGui();
}
if (gciGuiHostForm == null) return;
gciGuiHostForm.Show();
gciGuiHostForm.BringToFront();
log.InfoFormat("{0}: ShowGciGui invoked.", Name);
}
public void HideGciGui()
{
if (!gciBridgeCfg.EnableGuiAccess) return;
if (gciGuiHostForm == null || gciGuiHostForm.IsDisposed) return;
gciGuiHostForm.Hide();
log.InfoFormat("{0}: HideGciGui invoked.", Name);
}
public void ShowGui()
{
if (!gciBridgeCfg.EnableGuiAccess) return;
if (gciBridgeGuiForm == null || gciBridgeGuiForm.IsDisposed)
{
TryInitializeGciBridgeGui();
}
if (gciBridgeGuiForm == null || gciBridgeGuiForm.IsDisposed)
return;
gciBridgeGuiForm.Show();
gciBridgeGuiForm.BringToFront();
log.InfoFormat("{0}: ShowGui invoked.", Name);
}
public void HideGui()
{
if (!gciBridgeCfg.EnableGuiAccess) return;
if (gciBridgeGuiForm == null || gciBridgeGuiForm.IsDisposed) return;
gciBridgeGuiForm.Hide();
log.InfoFormat("{0}: HideGciGui invoked.", Name);
}
void EnsureExternalInterface()
{
if (!gciBridgeCfg.EnableExternalAccess)
throw new Exception("GCI external access is disabled.");
if (!IsExternalInitialized)
TryInitializeExternalInterface();
if (gciExternalInterface == null)
throw new Exception("GCI external interface is not initialized.");
}
void EnsureReader()
{
if (reader == null)
throw new Exception("UniDataStorageReader is not linked to GciBridge.");
}
// API:
#region ======================================= GCI Public Interface =======================================
/*/// <summary>
/// Initializes one GCI slot through the external GCI interface.
///
/// Trace:
/// GciBridge.InitSlotAsync()
/// -> InterfaceOutsideToGCI.InitSlotAsync()
/// -> InterfaceGCIToLaatzen.InitOneMeterFromExternAsync()
/// -> per-slot GCI worker
/// -> GenesisMeter.SetupFromExternConfig()
/// </summary>
/// <param name="request">Public GCI slot initialization request.</param>
/// <returns>Initialization result for the requested slot.</returns>
public async Task<GciPublicModels.GciInitSlotResult> InitSlotAsync(GciPublicModels.GciInitSlotRequest request)
{
EnsureExternalInterface();
if (request == null)
throw new ArgumentNullException("request");
if (request.SlotId <= 0)
throw new ArgumentException("Invalid slot id.");
GciPublicModels.GciInitSlotResult result =
await gciExternalInterface.InitSlotAsync(request);
log.InfoFormat("{0}: InitSlotAsync invoked. {1}, Result={2}", Name, request, result);
return result;
}*/
/// <summary>
/// Initializes one GCI slot through the external GCI interface.
///
/// Trace:
/// GciBridge.InitSlotAsync()
/// -> InterfaceOutsideToGCI.InitSlotAsync()
/// -> InterfaceGCIToLaatzen.InitSlotAsync()
/// -> per-slot GCI worker
/// -> GenesisMeter.SetupFromExternConfig()
/// </summary>
/// <param name="request">Public GCI slot initialization request.</param>
/// <returns>Initialization result for the requested slot.</returns>
public async Task<GciPublicModels.GciInitSlotResult> InitSlotAsync(
GciPublicModels.GciInitSlotRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.SlotId <= 0)
throw new ArgumentException("Invalid slot id.");
if (string.Equals(request.RequestPort?.Type, "SIMULATE", StringComparison.OrdinalIgnoreCase))
{
log.InfoFormat("{0}: InitSlotAsync invoked. {1}, SIMULATE", Name, request);
await Task.Delay(500);
log.InfoFormat("{0}: InitSlotAsync SIMULATE finished.", Name);
return new GciPublicModels.GciInitSlotResult()
{
SlotId = request.SlotId,
Success = true,
AlreadyExists = false,
Updated = false,
Message = "Simulated",
};
}
EnsureExternalInterface();
var result = await gciExternalInterface.InitSlotAsync(request);
log.InfoFormat("{0}: InitSlotAsync invoked. {1}, Result={2}", Name, request, result);
return result;
}
/// <summary>
/// Updates an already initialized GCI slot.
///
/// Trace:
/// GciBridge.UpdateSlotAsync()
/// -> InterfaceOutsideToGCI.UpdateSlotAsync()
/// -> InterfaceGCIToLaatzen.UpdateSlotAsync()
/// -> per-slot GCI worker
/// -> GenesisMeter.SetupFromExternConfig()
/// </summary>
/// <param name="request">Public GCI slot update request.</param>
/// <returns>Update result for the requested slot.</returns>
public async Task<GciPublicModels.GciInitSlotResult> UpdateSlotAsync(
GciPublicModels.GciInitSlotRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.SlotId <= 0)
throw new ArgumentException("Invalid slot id.");
if (string.Equals(request.RequestPort?.Type, "SIMULATE", StringComparison.OrdinalIgnoreCase))
{
log.InfoFormat("{0}: UpdateSlotAsync invoked. {1}, SIMULATE", Name, request);
await Task.Delay(500);
log.InfoFormat("{0}: UpdateSlotAsync SIMULATE finished.", Name);
return new GciPublicModels.GciInitSlotResult()
{
SlotId = request.SlotId,
Success = true,
AlreadyExists = false,
Updated = true,
Message = "Simulated",
};
}
EnsureExternalInterface();
var result = await gciExternalInterface.UpdateSlotAsync(request);
log.InfoFormat("{0}: UpdateSlotAsync invoked. {1}, Result={2}", Name, request, result);
return result;
}
/// <summary>
/// Reads current information about one initialized GCI slot.
///
/// Trace:
/// GciBridge.GetSlotAsync()
/// -> InterfaceOutsideToGCI.GetSlotAsync()
/// -> InterfaceGCIToLaatzen.GetOneMeterInfo()
/// -> per-slot GCI worker
/// -> MeterBatch / GenesisMeter snapshot
/// </summary>
/// <param name="slotId">Slot number.</param>
/// <returns>Current public slot information.</returns>
public async Task<GciPublicModels.GciSlotInfo> GetSlotAsync(int slotId)
{
EnsureExternalInterface();
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
GciPublicModels.GciSlotInfo result =
await gciExternalInterface.GetSlotAsync(slotId);
log.InfoFormat("{0}: GetSlotAsync({1}) invoked. Result={2}", Name, slotId, result);
return result;
}
/// <summary>
/// Clears all initialized GCI slots and related GCI worker state.
///
/// Trace:
/// GciBridge.CleanSlotsAsync()
/// -> InterfaceOutsideToGCI.CleanSlotsAsync()
/// -> InterfaceGCIToLaatzen.CleanSlotsAsync()
/// -> MeterBatch cleanup
/// -> selected slot cleanup
/// -> worker cleanup
/// </summary>
/// <returns>Cleanup operation result.</returns>
public async Task<GciPublicModels.GciCleanSlotsResult> CleanSlotsAsync()
{
EnsureExternalInterface();
GciPublicModels.GciCleanSlotsResult result =
await gciExternalInterface.CleanSlotsAsync();
log.InfoFormat("{0}: CleanSlotsAsync invoked.", Name);
return result;
}
/// <summary>
/// Reads PCB ID from the meter assigned to the requested slot.
///
/// Trace:
/// GciBridge.GetPcbIdAsync()
/// -> InterfaceOutsideToGCI.GetPcbIdAsync()
/// -> InterfaceGCIToLaatzen.GetPcbIdAsync()
/// -> per-slot GCI worker
/// -> GenesisMeter.GetPcbId()
/// </summary>
/// <param name="slotId">Slot number.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>PCB ID read result.</returns>
public async Task<GciPublicModels.GciGetPcbIdResult> GetPcbIdAsync(
int slotId,
CancellationToken token = default)
{
EnsureExternalInterface();
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await gciExternalInterface.GetPcbIdAsync(slotId, token);
log.InfoFormat("{0}: GetPcbIdAsync({1}) invoked. Result={2}", Name, slotId, result);
return result;
}
/// <summary>
/// Connects/logs in to the meter assigned to the requested slot.
///
/// Trace:
/// GciBridge.ConnectAsync()
/// -> InterfaceOutsideToGCI.ConnectOneSlotAsync()
/// -> InterfaceGCIToLaatzen.ConnectOneMeterAsync()
/// -> per-slot GCI worker
/// -> MeterBatch.MetersLogin()
/// -> GenesisMeter register snapshot
/// </summary>
/// <param name="slotId">Slot number.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>Connection result including firmware/interface information.</returns>
public async Task<GciPublicModels.GciConnectResult> ConnectAsync(
int slotId,
CancellationToken token = default)
{
EnsureExternalInterface();
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await gciExternalInterface.ConnectOneSlotAsync(slotId, token);
log.InfoFormat("{0}: ConnectAsync({1}) invoked. Result={2}", Name, slotId, result);
return result;
}
public Task<GciPublicModels.GciLoginResult> LoginAsync(
int slot,
CancellationToken token = default)
{
return gciExternalInterface.LoginOneSlotAsync(slot, token);
}
/// <summary>
/// Disconnects/logs out from the meter assigned to the requested slot.
///
/// Trace:
/// GciBridge.DisconnectAsync()
/// -> InterfaceOutsideToGCI.DisconnectAsync()
/// -> InterfaceGCIToLaatzen.DisconnectAsync()
/// -> per-slot GCI worker
/// -> GenesisMeter.Logout()
/// -> GenesisMeter.DisposeMeter()
/// -> worker disposal
/// </summary>
/// <param name="slotId">Slot number.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>Disconnect operation result.</returns>
public async Task<GciPublicModels.GciDisconnectResult> DisconnectAsync(
int slotId,
CancellationToken token = default)
{
log.InfoFormat("{0}: DisconnectAsync({1}) invoked.", Name, slotId);
EnsureExternalInterface();
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await gciExternalInterface.DisconnectAsync(slotId, token);
log.InfoFormat("{0}: DisconnectAsync({1}) invoked. Result={2}", Name, slotId, result);
return result;
}
/// <summary>
/// Sets password to meter in given slot.
///
/// Trace:
/// GciBridge.SetPasswordAsync()
/// -> InterfaceOutsideToGCI.SetPasswordAsync()
/// -> InterfaceGCIToLaatzen.SetMeterPasswordAsync()
/// -> per-slot GCI worker
/// -> GenesisMeter.Password = password
/// </summary>
public async Task<GciPublicModels.GciSetPasswordResult> SetPasswordAsync(
int slotId,
string password,
CancellationToken token = default)
{
EnsureExternalInterface();
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("Password is empty.");
GciPublicModels.GciSetPasswordResult result =
await gciExternalInterface.SetPasswordAsync(slotId, password, token);
log.InfoFormat("{0}: SetPasswordAsync({1}) invoked. Result={2}", Name, slotId, result);
return result;
}
public async Task<GciPublicModels.RegisterReadResult> ReadRegisterAsync(
int slotId,
string registerName,
CancellationToken token = default)
{
EnsureExternalInterface();
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name is empty.");
var result = await gciExternalInterface.ReadRegisterAsync(slotId, registerName, token);
log.InfoFormat("{0}: ReadRegisterAsync({1}, {2}) invoked. Result={3}",
Name, slotId, registerName, result);
return result;
}
public async Task<GciPublicModels.RegisterWriteResult> WriteRegisterAsync(
int slotId,
string registerName,
object value,
bool storeToDevice = false,
bool refreshSystemState = false,
CancellationToken token = default)
{
EnsureExternalInterface();
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name is empty.");
var result = await gciExternalInterface.WriteRegisterAsync(
slotId,
registerName,
value,
storeToDevice,
refreshSystemState,
token);
log.InfoFormat("{0}: WriteRegisterAsync({1}, {2}, {3}) invoked. Result={4}",
Name, slotId, registerName, value, result);
return result;
}
#endregion
#region ======================================= UDSR Public Interface =======================================
/// <summary>
/// Reads password data from UniDataStorageReader by PCB ID.
///
/// Trace:
/// GciBridge.GetPasswordAsync()
/// -> UniDataStorageReader.Reader.GetDataFromStorageByParameterAsync()
/// -> Reader queue/lock
/// -> Reader.GetDataFromStorageByParameter()
/// -> selected storage reader by configuration
/// -> DatabaseReader.GetData() / RestApiReader.GetData() / JsonReader.GetData() / CsvReader.GetData()
/// </summary>
/// <param name="pcbId">PCB ID used as query parameter.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>Password lookup result.</returns>
public async Task<UdsPasswordResult> GetPasswordAsync(
string pcbId,
CancellationToken token = default)
{
EnsureReader();
if (string.IsNullOrWhiteSpace(pcbId))
throw new ArgumentException("PCB ID is empty.", nameof(pcbId));
try
{
UDSRPublicModels.DataQuery query = CreatePasswordQuery(pcbId);
object data = await reader.GetDataFromStorageByParameterAsync(query, token);
string password = ExtractPassword(data);
return new UdsPasswordResult
{
Success = !string.IsNullOrWhiteSpace(password),
PcbId = pcbId,
Password = password,
Message = !string.IsNullOrWhiteSpace(password)
? "Password found."
: "Password was not found."
};
}
catch (Exception ex)
{
log.Error("GetPasswordAsync failed.", ex);
return new UdsPasswordResult
{
Success = false,
PcbId = pcbId,
Password = null,
Message = ex.Message
};
}
}
#endregion
#region ======================================= Helpers =======================================
private UDSRPublicModels.DataQuery CreatePasswordQuery(string pcbId)
{
var query = new UDSRPublicModels.DataQuery();
query.QueryParams.Add(pcbId);
return query;
}
private string ExtractPassword(object data)
{
if (data == null)
return null;
if (data is string str)
return str;
if (data is DatabaseSearchResult result)
{
if (!result.Found)
return null;
if (result.Values != null &&
result.Values.TryGetValue("Password", out var value))
{
return value?.ToString();
}
return null;
}
// fallback
return data.ToString();
}
#endregion
public List<string> GetAllRegisterNames()
{
EnsureExternalInterface();
return gciExternalInterface.GetAllRegisterNames();
}
}
}