diff --git a/GenesisCordonelInterface/API/GciPublicModels.cs b/GenesisCordonelInterface/API/GciPublicModels.cs new file mode 100644 index 000000000..aee36896a --- /dev/null +++ b/GenesisCordonelInterface/API/GciPublicModels.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static GenesisCordonelInterface.API.InterfaceOutsideToGCI; +using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter; +using Xylem.Common.Hardware.Interfaces.Ports.PortCore; + +namespace GenesisCordonelInterface.API +{ + /// + /// Public data contract layer for the Genesis Cordonel Interface (GCI). + /// + /// This class defines all Data Transfer Objects (DTOs) that are exposed + /// to external consumers (e.g. TBF, UI, or other integration layers). + /// + /// Responsibilities: + /// - Provide stable, dependency-free models for external usage + /// - Decouple internal GCI implementation (GenesisMeter, Xylem libraries) + /// from external systems + /// - Define request/response contracts for all supported operations + /// - Contain mapping methods between public DTOs and internal domain models + /// + /// Architecture: + /// External world (TBF / UI) + /// ↓ + /// GciPublicModels (this layer) + /// ↓ + /// Internal GCI API (InterfaceGCIToLaatzen, GenesisMeter, etc.) + /// + /// Notes: + /// - Public models must NOT expose internal types (e.g. GenesisMeter, IPort, etc.) + /// - All mapping between internal and external representations must be done here + /// - DTOs are designed to be simple, serializable, and stable over time + /// - Any change in internal implementation should not affect these models + /// + /// Pattern: + /// Each operation follows a consistent structure: + /// Request → Operation → Result + /// + /// Example: + /// GciInitSlotRequest → InitSlot → GciInitSlotResult + /// GetSlot → GciSlotInfo + /// GetPcbId → GciGetPcbIdResult + /// + /// This layer acts as a boundary between domain logic and integration logic. + /// + public class GciPublicModels + { + /// + /// Public DTOs exposed to external systems. + /// These models represent the contract of the GCI API. + /// They must remain stable and independent of internal implementation. + /// + #region ================================== PUBLIC MODELS =========================================== + + public class GciSlotInfo + { + public int SlotId { get; set; } + + public bool Exists { get; set; } + public bool Success { get; set; } + public string Message { get; set; } + public string PcbId { get; set; } + + public GciConfigSource ConfigSource { get; set; } + public GciPasswordSource PasswordSource { get; set; } + + public GciPortConfig RequestPort { get; set; } + public GciPortConfig StreamingPort { get; set; } + } + + public class Result + { + public int SlotId { get; set; } + public bool Success { get; set; } + public string Message { get; set; } + + public override string ToString() + { + return string.Format( + "SlotId={0}, Success={1}, Message={2}", + SlotId, + Success, + Message); + } + } + + public enum GciPasswordSource + { + RestApi = 0, + OfflineFile = 1, + InterfaceInputPassword = 2, + } + public enum GciConfigSource + { + FileConfig = 0, + InterfaceInputConfig = 1, + } + + /// + /// Collection of port settings + /// + public class GciPortConfig + { + public string PortName { get; set; } + public string Type { get; set; } + + public override string ToString() + { + return string.Format("PortName={0}, Type={1}", PortName, Type); + } + } + + public class GciInitSlotRequest + { + public int SlotId { get; set; } + public GciConfigSource ConfigSource { get; set; } + public GciPasswordSource PasswordSource { get; set; } + public GciPortConfig RequestPort { get; set; } + public GciPortConfig StreamingPort { get; set; } + + public override string ToString() + { + return string.Format( + "SlotId={0}, GciConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}", + SlotId, + ConfigSource, + PasswordSource, + RequestPort, + StreamingPort); + } + } + + public class GciInitSlotResult + { + public int SlotId { get; set; } + + public bool Success { get; set; } + + public string Message { get; set; } + + public string PcbId { get; set; } + + public override string ToString() + { + return string.Format( + "SlotId={0}, Success={1}, Message={2}, PcbId={3}", + SlotId, + Success, + Message, + PcbId); + } + } + + public class GciCleanSlotsResult + { + public bool Success { get; set; } + public string Message { get; set; } + + public override string ToString() + { + return string.Format( + "Success={0}, Message={1}", + Success, + Message); + } + } + + public class GciGetPcbIdResult + { + public int SlotId { get; set; } + + public bool Success { get; set; } + + public string PcbId { get; set; } + + public string Message { get; set; } + + public override string ToString() + { + return string.Format( + "SlotId={0}, Success={1}, PcbId={2}, Message={3}", + SlotId, + Success, + PcbId, + Message); + } + } + public class GciConnectResult + { + public int SlotId { get; set; } + public bool Success { 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 Registers { get; set; } = new List(); + public string Message { get; set; } + } + + public class GciRegisterSnapshot + { + 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 class GciDisconnectResult + { + public int SlotId { get; set; } + public bool Success { get; set; } + public string Message { get; set; } + + public override string ToString() + { + return string.Format( + "SlotId={0}, Success={1}, Message={2}", + SlotId, + Success, + Message); + } + } + #endregion + + /// + /// Mapping methods between public DTOs and internal GCI models. + /// Ensures separation between external contracts and internal domain objects. + /// + #region ================================== OUTERN/INTERN and back models mapping ================================== + public static PasswordSource MapPasswordSource(GciPasswordSource src) + { + return (PasswordSource)src; + } + + public static ConfigSource MapConfigSource(GciConfigSource src) + { + return (ConfigSource)src; + } + + public static Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig? MapPort(GciPortConfig port) + { + if (port == null) + return null; + + Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig result = new Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig(); + + result.PortName = port.PortName; + result.Type = port.Type; + + return result; + } + + public static GciInitSlotResult MapInitResult(GciInitSlotResult result) + { + if (result == null) + return null; + + return new GciInitSlotResult + { + SlotId = result.SlotId, + Success = result.Success, + Message = result.Message + }; + } + + public static GciPasswordSource MapPasswordSourceBack(PasswordSource src) + { + return (GciPasswordSource)src; + } + + public static GciConfigSource MapConfigSourceBack(ConfigSource src) + { + return (GciConfigSource)src; + } + + public static GciPortConfig MapPortBack(PortConfig? port) + { + if (!port.HasValue) + return null; + + PortConfig value = port.Value; + + return new GciPortConfig + { + PortName = value.PortName, + Type = value.Type + }; + } + + public static GciPortConfig MapPortBack(IPort port) + { + if (port == null) + return null; + + return new GciPortConfig + { + PortName = port.GetPortName(), + Type = port.GetType().Name + }; + } + #endregion + } +} diff --git a/GenesisCordonelInterface/API/InterfaceGCIToLaatzen.cs b/GenesisCordonelInterface/API/InterfaceGCIToLaatzen.cs index 8e0fdefd3..e0fdc31e6 100644 --- a/GenesisCordonelInterface/API/InterfaceGCIToLaatzen.cs +++ b/GenesisCordonelInterface/API/InterfaceGCIToLaatzen.cs @@ -1,15 +1,16 @@ -using System; +using GenesisCordonelInterface.Core.Threading; +using NLog; +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 GenesisCordonelInterface.API.GciPublicModels; using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter; namespace GenesisCordonelInterface.API @@ -31,7 +32,7 @@ namespace GenesisCordonelInterface.API #endregion - #region Worker + #region ================================== Worker ================================== private ApiWorker GetWorker(int slot) { @@ -43,7 +44,7 @@ namespace GenesisCordonelInterface.API #endregion - #region Worker Debug + #region ================================== Worker Debug ================================== public class WorkerDebugStatus { public int Slot { get; set; } @@ -73,7 +74,7 @@ namespace GenesisCordonelInterface.API } #endregion - #region MeterBatch Debug + #region ================================== MeterBatch Debug ================================== public class MeterBatchDebugStatus { public int Slot { get; set; } @@ -134,7 +135,7 @@ namespace GenesisCordonelInterface.API } #endregion - #region Helpers + #region ================================== Helpers ================================== private GenesisMeter GetMeter(int slot) { @@ -178,40 +179,32 @@ namespace GenesisCordonelInterface.API #endregion - #region INIT + #region ================================== INIT ================================== - public class InitResult + public Task InitOneMeterFromExternAsync( + int slot, + ConfigSource cfg, + PasswordSource pwd, + PortConfig? req, + PortConfig? str, + CancellationToken token = default) { - public bool Success { get; set; } - public int Slot { get; set; } - public string ErrorMessage { get; set; } + return GetWorker(slot).RunAsync(() => InitOneMeterFromExtern(slot, cfg, pwd, req, str), token); } - public Task 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) + public GciPublicModels.GciInitSlotResult 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}, " + + $"Start. Slot={slot}, GciConfigSource={cfg}, PasswordSource={pwd}, " + $"RequestPort={(req.HasValue ? req.Value.PortName.ToString() : "")}, " + $"StreamingPort={(str.HasValue ? str.Value.PortName.ToString() : "")}"); @@ -227,24 +220,128 @@ namespace GenesisCordonelInterface.API LogInfo(operation, $"Success. Slot={slot}, MeterBatchCount={_meterBatch.ListOfMeters.Count}"); - return new InitResult { Success = true, Slot = slot }; + return new GciPublicModels.GciInitSlotResult { Success = true, SlotId = slot }; } catch (Exception ex) { LogError(operation, ex); - return new InitResult + return new GciPublicModels.GciInitSlotResult { Success = false, - Slot = slot, - ErrorMessage = ex.Message + SlotId = slot, + Message = ex.Message + }; + } + } + + public Task GetOneMeterInfo( + int slot, + CancellationToken token = default) + { + return GetWorker(slot).RunAsync(() => GetOneMeterInfo(slot), token); + } + + public GciPublicModels.GciSlotInfo GetOneMeterInfo(int slotId) + { + const string operation = nameof(GetOneMeterInfo); + + try + { + LogInfo(operation, $"Start. Slot={slotId}"); + + var meter = _meterBatch.ListOfMeters + .OfType() + .FirstOrDefault(m => m.Slot == slotId); + + if (meter == null) + { + return new GciPublicModels.GciSlotInfo + { + SlotId = slotId, + Success = true, + Exists = false, + Message = "Slot is empty." + }; + } + + return new GciPublicModels.GciSlotInfo + { + SlotId = slotId, + Success = true, + Exists = true, + Message = "Slot found.", + + // PcbId = meter.PcbId, + + ConfigSource = GciPublicModels.MapConfigSourceBack(meter.useConfigSource), + PasswordSource = GciPublicModels.MapPasswordSourceBack(meter.usePasswordSource), + + RequestPort = GciPublicModels.MapPortBack(meter.RequestPort), + StreamingPort = GciPublicModels.MapPortBack(meter.StreamingPort) + }; + } + catch (Exception ex) + { + LogError(operation, ex); + + return new GciPublicModels.GciSlotInfo + { + SlotId = slotId, + Success = false, + Exists = false, + Message = ex.Message + }; + } + } + + public Task CleanSlotsAsync( + CancellationToken token = default) + { + return Task.Run(() => CleanSlots(), token); + } + + public GciPublicModels.GciCleanSlotsResult CleanSlots() + { + const string operation = nameof(CleanSlots); + + try + { + LogInfo(operation, "Start."); + + _meterBatch.ListOfMeters.Clear(); + _selectedSlots.Clear(); + + foreach (var worker in _workers.Values) + { + worker.Dispose(); + } + + _workers.Clear(); + + LogInfo(operation, "Success. Meter batch, selected slots and workers cleared."); + + return new GciPublicModels.GciCleanSlotsResult + { + Success = true, + Message = "Slots cleaned." + }; + } + catch (Exception ex) + { + LogError(operation, ex); + + return new GciPublicModels.GciCleanSlotsResult + { + Success = false, + Message = ex.Message }; } } #endregion - #region PORT DETECTION + #region ================================== PORT DETECTION ================================== public class PortDetectionResult { @@ -361,45 +458,16 @@ namespace GenesisCordonelInterface.API #endregion - #region CONNECT + #region ================================== CONNECT ================================== - public class ConnectResult + public Task ConnectOneSlotAsync(int slot, CancellationToken token = default) { - 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 Registers { get; set; } = new List(); - - public string ErrorMessage { get; set; } + return GetWorker(slot).RunAsync(() => ConnectOneSlot(slot), token); } - public class RegisterSnapshot + public GciPublicModels.GciConnectResult ConnectOneSlot(int slot) { - 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 ConnectOneMeterAsync(int slot, CancellationToken token = default) - { - return GetWorker(slot).RunAsync(() => ConnectOneMeter(slot), token); - } - - public ConnectResult ConnectOneMeter(int slot) - { - const string operation = nameof(ConnectOneMeter); + const string operation = nameof(ConnectOneSlot); try { @@ -413,18 +481,18 @@ namespace GenesisCordonelInterface.API { LogInfo(operation, $"Failed. Slot={slot}, Reason=Login failed."); - return new ConnectResult + return new GciPublicModels.GciConnectResult { Success = false, - Slot = slot, - ErrorMessage = "Login failed." + SlotId = slot, + Message = "Login failed." }; } - var result = new ConnectResult + var result = new GciPublicModels.GciConnectResult { Success = true, - Slot = slot, + SlotId = slot, PcbId = meter.PcbId, IsLoggedOn = true, FwVersion = meter.FwVersion, @@ -444,25 +512,25 @@ namespace GenesisCordonelInterface.API { LogError(operation, ex); - return new ConnectResult + return new GciPublicModels.GciConnectResult { Success = false, - Slot = slot, - ErrorMessage = ex.Message + SlotId = slot, + Message = ex.Message }; } } - private List BuildRegisters(GenesisMeter meter) + private List BuildRegisters(GenesisMeter meter) { - var list = new List(); + var list = new List(); 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 + list.Add(new GciRegisterSnapshot { Name = item.Key.GetIdent(), Type = item.Key.DataType.Name, @@ -481,14 +549,72 @@ namespace GenesisCordonelInterface.API #endregion - #region PCB + #region ================================== DISCONNECT ================================== - public Task GetPcbIdAsync(int slot, CancellationToken token = default(CancellationToken)) + public Task DisconnectAsync( + int slot, + CancellationToken token = default) + { + return GetWorker(slot).RunAsync(() => Disconnect(slot), token); + } + + public GciPublicModels.GciDisconnectResult 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}"); + + return new GciPublicModels.GciDisconnectResult + { + SlotId = slot, + Success = true, + Message = "Disconnected successfully." + }; + } + catch (Exception ex) + { + LogError(operation, ex); + + return new GciPublicModels.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 GetPcbIdAsync( + int slot, + CancellationToken token = default(CancellationToken)) { return GetWorker(slot).RunAsync(() => GetPcbId(slot), token, "GetPcbId"); } - public string GetPcbId(int slot) + public GciPublicModels.GciGetPcbIdResult GetPcbId(int slot) { const string operation = nameof(GetPcbId); @@ -507,19 +633,34 @@ namespace GenesisCordonelInterface.API LogInfo(operation, $"Finish. Slot={slot}, PcbId={pcbId ?? ""}"); - return pcbId; + return new GciPublicModels.GciGetPcbIdResult + { + SlotId = slot, + Success = !string.IsNullOrWhiteSpace(pcbId), + PcbId = pcbId, + Message = !string.IsNullOrWhiteSpace(pcbId) + ? "PCB ID read successfully." + : "PCB ID is empty." + }; } } catch (Exception ex) { LogError(operation, ex); - throw; + + return new GciPublicModels.GciGetPcbIdResult + { + SlotId = slot, + Success = false, + PcbId = null, + Message = ex.Message + }; } } #endregion - #region REGISTER READ + #region ================================== REGISTER READ ================================== public class RegisterReadResult { @@ -573,7 +714,7 @@ namespace GenesisCordonelInterface.API #endregion - #region REGISTER WRITE + #region ================================== REGISTER WRITE ================================== public class RegisterWriteResult { @@ -700,47 +841,7 @@ namespace GenesisCordonelInterface.API #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 + #region ================================== PASSWORD ================================== public Task SetMeterPasswordAsync( int slot, @@ -776,7 +877,7 @@ namespace GenesisCordonelInterface.API #endregion - #region METER BATCH SETUP + #region ================================== METER BATCH SETUP ================================== // ---------------------------------------------------- public void ReloadSlotSetup() diff --git a/GenesisCordonelInterface/API/InterfaceOutsideToGCI.cs b/GenesisCordonelInterface/API/InterfaceOutsideToGCI.cs index 4101830df..e29aa1e92 100644 --- a/GenesisCordonelInterface/API/InterfaceOutsideToGCI.cs +++ b/GenesisCordonelInterface/API/InterfaceOutsideToGCI.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; +using System.IO.Ports; using System.Threading; using System.Threading.Tasks; -using Xylem.Common.Hardware.Interfaces.Ports.PortCore; -using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter; namespace GenesisCordonelInterface.API { @@ -15,10 +14,6 @@ namespace GenesisCordonelInterface.API { private readonly InterfaceGCIToLaatzen _innerMeterAPI; - private readonly Dictionary _requestPorts = new Dictionary(); - private readonly Dictionary _streamingPorts = new Dictionary(); - private readonly object _portLock = new object(); - public event Action> MeterBatchStatusChanged; public InterfaceOutsideToGCI() @@ -26,8 +21,7 @@ namespace GenesisCordonelInterface.API _innerMeterAPI = new InterfaceGCIToLaatzen(); } - #region PORT DETECTION - // ---------------------------------------------------- + #region ================================== PORT DETECTION ================================== public InterfaceGCIToLaatzen.PortDetectionResult DetectStreamingPort(int slot) { @@ -61,132 +55,96 @@ namespace GenesisCordonelInterface.API return result; } - // ---------------------------------------------------- #endregion - #region INIT - // ---------------------------------------------------- + #region ================================== INIT ================================== - public InterfaceGCIToLaatzen.InitResult InitOneMeterFromExtern( - int slot, - ConfigSource useConfigSource, - PasswordSource usePasswordSource, - PortConfig? requestPort, - PortConfig? streamingPort) + public async Task InitSlotAsync(GciPublicModels.GciInitSlotRequest request, CancellationToken token = default) { - var result = _innerMeterAPI.InitOneMeterFromExtern( - slot, - useConfigSource, - usePasswordSource, - requestPort, - streamingPort); + if (request == null) + throw new ArgumentNullException(nameof(request)); - RaiseMeterBatchStatusChanged(); - return result; - } - - public async Task InitOneMeterFromExternAsync( - int slot, - ConfigSource useConfigSource, - PasswordSource usePasswordSource, - PortConfig? requestPort, - PortConfig? streamingPort, - CancellationToken token = default(CancellationToken)) - { var result = await _innerMeterAPI.InitOneMeterFromExternAsync( - slot, - useConfigSource, - usePasswordSource, - requestPort, - streamingPort, + request.SlotId, + GciPublicModels.MapConfigSource(request.ConfigSource), + GciPublicModels.MapPasswordSource(request.PasswordSource), + GciPublicModels.MapPort(request.RequestPort), + GciPublicModels.MapPort(request.StreamingPort), token); RaiseMeterBatchStatusChanged(); + return result; } - public InterfaceGCIToLaatzen.InitResult InitSlotWithConfiguredPorts( - int slot, - ConfigSource useConfigSource, - PasswordSource usePasswordSource) + public async Task GetSlotAsync( + int slotId, + CancellationToken token = default) { - return InitOneMeterFromExtern( - slot, - useConfigSource, - usePasswordSource, - GetSlotRequestPort(slot), - GetSlotStreamingPort(slot)); + if (slotId <= 0) + throw new ArgumentException("Invalid slot id."); + + var result = await _innerMeterAPI.GetOneMeterInfo(slotId, token); + + return result; } - public Task InitSlotWithConfiguredPortsAsync( - int slot, - ConfigSource useConfigSource, - PasswordSource usePasswordSource, - CancellationToken token = default(CancellationToken)) + public async Task CleanSlotsAsync( + CancellationToken token = default) { - return InitOneMeterFromExternAsync( - slot, - useConfigSource, - usePasswordSource, - GetSlotRequestPort(slot), - GetSlotStreamingPort(slot), - token); - } + var result = await _innerMeterAPI.CleanSlotsAsync(token); - // ---------------------------------------------------- + RaiseMeterBatchStatusChanged(); + + return result; + } #endregion - #region CONNECTION - // ---------------------------------------------------- + #region ================================== CONNECTION ================================== - public InterfaceGCIToLaatzen.ConnectResult ConnectOneMeter(int slot) + public async Task ConnectOneSlotAsync( + int slot, + CancellationToken token = default) { - var result = _innerMeterAPI.ConnectOneMeter(slot); + GciPublicModels.GciConnectResult result = await _innerMeterAPI.ConnectOneSlotAsync(slot, token); + RaiseMeterBatchStatusChanged(); + return result; } - public async Task ConnectOneMeterAsync( - int slot, - CancellationToken token = default(CancellationToken)) + public async Task DisconnectAsync( + int slot, + CancellationToken token = default) { - var result = await _innerMeterAPI.ConnectOneMeterAsync(slot, token); + if (slot <= 0) + throw new ArgumentException("Invalid slot id."); + + var result = await _innerMeterAPI.DisconnectAsync(slot, token); + RaiseMeterBatchStatusChanged(); + return result; } - - public void Disconnect(int slot) - { - _innerMeterAPI.Disconnect(slot); - RaiseMeterBatchStatusChanged(); - } - - public async Task DisconnectAsync( - int slot, - CancellationToken token = default(CancellationToken)) - { - await _innerMeterAPI.DisconnectAsync(slot, token); - RaiseMeterBatchStatusChanged(); - } - - // ---------------------------------------------------- #endregion - #region READ + #region ================================== PCB ================================== + public async Task GetPcbIdAsync( + int slot, + CancellationToken token = default) + { + if (slot <= 0) + throw new ArgumentException("Invalid slot id."); + + var result = await _innerMeterAPI.GetPcbIdAsync(slot, token); + + return result; + } + #endregion + + #region ================================== READ ================================== // ---------------------------------------------------- - public string GetPcbId(int slot) - { - return _innerMeterAPI.GetPcbId(slot); - } - - public Task GetPcbIdAsync( - int slot, - CancellationToken token = default(CancellationToken)) - { - return _innerMeterAPI.GetPcbIdAsync(slot, token); - } - public InterfaceGCIToLaatzen.RegisterReadResult ReadRegister( int slot, string registerName) @@ -205,7 +163,7 @@ namespace GenesisCordonelInterface.API // ---------------------------------------------------- #endregion - #region WRITE + #region ================================== WRITE ================================== // ---------------------------------------------------- public async Task WriteRegisterAsync( @@ -261,7 +219,7 @@ namespace GenesisCordonelInterface.API // ---------------------------------------------------- #endregion - #region DEBUG STATUS + #region ================================== DEBUG STATUS ================================== // ---------------------------------------------------- public List GetWorkerDebugStatuses() @@ -286,7 +244,7 @@ namespace GenesisCordonelInterface.API // ---------------------------------------------------- #endregion - #region SLOT SELECTION + #region ================================== SLOT SELECTION ================================== // ---------------------------------------------------- public void SetSlotSelected(int slot, bool selected) @@ -308,10 +266,10 @@ namespace GenesisCordonelInterface.API // ---------------------------------------------------- #endregion - #region SLOT PORT CONFIG + #region ================================== SLOT PORT CONFIG ================================== // ---------------------------------------------------- - public void SetSlotRequestPort(int slot, string portName) + /*public void SetSlotRequestPort(int slot, string portName) { lock (_portLock) { @@ -321,7 +279,7 @@ namespace GenesisCordonelInterface.API } else { - _requestPorts[slot] = new PortConfig + _requestPorts[slot] = new GciPortConfig { PortName = portName, Type = "Serial" @@ -342,7 +300,7 @@ namespace GenesisCordonelInterface.API } else { - _streamingPorts[slot] = new PortConfig + _streamingPorts[slot] = new GciPortConfig { PortName = portName, Type = "Serial" @@ -353,11 +311,11 @@ namespace GenesisCordonelInterface.API RaiseMeterBatchStatusChanged(); } - public PortConfig? GetSlotRequestPort(int slot) + public GciPortConfig? GetSlotRequestPort(int slot) { lock (_portLock) { - PortConfig port; + GciPortConfig port; if (_requestPorts.TryGetValue(slot, out port)) return port; @@ -365,22 +323,22 @@ namespace GenesisCordonelInterface.API } } - public PortConfig? GetSlotStreamingPort(int slot) + public GciPortConfig? GetSlotStreamingPort(int slot) { lock (_portLock) { - PortConfig port; + GciPortConfig port; if (_streamingPorts.TryGetValue(slot, out port)) return port; return null; } - } + }*/ // ---------------------------------------------------- #endregion - #region METER BATCH SETUP + #region ================================== METER BATCH SETUP ================================== // ---------------------------------------------------- public void ReloadSlotSetup() diff --git a/GenesisCordonelInterface/GenesisCordonelInterface.csproj b/GenesisCordonelInterface/GenesisCordonelInterface.csproj index f619b0610..b9ba6997d 100644 --- a/GenesisCordonelInterface/GenesisCordonelInterface.csproj +++ b/GenesisCordonelInterface/GenesisCordonelInterface.csproj @@ -58,6 +58,7 @@ + diff --git a/GenesisCordonelInterface/UI/Debug/MeterBatchConfigPanel.cs b/GenesisCordonelInterface/UI/Debug/MeterBatchConfigPanel.cs index a59fd9c4f..a877d4f41 100644 --- a/GenesisCordonelInterface/UI/Debug/MeterBatchConfigPanel.cs +++ b/GenesisCordonelInterface/UI/Debug/MeterBatchConfigPanel.cs @@ -266,13 +266,13 @@ namespace GenesisCordonelInterface.UI.Debug if (columnName == "RequestPort") { - api.SetSlotRequestPort(slot, port); + //api.SetSlotRequestPort(slot, port); return; } if (columnName == "StreamingPort") { - api.SetSlotStreamingPort(slot, port); + //api.SetSlotStreamingPort(slot, port); return; } } diff --git a/GenesisCordonelInterface/UI/LaatzenAPI_GenesisToolBox/FrmRegisterStore.cs b/GenesisCordonelInterface/UI/LaatzenAPI_GenesisToolBox/FrmRegisterStore.cs index 7b7de7756..c66250f39 100644 --- a/GenesisCordonelInterface/UI/LaatzenAPI_GenesisToolBox/FrmRegisterStore.cs +++ b/GenesisCordonelInterface/UI/LaatzenAPI_GenesisToolBox/FrmRegisterStore.cs @@ -492,7 +492,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox DisableAllButtons(); _dataTable.Rows.Clear(); - var result = await interfaceToLaatzen.ConnectOneMeterAsync(slotNo); + var result = await interfaceToLaatzen.ConnectOneSlotAsync(slotNo); if (result.Success) { @@ -571,7 +571,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox lblState.Text = $@"Not Connected to PcbId:{result.PcbId}"; registerGridView.Visible = false; - MessageBox.Show(result.ErrorMessage ?? "Connect failed.", @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(result.Message ?? "Connect failed.", @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception ex) @@ -1172,11 +1172,16 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox SetBusy(true, "GetPcbId"); - // as new meter object will be generated the data grid shows outdated data _dataTable.Rows.Clear(); DisableAllButtons(); - _currentPcbId = await Task.Run(() => interfaceToLaatzen.GetPcbId(slotNr)); + GciPublicModels.GciGetPcbIdResult result = + await interfaceToLaatzen.GetPcbIdAsync(slotNr); + + if (!result.Success) + throw new Exception(result.Message); + + _currentPcbId = result.PcbId; btnConnect.Enabled = true; diff --git a/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/FrmGCIAPI.Designer.cs b/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/FrmGCIAPI.Designer.cs index c5a97d57f..5a760221d 100644 --- a/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/FrmGCIAPI.Designer.cs +++ b/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/FrmGCIAPI.Designer.cs @@ -82,7 +82,7 @@ this.lblConfigSource.Name = "lblConfigSource"; this.lblConfigSource.Size = new System.Drawing.Size(75, 13); this.lblConfigSource.TabIndex = 2; - this.lblConfigSource.Text = "ConfigSource:"; + this.lblConfigSource.Text = "GciConfigSource:"; // // cmbConfigSource // diff --git a/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/FrmGCIAPI.cs b/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/FrmGCIAPI.cs index 619dcf41e..d56f7d441 100644 --- a/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/FrmGCIAPI.cs +++ b/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/FrmGCIAPI.cs @@ -160,9 +160,15 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface } /// - /// Creates PortConfig from UI values. + /// Creates a public GCI port configuration object from UI input values. /// - private PortConfig? CreatePortConfig(string portName, string baudRateText) + /// Name of the port (e.g. COM3). + /// Baud rate entered in the UI. + /// + /// Instance of or null if port name is empty. + /// + /// Thrown when baud rate is invalid. + private GciPublicModels.GciPortConfig CreateGciPortConfig(string portName, string baudRateText) { if (string.IsNullOrWhiteSpace(portName)) return null; @@ -170,35 +176,37 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface if (!int.TryParse(baudRateText, out int baudRate)) throw new Exception($"Invalid baud rate for port {portName}."); - var cfg = new PortConfig + return new GciPublicModels.GciPortConfig { PortName = portName, - Type = "Serial" + Type = "Serial", }; - - var sp = cfg.GetSerialPort(); - if (sp == null) - throw new Exception($"Serial port object was not created for {portName}."); - - sp.BaudRate = baudRate; - - return cfg; } - private ConfigSource GetConfigSource() + /// + /// Gets selected configuration source from UI and maps it to public GCI model. + /// + /// Selected . + /// Thrown when no value is selected. + private GciPublicModels.GciConfigSource GetConfigSource() { if (cmbConfigSource.SelectedItem == null) - throw new Exception("ConfigSource is not selected."); + throw new Exception("Config source is not selected."); - return (ConfigSource)cmbConfigSource.SelectedItem; + return (GciPublicModels.GciConfigSource)cmbConfigSource.SelectedItem; } - private PasswordSource GetPasswordSource() + /// + /// Gets selected password source from UI and maps it to public GCI model. + /// + /// Selected . + /// Thrown when no value is selected. + private GciPublicModels.GciPasswordSource GetPasswordSource() { if (cmbPasswordSource.SelectedItem == null) - throw new Exception("PasswordSource is not selected."); + throw new Exception("Password source is not selected."); - return (PasswordSource)cmbPasswordSource.SelectedItem; + return (GciPublicModels.GciPasswordSource)cmbPasswordSource.SelectedItem; } #endregion @@ -221,15 +229,19 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface await ExecuteApiActionAsync(async () => { - var result = await _api.InitOneMeterFromExternAsync( - slot, - GetConfigSource(), - GetPasswordSource(), - CreatePortConfig(txtRequestPort.Text, txtRequestBaudRate.Text), - CreatePortConfig(txtStreamingPort.Text, txtStreamingBaudRate.Text)); + var request = new GciPublicModels.GciInitSlotRequest + { + SlotId = slot, + ConfigSource = GetConfigSource(), + PasswordSource = GetPasswordSource(), + RequestPort = CreateGciPortConfig(txtRequestPort.Text, txtRequestBaudRate.Text), + StreamingPort = CreateGciPortConfig(txtStreamingPort.Text, txtStreamingBaudRate.Text) + }; + + var result = await _api.InitSlotAsync(request); if (!result.Success) - throw new Exception(result.ErrorMessage); + throw new Exception(result.Message); MessageBox.Show("Meter initialized."); }); @@ -251,10 +263,10 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface await ExecuteApiActionAsync(async () => { - var result = await _api.ConnectOneMeterAsync(slot); + var result = await _api.ConnectOneSlotAsync(slot); if (!result.Success) - throw new Exception(result.ErrorMessage); + throw new Exception(result.Message); MessageBox.Show($"Connected.\r\nPCB ID: {result.PcbId}"); }); @@ -280,7 +292,6 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface MessageBox.Show("Disconnected."); }); } - /// /// Reads PCB ID asynchronously. /// @@ -297,8 +308,13 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface await ExecuteApiActionAsync(async () => { - string pcbId = await _api.GetPcbIdAsync(slot); - MessageBox.Show($"PCB ID: {pcbId}"); + GciPublicModels.GciGetPcbIdResult result = + await _api.GetPcbIdAsync(slot); + + if (!result.Success) + throw new Exception(result.Message); + + MessageBox.Show($"PCB ID: {result.PcbId}"); }); } @@ -345,10 +361,10 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface foreach (int slot in slots) { - var result = await _api.ConnectOneMeterAsync(slot); + var result = await _api.ConnectOneSlotAsync(slot); if (!result.Success) - throw new Exception($"Slot {slot}: {result.ErrorMessage}"); + throw new Exception($"Slot {slot}: {result.Message}"); } MessageBox.Show("Selected meters connected."); diff --git a/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/MetersActionView.cs b/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/MetersActionView.cs index 563925bcf..a3ff9046b 100644 --- a/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/MetersActionView.cs +++ b/GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/MetersActionView.cs @@ -66,10 +66,10 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface foreach (int slot in _api.GetSelectedSlots()) { - var result = await _api.ConnectOneMeterAsync(slot); + var result = await _api.ConnectOneSlotAsync(slot); if (!result.Success) - throw new Exception($"Slot {slot}: {result.ErrorMessage}"); + throw new Exception($"Slot {slot}: {result.Message}"); } MessageBox.Show("Selected meters connected."); diff --git a/TBF/Build/CopyGci.targets.xml b/TBF/Build/CopyGci.targets.xml new file mode 100644 index 000000000..9eb4197ee --- /dev/null +++ b/TBF/Build/CopyGci.targets.xml @@ -0,0 +1,35 @@ + + + + + + + + $(ProjectDir)..\GenesisCordonelInterface\RuntimePackage\Package\ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TBF/Rig/BridgeComponents/GciBridge/GciBridge.cs b/TBF/Rig/BridgeComponents/GciBridge/GciBridge.cs index fc9111e22..7aed00bb4 100644 --- a/TBF/Rig/BridgeComponents/GciBridge/GciBridge.cs +++ b/TBF/Rig/BridgeComponents/GciBridge/GciBridge.cs @@ -1,13 +1,16 @@ -using log4net; +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.Generic; -using ExternalInterfaceGUIType = GenesisCordonelInterface.UI.MainView; -using ExternalInterfaceType = GenesisCordonelInterface.API.InterfaceOutsideToGCI; +using GciGUIType = GenesisCordonelInterface.UI.MainView; +using GciType = GenesisCordonelInterface.API.InterfaceOutsideToGCI; using UdsReaderType = TBF.Rig.Input.DataStorage.UniDataStorageReader.Reader; using UdsWriterType = TBF.Rig.Output.DataStorage.UniDataStorageWriter.Writer; @@ -20,42 +23,37 @@ namespace TBF.Rig.BridgeComponents.GciBridge 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)); } + + public override string ToString() + { + return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); + } readonly GciBridgeCfg gciBridgeCfg; - /// - /// Linked UniDataStorage reader component. - /// readonly UdsReaderType reader; - - /// - /// Linked UniDataStorage writer component. - /// readonly UdsWriterType writer; - /// - /// Placeholder for GCI GUI entry point. - /// Replace object with real GCI MainForm type later. - /// - ExternalInterfaceGUIType gciGUI; - - /// - /// - /// + GciGUIType gciGUI; Form gciGuiHostForm; - /// - /// Placeholder for GCI external/public interface. - /// Replace object with real GCI interface type later. - /// - ExternalInterfaceType gciExternalInterface; + 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 UdsReaderType GetReader() + { + return reader; + } + + public UdsWriterType GetWriter() + { + return writer; + } + public GciBridge() { } public GciBridge(IComponentCfg cfg, IList components) @@ -97,10 +95,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge log.FatalFormat("{0} initialized: {1}", Name, this); } - /// - /// Initializes access to GCI GUI. - /// Replace placeholder implementation with real MainForm creation. - /// void TryInitializeGui() { try @@ -108,7 +102,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge if (gciGuiHostForm != null && !gciGuiHostForm.IsDisposed) return; - gciGUI = new ExternalInterfaceGUIType(); + gciGUI = new GciGUIType(); gciGUI.Dock = DockStyle.Fill; gciGuiHostForm = new Form(); @@ -127,15 +121,14 @@ namespace TBF.Rig.BridgeComponents.GciBridge } } - /// - /// Initializes access to GCI external/public interface. - /// Replace placeholder implementation with real interface creation. - /// void TryInitializeExternalInterface() { try { - gciExternalInterface = new ExternalInterfaceType(); + if (gciExternalInterface != null) + return; + + gciExternalInterface = new GciType(); log.InfoFormat("{0}: GCI external interface initialized.", Name); } @@ -145,9 +138,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge } } - /// - /// Shows GCI GUI if GUI access is enabled. - /// public void ShowGui() { if (!gciBridgeCfg.EnableGuiAccess) return; @@ -165,9 +155,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge log.InfoFormat("{0}: ShowGui invoked.", Name); } - /// - /// Hides GCI GUI if GUI access is enabled. - /// public void HideGui() { if (!gciBridgeCfg.EnableGuiAccess) return; @@ -178,50 +165,120 @@ namespace TBF.Rig.BridgeComponents.GciBridge log.InfoFormat("{0}: HideGui invoked.", Name); } - /// - /// Connects the external GCI interface. - /// - public void ConnectExternal() + void EnsureExternalInterface() { - if (!gciBridgeCfg.EnableExternalAccess) return; + if (!gciBridgeCfg.EnableExternalAccess) + throw new Exception("GCI external access is disabled."); if (!IsExternalInitialized) - { TryInitializeExternalInterface(); - } - /// TODO: - /// Replace with real external interface connect call. - log.InfoFormat("{0}: ConnectExternal invoked.", Name); + if (gciExternalInterface == null) + throw new Exception("GCI external interface is not initialized."); } - /// - /// Disconnects the external GCI interface. - /// - public void DisconnectExternal() + void EnsureReader() { - if (!gciBridgeCfg.EnableExternalAccess) return; - if (!IsExternalInitialized) return; - - /// TODO: - /// Replace with real external interface disconnect call. - log.InfoFormat("{0}: DisconnectExternal invoked.", Name); + if (reader == null) + throw new Exception("UniDataStorageReader is not linked to GciBridge."); } - /// - /// Returns linked reader component. - /// - public UdsReaderType GetReader() + // API: + #region ======================================= GCI Public Interface ======================================= + + public async Task InitSlotAsync(GciPublicModels.GciInitSlotRequest request) { - return reader; + 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; } - /// - /// Returns linked writer component. - /// - public UdsWriterType GetWriter() + public async Task GetSlotAsync(int slotId) { - return writer; + 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; } + + public async Task CleanSlotsAsync() + { + EnsureExternalInterface(); + + GciPublicModels.GciCleanSlotsResult result = + await gciExternalInterface.CleanSlotsAsync(); + + log.InfoFormat("{0}: CleanSlotsAsync invoked.", Name); + + return result; + } + + public async Task 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; + } + + public async Task 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 async Task DisconnectAsync( + int slotId, + CancellationToken token = default) + { + 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; + } + + #endregion } } \ No newline at end of file diff --git a/TBF/Rig/BridgeComponents/GciBridge/GciBridgeCfgCtrl.cs b/TBF/Rig/BridgeComponents/GciBridge/GciBridgeCfgCtrl.cs index 346669b87..ab7787721 100644 --- a/TBF/Rig/BridgeComponents/GciBridge/GciBridgeCfgCtrl.cs +++ b/TBF/Rig/BridgeComponents/GciBridge/GciBridgeCfgCtrl.cs @@ -182,7 +182,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge return; } - bridge.ConnectExternal(); + //bridge.ConnectExternal(); } catch (Exception ex) { diff --git a/TBF/Rig/BridgeComponents/GciBridge/GciBridgeOp.cs b/TBF/Rig/BridgeComponents/GciBridge/GciBridgeOp.cs new file mode 100644 index 000000000..a318dc1b6 --- /dev/null +++ b/TBF/Rig/BridgeComponents/GciBridge/GciBridgeOp.cs @@ -0,0 +1,14 @@ +using log4net; +/// +/// Copyright (c) 2015-2021 Sensus Slovensko a.s. +/// +using System; +using TBF.Rig.Sequences; + +namespace TBF.Rig.BridgeComponents.GciBridge +{ + public class GciBridgeOp + { + + } +} \ No newline at end of file diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index d3385c8e6..3dc69a350 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -173,6 +173,10 @@ ..\packages\Common\Xylem.Common.CommonCore.dll + + False + ..\packages\Common\Xylem.Common.Hardware.Interfaces.Ports.PortCore.dll + ..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.dll @@ -211,6 +215,7 @@ GciBridgeCfgCtrl.cs + @@ -4168,6 +4173,7 @@ TestProgressCtrl.cs + Always @@ -4385,6 +4391,7 @@ Results +