Upgrade (develop) - GCI - GciBridge - big change

This commit is contained in:
2026-04-27 16:40:42 +02:00
parent 15dc325275
commit 4c1ce73de9
14 changed files with 863 additions and 357 deletions
@@ -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
{
/// <summary>
/// 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.
/// </summary>
public class GciPublicModels
{
/// <summary>
/// Public DTOs exposed to external systems.
/// These models represent the contract of the GCI API.
/// They must remain stable and independent of internal implementation.
/// </summary>
#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,
}
/// <summary>
/// Collection of port settings
/// </summary>
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<GciRegisterSnapshot> Registers { get; set; } = new List<GciRegisterSnapshot>();
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
/// <summary>
/// Mapping methods between public DTOs and internal GCI models.
/// Ensures separation between external contracts and internal domain objects.
/// </summary>
#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
}
}
@@ -1,15 +1,16 @@
using System; using GenesisCordonelInterface.Core.Threading;
using NLog;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using GenesisCordonelInterface.Core.Threading;
using NLog;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore; using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore; using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using static GenesisCordonelInterface.API.GciPublicModels;
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter; using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
namespace GenesisCordonelInterface.API namespace GenesisCordonelInterface.API
@@ -31,7 +32,7 @@ namespace GenesisCordonelInterface.API
#endregion #endregion
#region Worker #region ================================== Worker ==================================
private ApiWorker GetWorker(int slot) private ApiWorker GetWorker(int slot)
{ {
@@ -43,7 +44,7 @@ namespace GenesisCordonelInterface.API
#endregion #endregion
#region Worker Debug #region ================================== Worker Debug ==================================
public class WorkerDebugStatus public class WorkerDebugStatus
{ {
public int Slot { get; set; } public int Slot { get; set; }
@@ -73,7 +74,7 @@ namespace GenesisCordonelInterface.API
} }
#endregion #endregion
#region MeterBatch Debug #region ================================== MeterBatch Debug ==================================
public class MeterBatchDebugStatus public class MeterBatchDebugStatus
{ {
public int Slot { get; set; } public int Slot { get; set; }
@@ -134,7 +135,7 @@ namespace GenesisCordonelInterface.API
} }
#endregion #endregion
#region Helpers #region ================================== Helpers ==================================
private GenesisMeter GetMeter(int slot) private GenesisMeter GetMeter(int slot)
{ {
@@ -178,40 +179,32 @@ namespace GenesisCordonelInterface.API
#endregion #endregion
#region INIT #region ================================== INIT ==================================
public class InitResult public Task<GciPublicModels.GciInitSlotResult> InitOneMeterFromExternAsync(
int slot,
ConfigSource cfg,
PasswordSource pwd,
PortConfig? req,
PortConfig? str,
CancellationToken token = default)
{ {
public bool Success { get; set; } return GetWorker(slot).RunAsync(() => InitOneMeterFromExtern(slot, cfg, pwd, req, str), token);
public int Slot { get; set; }
public string ErrorMessage { get; set; }
} }
public Task<InitResult> InitOneMeterFromExternAsync( public GciPublicModels.GciInitSlotResult InitOneMeterFromExtern(
int slot, int slot,
ConfigSource cfg, ConfigSource cfg,
PasswordSource pwd, PasswordSource pwd,
PortConfig? req, PortConfig? req,
PortConfig? str, 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); const string operation = nameof(InitOneMeterFromExtern);
try try
{ {
LogInfo(operation, LogInfo(operation,
$"Start. Slot={slot}, ConfigSource={cfg}, PasswordSource={pwd}, " + $"Start. Slot={slot}, GciConfigSource={cfg}, PasswordSource={pwd}, " +
$"RequestPort={(req.HasValue ? req.Value.PortName.ToString() : "<empty>")}, " + $"RequestPort={(req.HasValue ? req.Value.PortName.ToString() : "<empty>")}, " +
$"StreamingPort={(str.HasValue ? str.Value.PortName.ToString() : "<empty>")}"); $"StreamingPort={(str.HasValue ? str.Value.PortName.ToString() : "<empty>")}");
@@ -227,24 +220,128 @@ namespace GenesisCordonelInterface.API
LogInfo(operation, $"Success. Slot={slot}, MeterBatchCount={_meterBatch.ListOfMeters.Count}"); 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) catch (Exception ex)
{ {
LogError(operation, ex); LogError(operation, ex);
return new InitResult return new GciPublicModels.GciInitSlotResult
{ {
Success = false, Success = false,
Slot = slot, SlotId = slot,
ErrorMessage = ex.Message Message = ex.Message
};
}
}
public Task<GciPublicModels.GciSlotInfo> 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<GenesisMeter>()
.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<GciPublicModels.GciCleanSlotsResult> 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 #endregion
#region PORT DETECTION #region ================================== PORT DETECTION ==================================
public class PortDetectionResult public class PortDetectionResult
{ {
@@ -361,45 +458,16 @@ namespace GenesisCordonelInterface.API
#endregion #endregion
#region CONNECT #region ================================== CONNECT ==================================
public class ConnectResult public Task<GciPublicModels.GciConnectResult> ConnectOneSlotAsync(int slot, CancellationToken token = default)
{ {
public bool Success { get; set; } return GetWorker(slot).RunAsync(() => ConnectOneSlot(slot), token);
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 GciPublicModels.GciConnectResult ConnectOneSlot(int slot)
{ {
public string Name { get; set; } const string operation = nameof(ConnectOneSlot);
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 try
{ {
@@ -413,18 +481,18 @@ namespace GenesisCordonelInterface.API
{ {
LogInfo(operation, $"Failed. Slot={slot}, Reason=Login failed."); LogInfo(operation, $"Failed. Slot={slot}, Reason=Login failed.");
return new ConnectResult return new GciPublicModels.GciConnectResult
{ {
Success = false, Success = false,
Slot = slot, SlotId = slot,
ErrorMessage = "Login failed." Message = "Login failed."
}; };
} }
var result = new ConnectResult var result = new GciPublicModels.GciConnectResult
{ {
Success = true, Success = true,
Slot = slot, SlotId = slot,
PcbId = meter.PcbId, PcbId = meter.PcbId,
IsLoggedOn = true, IsLoggedOn = true,
FwVersion = meter.FwVersion, FwVersion = meter.FwVersion,
@@ -444,25 +512,25 @@ namespace GenesisCordonelInterface.API
{ {
LogError(operation, ex); LogError(operation, ex);
return new ConnectResult return new GciPublicModels.GciConnectResult
{ {
Success = false, Success = false,
Slot = slot, SlotId = slot,
ErrorMessage = ex.Message Message = ex.Message
}; };
} }
} }
private List<RegisterSnapshot> BuildRegisters(GenesisMeter meter) private List<GciPublicModels.GciRegisterSnapshot> BuildRegisters(GenesisMeter meter)
{ {
var list = new List<RegisterSnapshot>(); var list = new List<GciRegisterSnapshot>();
foreach (var item in meter.GetRegistersDic()) foreach (var item in meter.GetRegistersDic())
{ {
var from = item.Key.RegisterDetail.Version.First?.ToString() ?? "-"; var from = item.Key.RegisterDetail.Version.First?.ToString() ?? "-";
var to = item.Key.RegisterDetail.Version.Last?.ToString() ?? "-"; var to = item.Key.RegisterDetail.Version.Last?.ToString() ?? "-";
list.Add(new RegisterSnapshot list.Add(new GciRegisterSnapshot
{ {
Name = item.Key.GetIdent(), Name = item.Key.GetIdent(),
Type = item.Key.DataType.Name, Type = item.Key.DataType.Name,
@@ -481,14 +549,72 @@ namespace GenesisCordonelInterface.API
#endregion #endregion
#region PCB #region ================================== DISCONNECT ==================================
public Task<string> GetPcbIdAsync(int slot, CancellationToken token = default(CancellationToken)) public Task<GciPublicModels.GciDisconnectResult> 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<GciPublicModels.GciGetPcbIdResult> GetPcbIdAsync(
int slot,
CancellationToken token = default(CancellationToken))
{ {
return GetWorker(slot).RunAsync(() => GetPcbId(slot), token, "GetPcbId"); return GetWorker(slot).RunAsync(() => GetPcbId(slot), token, "GetPcbId");
} }
public string GetPcbId(int slot) public GciPublicModels.GciGetPcbIdResult GetPcbId(int slot)
{ {
const string operation = nameof(GetPcbId); const string operation = nameof(GetPcbId);
@@ -507,19 +633,34 @@ namespace GenesisCordonelInterface.API
LogInfo(operation, $"Finish. Slot={slot}, PcbId={pcbId ?? "<empty>"}"); LogInfo(operation, $"Finish. Slot={slot}, PcbId={pcbId ?? "<empty>"}");
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) catch (Exception ex)
{ {
LogError(operation, ex); LogError(operation, ex);
throw;
return new GciPublicModels.GciGetPcbIdResult
{
SlotId = slot,
Success = false,
PcbId = null,
Message = ex.Message
};
} }
} }
#endregion #endregion
#region REGISTER READ #region ================================== REGISTER READ ==================================
public class RegisterReadResult public class RegisterReadResult
{ {
@@ -573,7 +714,7 @@ namespace GenesisCordonelInterface.API
#endregion #endregion
#region REGISTER WRITE #region ================================== REGISTER WRITE ==================================
public class RegisterWriteResult public class RegisterWriteResult
{ {
@@ -700,47 +841,7 @@ namespace GenesisCordonelInterface.API
#endregion #endregion
#region DISCONNECT #region ================================== PASSWORD ==================================
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( public Task<bool> SetMeterPasswordAsync(
int slot, int slot,
@@ -776,7 +877,7 @@ namespace GenesisCordonelInterface.API
#endregion #endregion
#region METER BATCH SETUP #region ================================== METER BATCH SETUP ==================================
// ---------------------------------------------------- // ----------------------------------------------------
public void ReloadSlotSetup() public void ReloadSlotSetup()
@@ -1,9 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Ports;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
namespace GenesisCordonelInterface.API namespace GenesisCordonelInterface.API
{ {
@@ -15,10 +14,6 @@ namespace GenesisCordonelInterface.API
{ {
private readonly InterfaceGCIToLaatzen _innerMeterAPI; private readonly InterfaceGCIToLaatzen _innerMeterAPI;
private readonly Dictionary<int, PortConfig> _requestPorts = new Dictionary<int, PortConfig>();
private readonly Dictionary<int, PortConfig> _streamingPorts = new Dictionary<int, PortConfig>();
private readonly object _portLock = new object();
public event Action<List<InterfaceGCIToLaatzen.MeterBatchDebugStatus>> MeterBatchStatusChanged; public event Action<List<InterfaceGCIToLaatzen.MeterBatchDebugStatus>> MeterBatchStatusChanged;
public InterfaceOutsideToGCI() public InterfaceOutsideToGCI()
@@ -26,8 +21,7 @@ namespace GenesisCordonelInterface.API
_innerMeterAPI = new InterfaceGCIToLaatzen(); _innerMeterAPI = new InterfaceGCIToLaatzen();
} }
#region PORT DETECTION #region ================================== PORT DETECTION ==================================
// ----------------------------------------------------
public InterfaceGCIToLaatzen.PortDetectionResult DetectStreamingPort(int slot) public InterfaceGCIToLaatzen.PortDetectionResult DetectStreamingPort(int slot)
{ {
@@ -61,132 +55,96 @@ namespace GenesisCordonelInterface.API
return result; return result;
} }
// ----------------------------------------------------
#endregion #endregion
#region INIT #region ================================== INIT ==================================
// ----------------------------------------------------
public InterfaceGCIToLaatzen.InitResult InitOneMeterFromExtern( public async Task<GciPublicModels.GciInitSlotResult> InitSlotAsync(GciPublicModels.GciInitSlotRequest request, CancellationToken token = default)
int slot,
ConfigSource useConfigSource,
PasswordSource usePasswordSource,
PortConfig? requestPort,
PortConfig? streamingPort)
{ {
var result = _innerMeterAPI.InitOneMeterFromExtern( if (request == null)
slot, throw new ArgumentNullException(nameof(request));
useConfigSource,
usePasswordSource,
requestPort,
streamingPort);
RaiseMeterBatchStatusChanged();
return result;
}
public async Task<InterfaceGCIToLaatzen.InitResult> InitOneMeterFromExternAsync(
int slot,
ConfigSource useConfigSource,
PasswordSource usePasswordSource,
PortConfig? requestPort,
PortConfig? streamingPort,
CancellationToken token = default(CancellationToken))
{
var result = await _innerMeterAPI.InitOneMeterFromExternAsync( var result = await _innerMeterAPI.InitOneMeterFromExternAsync(
slot, request.SlotId,
useConfigSource, GciPublicModels.MapConfigSource(request.ConfigSource),
usePasswordSource, GciPublicModels.MapPasswordSource(request.PasswordSource),
requestPort, GciPublicModels.MapPort(request.RequestPort),
streamingPort, GciPublicModels.MapPort(request.StreamingPort),
token); token);
RaiseMeterBatchStatusChanged(); RaiseMeterBatchStatusChanged();
return result; return result;
} }
public InterfaceGCIToLaatzen.InitResult InitSlotWithConfiguredPorts( public async Task<GciPublicModels.GciSlotInfo> GetSlotAsync(
int slot, int slotId,
ConfigSource useConfigSource, CancellationToken token = default)
PasswordSource usePasswordSource)
{ {
return InitOneMeterFromExtern( if (slotId <= 0)
slot, throw new ArgumentException("Invalid slot id.");
useConfigSource,
usePasswordSource, var result = await _innerMeterAPI.GetOneMeterInfo(slotId, token);
GetSlotRequestPort(slot),
GetSlotStreamingPort(slot)); return result;
} }
public Task<InterfaceGCIToLaatzen.InitResult> InitSlotWithConfiguredPortsAsync( public async Task<GciPublicModels.GciCleanSlotsResult> CleanSlotsAsync(
int slot, CancellationToken token = default)
ConfigSource useConfigSource,
PasswordSource usePasswordSource,
CancellationToken token = default(CancellationToken))
{ {
return InitOneMeterFromExternAsync( var result = await _innerMeterAPI.CleanSlotsAsync(token);
slot,
useConfigSource,
usePasswordSource,
GetSlotRequestPort(slot),
GetSlotStreamingPort(slot),
token);
}
// ---------------------------------------------------- RaiseMeterBatchStatusChanged();
return result;
}
#endregion #endregion
#region CONNECTION #region ================================== CONNECTION ==================================
// ----------------------------------------------------
public InterfaceGCIToLaatzen.ConnectResult ConnectOneMeter(int slot) public async Task<GciPublicModels.GciConnectResult> ConnectOneSlotAsync(
int slot,
CancellationToken token = default)
{ {
var result = _innerMeterAPI.ConnectOneMeter(slot); GciPublicModels.GciConnectResult result = await _innerMeterAPI.ConnectOneSlotAsync(slot, token);
RaiseMeterBatchStatusChanged(); RaiseMeterBatchStatusChanged();
return result; return result;
} }
public async Task<InterfaceGCIToLaatzen.ConnectResult> ConnectOneMeterAsync( public async Task<GciPublicModels.GciDisconnectResult> DisconnectAsync(
int slot, int slot,
CancellationToken token = default(CancellationToken)) 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(); RaiseMeterBatchStatusChanged();
return result; 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 #endregion
#region READ #region ================================== PCB ==================================
public async Task<GciPublicModels.GciGetPcbIdResult> 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<string> GetPcbIdAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
return _innerMeterAPI.GetPcbIdAsync(slot, token);
}
public InterfaceGCIToLaatzen.RegisterReadResult ReadRegister( public InterfaceGCIToLaatzen.RegisterReadResult ReadRegister(
int slot, int slot,
string registerName) string registerName)
@@ -205,7 +163,7 @@ namespace GenesisCordonelInterface.API
// ---------------------------------------------------- // ----------------------------------------------------
#endregion #endregion
#region WRITE #region ================================== WRITE ==================================
// ---------------------------------------------------- // ----------------------------------------------------
public async Task<InterfaceGCIToLaatzen.RegisterWriteResult> WriteRegisterAsync( public async Task<InterfaceGCIToLaatzen.RegisterWriteResult> WriteRegisterAsync(
@@ -261,7 +219,7 @@ namespace GenesisCordonelInterface.API
// ---------------------------------------------------- // ----------------------------------------------------
#endregion #endregion
#region DEBUG STATUS #region ================================== DEBUG STATUS ==================================
// ---------------------------------------------------- // ----------------------------------------------------
public List<InterfaceGCIToLaatzen.WorkerDebugStatus> GetWorkerDebugStatuses() public List<InterfaceGCIToLaatzen.WorkerDebugStatus> GetWorkerDebugStatuses()
@@ -286,7 +244,7 @@ namespace GenesisCordonelInterface.API
// ---------------------------------------------------- // ----------------------------------------------------
#endregion #endregion
#region SLOT SELECTION #region ================================== SLOT SELECTION ==================================
// ---------------------------------------------------- // ----------------------------------------------------
public void SetSlotSelected(int slot, bool selected) public void SetSlotSelected(int slot, bool selected)
@@ -308,10 +266,10 @@ namespace GenesisCordonelInterface.API
// ---------------------------------------------------- // ----------------------------------------------------
#endregion #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) lock (_portLock)
{ {
@@ -321,7 +279,7 @@ namespace GenesisCordonelInterface.API
} }
else else
{ {
_requestPorts[slot] = new PortConfig _requestPorts[slot] = new GciPortConfig
{ {
PortName = portName, PortName = portName,
Type = "Serial" Type = "Serial"
@@ -342,7 +300,7 @@ namespace GenesisCordonelInterface.API
} }
else else
{ {
_streamingPorts[slot] = new PortConfig _streamingPorts[slot] = new GciPortConfig
{ {
PortName = portName, PortName = portName,
Type = "Serial" Type = "Serial"
@@ -353,11 +311,11 @@ namespace GenesisCordonelInterface.API
RaiseMeterBatchStatusChanged(); RaiseMeterBatchStatusChanged();
} }
public PortConfig? GetSlotRequestPort(int slot) public GciPortConfig? GetSlotRequestPort(int slot)
{ {
lock (_portLock) lock (_portLock)
{ {
PortConfig port; GciPortConfig port;
if (_requestPorts.TryGetValue(slot, out port)) if (_requestPorts.TryGetValue(slot, out port))
return port; return port;
@@ -365,22 +323,22 @@ namespace GenesisCordonelInterface.API
} }
} }
public PortConfig? GetSlotStreamingPort(int slot) public GciPortConfig? GetSlotStreamingPort(int slot)
{ {
lock (_portLock) lock (_portLock)
{ {
PortConfig port; GciPortConfig port;
if (_streamingPorts.TryGetValue(slot, out port)) if (_streamingPorts.TryGetValue(slot, out port))
return port; return port;
return null; return null;
} }
} }*/
// ---------------------------------------------------- // ----------------------------------------------------
#endregion #endregion
#region METER BATCH SETUP #region ================================== METER BATCH SETUP ==================================
// ---------------------------------------------------- // ----------------------------------------------------
public void ReloadSlotSetup() public void ReloadSlotSetup()
@@ -58,6 +58,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="API\InterfaceOutsideToGCI.cs" /> <Compile Include="API\InterfaceOutsideToGCI.cs" />
<Compile Include="API\InterfaceGCIToLaatzen.cs" /> <Compile Include="API\InterfaceGCIToLaatzen.cs" />
<Compile Include="API\GciPublicModels.cs" />
<Compile Include="Core\Logging\UiLogBus.cs" /> <Compile Include="Core\Logging\UiLogBus.cs" />
<Compile Include="Core\Logging\UiTarget.cs" /> <Compile Include="Core\Logging\UiTarget.cs" />
<Compile Include="Core\Threading\ApiWorker\ApiWorker.cs" /> <Compile Include="Core\Threading\ApiWorker\ApiWorker.cs" />
@@ -266,13 +266,13 @@ namespace GenesisCordonelInterface.UI.Debug
if (columnName == "RequestPort") if (columnName == "RequestPort")
{ {
api.SetSlotRequestPort(slot, port); //api.SetSlotRequestPort(slot, port);
return; return;
} }
if (columnName == "StreamingPort") if (columnName == "StreamingPort")
{ {
api.SetSlotStreamingPort(slot, port); //api.SetSlotStreamingPort(slot, port);
return; return;
} }
} }
@@ -492,7 +492,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
DisableAllButtons(); DisableAllButtons();
_dataTable.Rows.Clear(); _dataTable.Rows.Clear();
var result = await interfaceToLaatzen.ConnectOneMeterAsync(slotNo); var result = await interfaceToLaatzen.ConnectOneSlotAsync(slotNo);
if (result.Success) if (result.Success)
{ {
@@ -571,7 +571,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
lblState.Text = $@"Not Connected to PcbId:{result.PcbId}"; lblState.Text = $@"Not Connected to PcbId:{result.PcbId}";
registerGridView.Visible = false; 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) catch (Exception ex)
@@ -1172,11 +1172,16 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
SetBusy(true, "GetPcbId"); SetBusy(true, "GetPcbId");
// as new meter object will be generated the data grid shows outdated data
_dataTable.Rows.Clear(); _dataTable.Rows.Clear();
DisableAllButtons(); 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; btnConnect.Enabled = true;
@@ -82,7 +82,7 @@
this.lblConfigSource.Name = "lblConfigSource"; this.lblConfigSource.Name = "lblConfigSource";
this.lblConfigSource.Size = new System.Drawing.Size(75, 13); this.lblConfigSource.Size = new System.Drawing.Size(75, 13);
this.lblConfigSource.TabIndex = 2; this.lblConfigSource.TabIndex = 2;
this.lblConfigSource.Text = "ConfigSource:"; this.lblConfigSource.Text = "GciConfigSource:";
// //
// cmbConfigSource // cmbConfigSource
// //
@@ -160,9 +160,15 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
} }
/// <summary> /// <summary>
/// Creates PortConfig from UI values. /// Creates a public GCI port configuration object from UI input values.
/// </summary> /// </summary>
private PortConfig? CreatePortConfig(string portName, string baudRateText) /// <param name="portName">Name of the port (e.g. COM3).</param>
/// <param name="baudRateText">Baud rate entered in the UI.</param>
/// <returns>
/// Instance of <see cref="GciPublicModels.GciPortConfig"/> or null if port name is empty.
/// </returns>
/// <exception cref="Exception">Thrown when baud rate is invalid.</exception>
private GciPublicModels.GciPortConfig CreateGciPortConfig(string portName, string baudRateText)
{ {
if (string.IsNullOrWhiteSpace(portName)) if (string.IsNullOrWhiteSpace(portName))
return null; return null;
@@ -170,35 +176,37 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
if (!int.TryParse(baudRateText, out int baudRate)) if (!int.TryParse(baudRateText, out int baudRate))
throw new Exception($"Invalid baud rate for port {portName}."); throw new Exception($"Invalid baud rate for port {portName}.");
var cfg = new PortConfig return new GciPublicModels.GciPortConfig
{ {
PortName = portName, 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() /// <summary>
/// Gets selected configuration source from UI and maps it to public GCI model.
/// </summary>
/// <returns>Selected <see cref="GciPublicModels.GciConfigSource"/>.</returns>
/// <exception cref="Exception">Thrown when no value is selected.</exception>
private GciPublicModels.GciConfigSource GetConfigSource()
{ {
if (cmbConfigSource.SelectedItem == null) 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() /// <summary>
/// Gets selected password source from UI and maps it to public GCI model.
/// </summary>
/// <returns>Selected <see cref="GciPublicModels.GciPasswordSource"/>.</returns>
/// <exception cref="Exception">Thrown when no value is selected.</exception>
private GciPublicModels.GciPasswordSource GetPasswordSource()
{ {
if (cmbPasswordSource.SelectedItem == null) 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 #endregion
@@ -221,15 +229,19 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
await ExecuteApiActionAsync(async () => await ExecuteApiActionAsync(async () =>
{ {
var result = await _api.InitOneMeterFromExternAsync( var request = new GciPublicModels.GciInitSlotRequest
slot, {
GetConfigSource(), SlotId = slot,
GetPasswordSource(), ConfigSource = GetConfigSource(),
CreatePortConfig(txtRequestPort.Text, txtRequestBaudRate.Text), PasswordSource = GetPasswordSource(),
CreatePortConfig(txtStreamingPort.Text, txtStreamingBaudRate.Text)); RequestPort = CreateGciPortConfig(txtRequestPort.Text, txtRequestBaudRate.Text),
StreamingPort = CreateGciPortConfig(txtStreamingPort.Text, txtStreamingBaudRate.Text)
};
var result = await _api.InitSlotAsync(request);
if (!result.Success) if (!result.Success)
throw new Exception(result.ErrorMessage); throw new Exception(result.Message);
MessageBox.Show("Meter initialized."); MessageBox.Show("Meter initialized.");
}); });
@@ -251,10 +263,10 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
await ExecuteApiActionAsync(async () => await ExecuteApiActionAsync(async () =>
{ {
var result = await _api.ConnectOneMeterAsync(slot); var result = await _api.ConnectOneSlotAsync(slot);
if (!result.Success) if (!result.Success)
throw new Exception(result.ErrorMessage); throw new Exception(result.Message);
MessageBox.Show($"Connected.\r\nPCB ID: {result.PcbId}"); MessageBox.Show($"Connected.\r\nPCB ID: {result.PcbId}");
}); });
@@ -280,7 +292,6 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
MessageBox.Show("Disconnected."); MessageBox.Show("Disconnected.");
}); });
} }
/// <summary> /// <summary>
/// Reads PCB ID asynchronously. /// Reads PCB ID asynchronously.
/// </summary> /// </summary>
@@ -297,8 +308,13 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
await ExecuteApiActionAsync(async () => await ExecuteApiActionAsync(async () =>
{ {
string pcbId = await _api.GetPcbIdAsync(slot); GciPublicModels.GciGetPcbIdResult result =
MessageBox.Show($"PCB ID: {pcbId}"); 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) foreach (int slot in slots)
{ {
var result = await _api.ConnectOneMeterAsync(slot); var result = await _api.ConnectOneSlotAsync(slot);
if (!result.Success) if (!result.Success)
throw new Exception($"Slot {slot}: {result.ErrorMessage}"); throw new Exception($"Slot {slot}: {result.Message}");
} }
MessageBox.Show("Selected meters connected."); MessageBox.Show("Selected meters connected.");
@@ -66,10 +66,10 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
foreach (int slot in _api.GetSelectedSlots()) foreach (int slot in _api.GetSelectedSlots())
{ {
var result = await _api.ConnectOneMeterAsync(slot); var result = await _api.ConnectOneSlotAsync(slot);
if (!result.Success) if (!result.Success)
throw new Exception($"Slot {slot}: {result.ErrorMessage}"); throw new Exception($"Slot {slot}: {result.Message}");
} }
MessageBox.Show("Selected meters connected."); MessageBox.Show("Selected meters connected.");
+35
View File
@@ -0,0 +1,35 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
Copies the prepared GCI runtime package into the TBF output directory.
This ensures all GCI dependencies are available next to TBF.exe.
-->
<Target Name="CopyGciRuntimePackageToTbfOutput" AfterTargets="Build">
<!-- Source directory containing the prepared GCI runtime package -->
<PropertyGroup>
<GciRuntimePackageDir>$(ProjectDir)..\GenesisCordonelInterface\RuntimePackage\Package\</GciRuntimePackageDir>
</PropertyGroup>
<!-- Collect runtime package files -->
<ItemGroup>
<GciRuntimeFiles Include="$(GciRuntimePackageDir)*.dll" />
<GciRuntimeFiles Include="$(GciRuntimePackageDir)*.exe" />
<GciRuntimeFiles Include="$(GciRuntimePackageDir)*.config" />
<GciRuntimeFiles Include="$(GciRuntimePackageDir)*.json" />
</ItemGroup>
<!-- Build output messages -->
<Message Text="Copying GCI runtime package to TBF output: $(TargetDir)" Importance="High" />
<Message Text="Source: $(GciRuntimePackageDir)" Importance="High" />
<Message Text="Files: @(GciRuntimeFiles)" Importance="Normal" />
<!-- Copy package files into TBF output directory -->
<Copy
SourceFiles="@(GciRuntimeFiles)"
DestinationFolder="$(TargetDir)"
SkipUnchangedFiles="false" />
</Target>
</Project>
+128 -71
View File
@@ -1,13 +1,16 @@
using log4net; using GenesisCordonelInterface.API;
using log4net;
/// ///
/// Copyright (c) 2015-2021 Sensus Slovensko a.s. /// Copyright (c) 2015-2021 Sensus Slovensko a.s.
/// ///
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using TBF.Rig.Generic; using TBF.Rig.Generic;
using ExternalInterfaceGUIType = GenesisCordonelInterface.UI.MainView; using GciGUIType = GenesisCordonelInterface.UI.MainView;
using ExternalInterfaceType = GenesisCordonelInterface.API.InterfaceOutsideToGCI; using GciType = GenesisCordonelInterface.API.InterfaceOutsideToGCI;
using UdsReaderType = TBF.Rig.Input.DataStorage.UniDataStorageReader.Reader; using UdsReaderType = TBF.Rig.Input.DataStorage.UniDataStorageReader.Reader;
using UdsWriterType = TBF.Rig.Output.DataStorage.UniDataStorageWriter.Writer; using UdsWriterType = TBF.Rig.Output.DataStorage.UniDataStorageWriter.Writer;
@@ -20,42 +23,37 @@ namespace TBF.Rig.BridgeComponents.GciBridge
public class GciBridge : ComponentBase public class GciBridge : ComponentBase
{ {
private static readonly ILog log = LogManager.GetLogger(typeof(GciBridge)); 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; readonly GciBridgeCfg gciBridgeCfg;
/// <summary>
/// Linked UniDataStorage reader component.
/// </summary>
readonly UdsReaderType reader; readonly UdsReaderType reader;
/// <summary>
/// Linked UniDataStorage writer component.
/// </summary>
readonly UdsWriterType writer; readonly UdsWriterType writer;
/// <summary> GciGUIType gciGUI;
/// Placeholder for GCI GUI entry point.
/// Replace object with real GCI MainForm type later.
/// </summary>
ExternalInterfaceGUIType gciGUI;
/// <summary>
///
/// </summary>
Form gciGuiHostForm; Form gciGuiHostForm;
/// <summary> GciType gciExternalInterface;
/// Placeholder for GCI external/public interface.
/// Replace object with real GCI interface type later.
/// </summary>
ExternalInterfaceType gciExternalInterface;
public bool HasReader { get { return reader != null; } } public bool HasReader { get { return reader != null; } }
public bool HasWriter { get { return writer != null; } } public bool HasWriter { get { return writer != null; } }
public bool IsGuiInitialized { get { return gciGUI != null; } } public bool IsGuiInitialized { get { return gciGUI != null; } }
public bool IsExternalInitialized { get { return gciExternalInterface != null; } } public bool IsExternalInitialized { get { return gciExternalInterface != null; } }
public UdsReaderType GetReader()
{
return reader;
}
public UdsWriterType GetWriter()
{
return writer;
}
public GciBridge() { } public GciBridge() { }
public GciBridge(IComponentCfg cfg, IList<IComponent> components) public GciBridge(IComponentCfg cfg, IList<IComponent> components)
@@ -97,10 +95,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge
log.FatalFormat("{0} initialized: {1}", Name, this); log.FatalFormat("{0} initialized: {1}", Name, this);
} }
/// <summary>
/// Initializes access to GCI GUI.
/// Replace placeholder implementation with real MainForm creation.
/// </summary>
void TryInitializeGui() void TryInitializeGui()
{ {
try try
@@ -108,7 +102,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (gciGuiHostForm != null && !gciGuiHostForm.IsDisposed) if (gciGuiHostForm != null && !gciGuiHostForm.IsDisposed)
return; return;
gciGUI = new ExternalInterfaceGUIType(); gciGUI = new GciGUIType();
gciGUI.Dock = DockStyle.Fill; gciGUI.Dock = DockStyle.Fill;
gciGuiHostForm = new Form(); gciGuiHostForm = new Form();
@@ -127,15 +121,14 @@ namespace TBF.Rig.BridgeComponents.GciBridge
} }
} }
/// <summary>
/// Initializes access to GCI external/public interface.
/// Replace placeholder implementation with real interface creation.
/// </summary>
void TryInitializeExternalInterface() void TryInitializeExternalInterface()
{ {
try try
{ {
gciExternalInterface = new ExternalInterfaceType(); if (gciExternalInterface != null)
return;
gciExternalInterface = new GciType();
log.InfoFormat("{0}: GCI external interface initialized.", Name); log.InfoFormat("{0}: GCI external interface initialized.", Name);
} }
@@ -145,9 +138,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge
} }
} }
/// <summary>
/// Shows GCI GUI if GUI access is enabled.
/// </summary>
public void ShowGui() public void ShowGui()
{ {
if (!gciBridgeCfg.EnableGuiAccess) return; if (!gciBridgeCfg.EnableGuiAccess) return;
@@ -165,9 +155,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge
log.InfoFormat("{0}: ShowGui invoked.", Name); log.InfoFormat("{0}: ShowGui invoked.", Name);
} }
/// <summary>
/// Hides GCI GUI if GUI access is enabled.
/// </summary>
public void HideGui() public void HideGui()
{ {
if (!gciBridgeCfg.EnableGuiAccess) return; if (!gciBridgeCfg.EnableGuiAccess) return;
@@ -178,50 +165,120 @@ namespace TBF.Rig.BridgeComponents.GciBridge
log.InfoFormat("{0}: HideGui invoked.", Name); log.InfoFormat("{0}: HideGui invoked.", Name);
} }
/// <summary> void EnsureExternalInterface()
/// Connects the external GCI interface.
/// </summary>
public void ConnectExternal()
{ {
if (!gciBridgeCfg.EnableExternalAccess) return; if (!gciBridgeCfg.EnableExternalAccess)
throw new Exception("GCI external access is disabled.");
if (!IsExternalInitialized) if (!IsExternalInitialized)
{
TryInitializeExternalInterface(); TryInitializeExternalInterface();
}
/// TODO: if (gciExternalInterface == null)
/// Replace with real external interface connect call. throw new Exception("GCI external interface is not initialized.");
log.InfoFormat("{0}: ConnectExternal invoked.", Name);
} }
/// <summary> void EnsureReader()
/// Disconnects the external GCI interface.
/// </summary>
public void DisconnectExternal()
{ {
if (!gciBridgeCfg.EnableExternalAccess) return; if (reader == null)
if (!IsExternalInitialized) return; throw new Exception("UniDataStorageReader is not linked to GciBridge.");
/// TODO:
/// Replace with real external interface disconnect call.
log.InfoFormat("{0}: DisconnectExternal invoked.", Name);
} }
/// <summary> // API:
/// Returns linked reader component. #region ======================================= GCI Public Interface =======================================
/// </summary>
public UdsReaderType GetReader() public async Task<GciPublicModels.GciInitSlotResult> 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;
} }
/// <summary> public async Task<GciPublicModels.GciSlotInfo> GetSlotAsync(int slotId)
/// Returns linked writer component.
/// </summary>
public UdsWriterType GetWriter()
{ {
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<GciPublicModels.GciCleanSlotsResult> CleanSlotsAsync()
{
EnsureExternalInterface();
GciPublicModels.GciCleanSlotsResult result =
await gciExternalInterface.CleanSlotsAsync();
log.InfoFormat("{0}: CleanSlotsAsync invoked.", Name);
return result;
}
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;
}
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 async Task<GciPublicModels.GciDisconnectResult> 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
} }
} }
@@ -182,7 +182,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
return; return;
} }
bridge.ConnectExternal(); //bridge.ConnectExternal();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -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
{
}
}
+7
View File
@@ -173,6 +173,10 @@
<Reference Include="Xylem.Common.CommonCore"> <Reference Include="Xylem.Common.CommonCore">
<HintPath>..\packages\Common\Xylem.Common.CommonCore.dll</HintPath> <HintPath>..\packages\Common\Xylem.Common.CommonCore.dll</HintPath>
</Reference> </Reference>
<Reference Include="Xylem.Common.Hardware.Interfaces.Ports.PortCore, Version=2.8.18.15967, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Common\Xylem.Common.Hardware.Interfaces.Ports.PortCore.dll</HintPath>
</Reference>
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig"> <Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig">
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.dll</HintPath> <HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.dll</HintPath>
</Reference> </Reference>
@@ -211,6 +215,7 @@
<Compile Include="Rig\BridgeComponents\GciBridge\GciBridgeCfgCtrl.Designer.cs"> <Compile Include="Rig\BridgeComponents\GciBridge\GciBridgeCfgCtrl.Designer.cs">
<DependentUpon>GciBridgeCfgCtrl.cs</DependentUpon> <DependentUpon>GciBridgeCfgCtrl.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Rig\BridgeComponents\GciBridge\GciBridgeOp.cs" />
<Compile Include="Rig\BuiltIn\PumpTandem\Pump.cs" /> <Compile Include="Rig\BuiltIn\PumpTandem\Pump.cs" />
<Compile Include="Rig\BuiltIn\PumpTandem\PumpCfg.cs" /> <Compile Include="Rig\BuiltIn\PumpTandem\PumpCfg.cs" />
<Compile Include="Rig\BuiltIn\PumpTandem\PumpCfgCtrl.cs"> <Compile Include="Rig\BuiltIn\PumpTandem\PumpCfgCtrl.cs">
@@ -4168,6 +4173,7 @@
<DependentUpon>TestProgressCtrl.cs</DependentUpon> <DependentUpon>TestProgressCtrl.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<None Include="app.config" /> <None Include="app.config" />
<Content Include="Build\CopyGci.targets.xml" />
<Content Include="CameraBinaryFiles\GrabImage"> <Content Include="CameraBinaryFiles\GrabImage">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
@@ -4385,6 +4391,7 @@
<Name>Results</Name> <Name>Results</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="Build\CopyGci.targets.xml" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.