Merge remote-tracking branch 'private-origin/develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode
# Conflicts: # TBF/TBF.csproj
@ -56,7 +56,7 @@ namespace Common.Iperl
|
||||
public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; }
|
||||
public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; }
|
||||
public Int32 FlipTime() { return Impedance; }
|
||||
public decimal TimestampDec() { return (decimal)TimestampExt / (decimal)8192; }
|
||||
public decimal TimestampDec() { return (decimal)TimestampExt; }
|
||||
public double VolumeDelta(double scalingFactor, OptoTelegramRaw previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); }
|
||||
public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
|
||||
public string Label()
|
||||
|
||||
17
GenesisCordonelInterface/API/Enums.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.API
|
||||
{
|
||||
public class Enums
|
||||
{
|
||||
public enum DataStorageReaderTypes : sbyte
|
||||
{
|
||||
LoginPasswordsReader,
|
||||
CalibrationParamsReader
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,33 +1,104 @@
|
||||
using System;
|
||||
using CordonelPreadjustmentUi;
|
||||
using CordonelPreadjustmentUi.Processes.Itinerary;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Config;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
||||
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
|
||||
using Xylem.Common.Ui.CordonelPreadjustmentUi;
|
||||
using static GenesisCordonelInterface.API.PublicModels;
|
||||
|
||||
namespace GenesisCordonelInterface.API
|
||||
{
|
||||
/// <summary>
|
||||
/// Outside-facing facade for Genesis Cordonel Interface.
|
||||
/// Exposes only selected operations intended for external callers.
|
||||
/// Public-facing facade for external applications integrating with
|
||||
/// Genesis Cordonel Interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class exposes a simplified and controlled API for external callers.
|
||||
/// It validates public input, maps public request models to internal models,
|
||||
/// forwards operations to the internal GCI implementation and exposes status
|
||||
/// notifications for meter batch changes.
|
||||
///
|
||||
/// This layer should stay thin. Business logic and meter communication are
|
||||
/// handled by <see cref="InterfaceGCIToLaatzen"/>.
|
||||
/// </remarks>
|
||||
public class InterfaceOutsideToGCI
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal GCI implementation used by this public facade.
|
||||
/// </summary>
|
||||
public readonly InterfaceGCIToLaatzen _innerMeterAPI;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when meter batch status information changes.
|
||||
/// </summary>
|
||||
public event Action<List<MeterBatchDebugStatus>> MeterBatchStatusChanged;
|
||||
private readonly IMeterLoginPasswordReader loginPasswordsReader;
|
||||
private readonly IPreAdjustmentCalibrationParamsReader calibrationParamsReader;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the public GCI facade.
|
||||
/// </summary>
|
||||
public InterfaceOutsideToGCI()
|
||||
{
|
||||
_innerMeterAPI = new InterfaceGCIToLaatzen();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the public GCI facade.
|
||||
/// </summary>
|
||||
public InterfaceOutsideToGCI(
|
||||
InterfaceGCIToLaatzen innerMeterApi,
|
||||
IMeterLoginPasswordReader passwordReader,
|
||||
IPreAdjustmentCalibrationParamsReader calibrationReader)
|
||||
{
|
||||
_innerMeterAPI = innerMeterApi?? throw new ArgumentNullException(nameof(innerMeterApi));
|
||||
loginPasswordsReader = passwordReader ?? throw new ArgumentNullException(nameof(passwordReader));
|
||||
calibrationParamsReader = calibrationReader ?? throw new ArgumentNullException(nameof(calibrationReader));
|
||||
|
||||
//init handlers
|
||||
//_innerMeterAPI._progressProcess.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
|
||||
}
|
||||
|
||||
private async void RequestedCalibrationParamsFromDb(
|
||||
object sender,
|
||||
EventArgsProcessProgress e)
|
||||
{
|
||||
try
|
||||
{
|
||||
DataQuery query = new DataQuery();
|
||||
query.QueryParams.Add(((int)e.Value.Setting.MeterSize).ToString());
|
||||
|
||||
Dictionary<string, UInt32> calibrationParams = await ReadPreAdjustmentCalibrationParamsAsync(query).ConfigureAwait(false);
|
||||
|
||||
e.Value.PushedCalibrationParams = calibrationParams;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
e.Value.DebugMessage(
|
||||
$"Calibration params reading failed: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
e.Value.CalibrationParamsReadEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
// Laatzen ToolBox actions
|
||||
|
||||
#region ================================== PORT DETECTION ==================================
|
||||
|
||||
public PortDetectionResult DetectStreamingPort(int slot)
|
||||
{
|
||||
var result = _innerMeterAPI.DetectStreamingPort(slot);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
var result = _innerMeterAPI?.DetectStreamingPort(slot);
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -35,15 +106,15 @@ namespace GenesisCordonelInterface.API
|
||||
int slot,
|
||||
CancellationToken token = default(CancellationToken))
|
||||
{
|
||||
var result = await _innerMeterAPI.DetectStreamingPortAsync(slot, token);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
var result = await _innerMeterAPI?.DetectStreamingPortAsync(slot, token);
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
public PortDetectionResult DetectRequestPort(int slot)
|
||||
{
|
||||
var result = _innerMeterAPI.DetectRequestPort(slot);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
var result = _innerMeterAPI?.DetectRequestPort(slot);
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -51,32 +122,29 @@ namespace GenesisCordonelInterface.API
|
||||
int slot,
|
||||
CancellationToken token = default(CancellationToken))
|
||||
{
|
||||
var result = await _innerMeterAPI.DetectRequestPortAsync(slot, token);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
var result = await _innerMeterAPI?.DetectRequestPortAsync(slot, token);
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================================== INIT ==================================
|
||||
#region ================================== INIT/UPDATE/GET slot ==================================
|
||||
|
||||
/*public async Task<GciInitSlotResult> InitSlotAsync(GciInitSlotRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
|
||||
var result = await _innerMeterAPI.InitOneMeterFromExternAsync(
|
||||
request.SlotId,
|
||||
ModelsMapping.MapConfigSource(request.ConfigSource),
|
||||
ModelsMapping.MapPasswordSource(request.PasswordSource),
|
||||
ModelsMapping.MapPort(request.RequestPort),
|
||||
ModelsMapping.MapPort(request.StreamingPort),
|
||||
token);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}*/
|
||||
/// <summary>
|
||||
/// Initializes a meter slot using the provided slot configuration.
|
||||
/// </summary>
|
||||
/// <param name="request">
|
||||
/// Slot initialization request containing slot id, configuration source,
|
||||
/// password source, request port and streaming port.
|
||||
/// </param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Result describing whether the slot was created, updated, already existed or failed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Thrown when <paramref name="request"/> is null.
|
||||
/// </exception>
|
||||
public async Task<GciInitSlotResult> InitSlotAsync(
|
||||
GciInitSlotRequest request,
|
||||
CancellationToken token = default)
|
||||
@ -90,13 +158,27 @@ namespace GenesisCordonelInterface.API
|
||||
ModelsMapping.MapPasswordSource(request.PasswordSource),
|
||||
ModelsMapping.MapPort(request.RequestPort),
|
||||
ModelsMapping.MapPort(request.StreamingPort),
|
||||
token);
|
||||
token).ConfigureAwait(false);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates configuration of an existing meter slot.
|
||||
/// </summary>
|
||||
/// <param name="request">
|
||||
/// Slot configuration request containing updated configuration source,
|
||||
/// password source and port settings.
|
||||
/// </param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Result describing whether the slot update succeeded or failed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Thrown when <paramref name="request"/> is null.
|
||||
/// </exception>
|
||||
public async Task<GciInitSlotResult> UpdateSlotAsync(
|
||||
GciInitSlotRequest request,
|
||||
CancellationToken token = default)
|
||||
@ -110,13 +192,25 @@ namespace GenesisCordonelInterface.API
|
||||
ModelsMapping.MapPasswordSource(request.PasswordSource),
|
||||
ModelsMapping.MapPort(request.RequestPort),
|
||||
ModelsMapping.MapPort(request.StreamingPort),
|
||||
token);
|
||||
token).ConfigureAwait(false);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about one meter slot.
|
||||
/// </summary>
|
||||
/// <param name="slotId">Slot id to query. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Slot information including existence, connection state, login state,
|
||||
/// PCB id and configured communication ports.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slotId"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciSlotInfo> GetSlotAsync(
|
||||
int slotId,
|
||||
CancellationToken token = default)
|
||||
@ -124,31 +218,81 @@ namespace GenesisCordonelInterface.API
|
||||
if (slotId <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await _innerMeterAPI.GetOneMeterInfo(slotId, token);
|
||||
var result = await _innerMeterAPI.GetOneMeterInfo(slotId, token).ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about all currently initialized meter slots.
|
||||
/// </summary>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Collection of slot information records for all known meters.
|
||||
/// </returns>
|
||||
public async Task<GciAllSlotsInfo> GetAllSlotsAsync(
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var result = await _innerMeterAPI.GetAllMetersInfo(token);
|
||||
var result = await _innerMeterAPI.GetAllMetersInfo(token).ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GciCleanSlotsResult> CleanSlotsAsync(
|
||||
/// <summary>
|
||||
/// Cleans one meter slot and releases its runtime resources.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id to clean. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Result describing whether the slot cleanup succeeded or failed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciCleanSlotResult> CleanSlotAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var result = await _innerMeterAPI.CleanSlotsAsync(token);
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.", nameof(slot));
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
var result = await _innerMeterAPI.CleanSlotAsync(slot, token).ConfigureAwait(false);
|
||||
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans all initialized meter slots and releases related runtime resources.
|
||||
/// </summary>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Result describing whether cleanup of all slots succeeded or failed.
|
||||
/// </returns>
|
||||
public async Task<GciCleanAllSlotsResult> CleanAllSlotsAsync(
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var result = await _innerMeterAPI.CleanAllSlotsAsync(token).ConfigureAwait(false);
|
||||
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ================================== PASSWORD ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Sets runtime password for the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="password">Password to assign to the meter.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Result containing password update status.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when slot id is invalid or password is empty.
|
||||
/// </exception>
|
||||
public async Task<GciSetPasswordResult> SetPasswordAsync(
|
||||
int slot,
|
||||
string password,
|
||||
@ -160,9 +304,9 @@ namespace GenesisCordonelInterface.API
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
throw new ArgumentException("Password is empty.");
|
||||
|
||||
GciSetPasswordResult result = await _innerMeterAPI.SetMeterPasswordAsync(slot, password, token);
|
||||
var result = await _innerMeterAPI.SetMeterPasswordAsync(slot, password, token).ConfigureAwait(false);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -170,27 +314,64 @@ namespace GenesisCordonelInterface.API
|
||||
#endregion
|
||||
|
||||
#region ================================== LOGIN ==================================
|
||||
public Task<PublicModels.GciLoginResult> LoginOneSlotAsync(
|
||||
|
||||
/// <summary>
|
||||
/// Logs in to the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Login result containing login state and status message.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<PublicModels.GciLoginResult> LoginOneSlotAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.LoginOneSlotAsync(slot, token);
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await _innerMeterAPI.LoginOneSlotAsync(slot, token).ConfigureAwait(false);
|
||||
return result;
|
||||
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ================================== CONNECTION ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Connects the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Connection result containing connection state and status message.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciConnectResult> ConnectOneSlotAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
GciConnectResult result = await _innerMeterAPI.ConnectOneSlotAsync(slot, token);
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
var result = await _innerMeterAPI.ConnectOneSlotAsync(slot, token).ConfigureAwait(false);
|
||||
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Disconnect result containing final connection state and status message.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciDisconnectResult> DisconnectAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
@ -198,15 +379,25 @@ namespace GenesisCordonelInterface.API
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await _innerMeterAPI.DisconnectAsync(slot, token);
|
||||
var result = await _innerMeterAPI.DisconnectAsync(slot, token).ConfigureAwait(false);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ================================== PCB ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Reads PCB identifier from the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Result containing PCB id and read status.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciGetPcbIdResult> GetPcbIdAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
@ -214,104 +405,167 @@ namespace GenesisCordonelInterface.API
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await _innerMeterAPI.GetPcbIdAsync(slot, token);
|
||||
var result = await _innerMeterAPI.GetPcbIdAsync(slot, token).ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ================================== READ ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
public RegisterReadResult ReadRegister(
|
||||
int slot,
|
||||
string registerName)
|
||||
{
|
||||
return _innerMeterAPI.ReadRegister(slot, registerName);
|
||||
}
|
||||
|
||||
public Task<RegisterReadResult> ReadRegisterAsync(
|
||||
/// <summary>
|
||||
/// Reads a firmware register value from the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="registerName">Register identifier to read.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Register read result containing raw register value and operation status.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when slot id or register name is invalid.
|
||||
/// </exception>
|
||||
public async Task<RegisterReadResult> ReadRegisterAsync(
|
||||
int slot,
|
||||
string registerName,
|
||||
CancellationToken token = default(CancellationToken))
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.ReadRegisterAsync(slot, registerName, token);
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.", nameof(slot));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(registerName))
|
||||
throw new ArgumentException("Register name is empty.", nameof(registerName));
|
||||
|
||||
var result = await _innerMeterAPI
|
||||
.ReadRegisterAsync(slot, registerName, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== WRITE ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Writes a value to a firmware register.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="registerName">Register identifier to write.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
/// <param name="storeToDevice">
|
||||
/// Indicates whether configuration should be permanently stored.
|
||||
/// </param>
|
||||
/// <param name="refreshSystemState">
|
||||
/// Indicates whether firmware system state should be refreshed after write.
|
||||
/// </param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Register write result describing write status.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when slot id or register name is invalid.
|
||||
/// </exception>
|
||||
public async Task<RegisterWriteResult> WriteRegisterAsync(
|
||||
int slot,
|
||||
string registerName,
|
||||
object value,
|
||||
bool storeToDevice = false,
|
||||
bool refreshSystemState = false,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.", nameof(slot));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(registerName))
|
||||
throw new ArgumentException("Register name is empty.", nameof(registerName));
|
||||
|
||||
var result = await _innerMeterAPI
|
||||
.WriteRegisterAsync(
|
||||
slot,
|
||||
registerName,
|
||||
value,
|
||||
storeToDevice,
|
||||
refreshSystemState,
|
||||
token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ================================== Password ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Updates meter password for the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="password">New password.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Password update result.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when slot id or password is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciSetPasswordResult> SetMeterPasswordAsync(
|
||||
int slot,
|
||||
string registerName,
|
||||
object value,
|
||||
bool storeToDevice = false,
|
||||
bool refreshSystemState = false,
|
||||
string password,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var result = await _innerMeterAPI.WriteRegisterAsync(
|
||||
slot,
|
||||
registerName,
|
||||
value,
|
||||
storeToDevice,
|
||||
refreshSystemState,
|
||||
token);
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.", nameof(slot));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
throw new ArgumentException("Password is empty.", nameof(password));
|
||||
|
||||
var result = await _innerMeterAPI
|
||||
.SetMeterPasswordAsync(slot, password, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
public RegisterWriteResult WriteRegister(
|
||||
int slot,
|
||||
string registerName,
|
||||
object value,
|
||||
bool storeToDevice = false,
|
||||
bool refreshSystemState = false)
|
||||
{
|
||||
var result = _innerMeterAPI.WriteRegister(
|
||||
slot,
|
||||
registerName,
|
||||
value,
|
||||
storeToDevice,
|
||||
refreshSystemState);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GciSetPasswordResult> SetMeterPasswordAsync(int slot, string password)
|
||||
{
|
||||
var result = await _innerMeterAPI.SetMeterPasswordAsync(slot, password);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
public GciSetPasswordResult SetMeterPassword(int slot, string password)
|
||||
{
|
||||
var result = _innerMeterAPI.SetMeterPassword(slot, password);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== DEBUG STATUS ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Gets runtime diagnostic information for all active workers.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Collection containing worker state, queue information,
|
||||
/// current operation and activity timestamps.
|
||||
/// </returns>
|
||||
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
|
||||
{
|
||||
return _innerMeterAPI.GetWorkerDebugStatuses();
|
||||
return _innerMeterAPI?.GetWorkerDebugStatuses();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets runtime diagnostic information for all meter slots.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Collection containing slot state, connection state,
|
||||
/// selected state and communication configuration.
|
||||
/// </returns>
|
||||
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
|
||||
{
|
||||
return _innerMeterAPI.GetMeterBatchDebugStatuses();
|
||||
return _innerMeterAPI?.GetMeterBatchDebugStatuses();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises meter batch status change notification.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Intended to notify external consumers after changes in meter state.
|
||||
/// </remarks>
|
||||
public void RaiseMeterBatchStatusChanged()
|
||||
{
|
||||
var statuses = GetMeterBatchDebugStatuses();
|
||||
@ -321,124 +575,211 @@ namespace GenesisCordonelInterface.API
|
||||
handler(statuses);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== SLOT SELECTION ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Sets selection state for a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id.</param>
|
||||
/// <param name="selected">Selection state.</param>
|
||||
public void SetSlotSelected(int slot, bool selected)
|
||||
{
|
||||
_innerMeterAPI.SetSlotSelected(slot, selected);
|
||||
_innerMeterAPI?.SetSlotSelected(slot, selected);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified slot is selected.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id.</param>
|
||||
/// <returns>
|
||||
/// True if slot is selected; otherwise false.
|
||||
/// </returns>
|
||||
public bool IsSlotSelected(int slot)
|
||||
{
|
||||
return _innerMeterAPI.IsSlotSelected(slot);
|
||||
return (bool)(_innerMeterAPI?.IsSlotSelected(slot));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all selected slot identifiers.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Ordered collection of selected slot ids.
|
||||
/// </returns>
|
||||
public List<int> GetSelectedSlots()
|
||||
{
|
||||
return _innerMeterAPI.GetSelectedSlots();
|
||||
return _innerMeterAPI?.GetSelectedSlots();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== SLOT PORT CONFIG ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
/*public void SetSlotRequestPort(int slot, string portName)
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(portName))
|
||||
{
|
||||
_requestPorts.Remove(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
_requestPorts[slot] = new GciPortConfig
|
||||
{
|
||||
PortName = portName,
|
||||
Type = "Serial"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
public void SetSlotStreamingPort(int slot, string portName)
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(portName))
|
||||
{
|
||||
_streamingPorts.Remove(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
_streamingPorts[slot] = new GciPortConfig
|
||||
{
|
||||
PortName = portName,
|
||||
Type = "Serial"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
public GciPortConfig? GetSlotRequestPort(int slot)
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
GciPortConfig port;
|
||||
if (_requestPorts.TryGetValue(slot, out port))
|
||||
return port;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public GciPortConfig? GetSlotStreamingPort(int slot)
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
GciPortConfig port;
|
||||
if (_streamingPorts.TryGetValue(slot, out port))
|
||||
return port;
|
||||
|
||||
return null;
|
||||
}
|
||||
}*/
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== METER BATCH SETUP ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
public void ReloadSlotSetup()
|
||||
{
|
||||
_innerMeterAPI.ReloadSlotSetup();
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
public void SaveSlotSetup(List<MeterBatchDebugStatus> data)
|
||||
{
|
||||
_innerMeterAPI.SaveSlotSetup(data);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
#region ================================== Register names ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available firmware register identifiers.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Ordered collection of register names.
|
||||
/// </returns>
|
||||
public List<string> GetAllRegisterNames()
|
||||
{
|
||||
return _innerMeterAPI.GetAllRegisterNames();
|
||||
return _innerMeterAPI?.GetAllRegisterNames();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Laatzen Preadjustment processes
|
||||
|
||||
#region ================================== PreAdjustment ==================================
|
||||
public PreAdjustmentInitializationResult Preadjustment_Initialization(
|
||||
ProcessProgress pp,
|
||||
List<MeterStateControl> mc)
|
||||
{
|
||||
//init handlers
|
||||
pp.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
|
||||
|
||||
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc);
|
||||
}
|
||||
|
||||
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
|
||||
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_DetectAsync(selectedSlots, token);
|
||||
}
|
||||
|
||||
public PreadjustmentDetectResult PreAdjustment_DetectDirect(
|
||||
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_DetectDirect(selectedSlots, token);
|
||||
}
|
||||
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_PreparationAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_PreparationAsync(slot, token);
|
||||
}
|
||||
|
||||
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect()
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_PreparationDirect();
|
||||
}
|
||||
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_AmplitudeTestAsync(slot, token);
|
||||
}
|
||||
|
||||
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect()
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_AmplitudeTestDirect();
|
||||
}
|
||||
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, token);
|
||||
}
|
||||
|
||||
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect()
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect();
|
||||
}
|
||||
|
||||
public bool PreAdjustment_PushTemperature(
|
||||
double temperature)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
|
||||
}
|
||||
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_OffsetTestAsync(slot, token);
|
||||
}
|
||||
|
||||
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect()
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_OffsetTestDirect();
|
||||
}
|
||||
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_CompletionAsync(slot, token);
|
||||
}
|
||||
|
||||
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect()
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_CompletionDirect();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================================== UNI DATA STORAGE READER ==================================
|
||||
|
||||
//LoginPasswords reading
|
||||
|
||||
/// <summary>
|
||||
/// Reads meter login password from configured GCI data storage.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing PCB ID or another configured lookup value.
|
||||
/// </param>
|
||||
/// <param name="token">
|
||||
/// Cancellation token used to cancel the asynchronous operation.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Password if found; otherwise null.
|
||||
/// </returns>
|
||||
public Task<string> ReadMeterLoginPasswordAsync(
|
||||
DataQuery query,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return loginPasswordsReader.ReadMeterLoginPasswordAsync(
|
||||
query,
|
||||
token);
|
||||
}
|
||||
|
||||
//CalibrationParams reading
|
||||
|
||||
/// <summary>
|
||||
/// Reads pre-adjustment calibration parameters
|
||||
/// from configured GCI data storage.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing meter size or another configured lookup value.
|
||||
/// </param>
|
||||
/// <param name="token">
|
||||
/// Cancellation token used to cancel the asynchronous operation.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Dictionary:
|
||||
///
|
||||
/// Key:
|
||||
/// Calibration parameter name
|
||||
///
|
||||
/// Value:
|
||||
/// Calibration parameter value
|
||||
/// </returns>
|
||||
public Task<Dictionary<string, UInt32>> ReadPreAdjustmentCalibrationParamsAsync(
|
||||
DataQuery query,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return calibrationParamsReader.ReadCalibrationParamsAsync(
|
||||
query,
|
||||
token);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -126,6 +126,15 @@ namespace GenesisCordonelInterface.API
|
||||
};
|
||||
}
|
||||
|
||||
public static PublicModels.GciPortConfig MapPortBack(string portName, string portTyoe)
|
||||
{
|
||||
return new PublicModels.GciPortConfig
|
||||
{
|
||||
PortName = portName,
|
||||
Type = portTyoe
|
||||
};
|
||||
}
|
||||
|
||||
public static MeterBatchDebugStatus ToMeterBatchDebugStatus(GciSlotInfo slot)
|
||||
{
|
||||
if (slot == null)
|
||||
@ -136,6 +145,7 @@ namespace GenesisCordonelInterface.API
|
||||
Slot = slot.SlotId,
|
||||
Selected = true,
|
||||
PcbId = slot.PcbId,
|
||||
IsConnected = false,
|
||||
IsLoggedOn = false,
|
||||
RequestPort = slot.RequestPort == null ? "" : slot.RequestPort.PortName,
|
||||
StreamingPort = slot.StreamingPort == null ? "" : slot.StreamingPort.PortName,
|
||||
|
||||
@ -49,6 +49,18 @@ namespace GenesisCordonelInterface.API
|
||||
/// </summary>
|
||||
public class PublicModels
|
||||
{
|
||||
public static bool HideSensitiveValues { get; set; } = true;
|
||||
|
||||
private static string FormatPassword(string password)
|
||||
{
|
||||
if (!HideSensitiveValues)
|
||||
return password ?? "<null>";
|
||||
|
||||
if (string.IsNullOrEmpty(password))
|
||||
return "<empty>";
|
||||
|
||||
return "********";
|
||||
}
|
||||
/// <summary>
|
||||
/// Public DTOs exposed to external systems.
|
||||
/// These models represent the contract of the GCI API.
|
||||
@ -90,6 +102,7 @@ namespace GenesisCordonelInterface.API
|
||||
public int Slot { get; set; }
|
||||
public bool Selected { get; set; }
|
||||
public string PcbId { get; set; }
|
||||
public bool IsConnected { get; set; }
|
||||
public bool IsLoggedOn { get; set; }
|
||||
public string RequestPort { get; set; }
|
||||
public string StreamingPort { get; set; }
|
||||
@ -97,6 +110,7 @@ namespace GenesisCordonelInterface.API
|
||||
public string FwVersion { get; set; }
|
||||
public string InterfaceVersion { get; set; }
|
||||
}
|
||||
|
||||
public class RegisterWriteResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
@ -140,29 +154,30 @@ namespace GenesisCordonelInterface.API
|
||||
public class GciSlotInfo
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
|
||||
public bool Exists { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public bool IsConnected { get; set; }
|
||||
public bool IsLoggedOn { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string PcbId { get; set; }
|
||||
public string Password { 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}, Exists={1}, Success={2}, Message={3}, PcbId={4}, Password={5}, ConfigSource={6}, PasswordSource={7}, RequestPort={8}, StreamingPort={9}",
|
||||
"SlotId={0}, Exists={1}, Success={2}, IsConnected={3}, IsLoggedOn={4}, Message={5}, PcbId={6}, Password={7}, ConfigSource={8}, PasswordSource={9}, RequestPort={10}, StreamingPort={11}",
|
||||
SlotId,
|
||||
Exists,
|
||||
Success,
|
||||
IsConnected,
|
||||
IsLoggedOn,
|
||||
Message,
|
||||
PcbId,
|
||||
Password,
|
||||
FormatPassword(Password),
|
||||
ConfigSource,
|
||||
PasswordSource,
|
||||
RequestPort,
|
||||
@ -267,12 +282,13 @@ namespace GenesisCordonelInterface.API
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"SlotId={0}, GciConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}, Password=hidden",
|
||||
"SlotId={0}, GciConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}, Password={5}",
|
||||
SlotId,
|
||||
ConfigSource,
|
||||
PasswordSource,
|
||||
RequestPort,
|
||||
StreamingPort);
|
||||
StreamingPort,
|
||||
FormatPassword(Password));
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,7 +322,7 @@ namespace GenesisCordonelInterface.API
|
||||
}
|
||||
}
|
||||
|
||||
public class GciCleanSlotsResult
|
||||
public class GciCleanAllSlotsResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
@ -319,6 +335,21 @@ namespace GenesisCordonelInterface.API
|
||||
Message);
|
||||
}
|
||||
}
|
||||
public class GciCleanSlotResult
|
||||
{
|
||||
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 class GciGetPcbIdResult
|
||||
{
|
||||
@ -341,39 +372,31 @@ namespace GenesisCordonelInterface.API
|
||||
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 bool IsConnected { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"SlotId={0}, Success={1}, IsLoggedOn={2}, PcbId={3}, FwVersion={4}, InterfaceVersion={5}, InterfaceSupportsFwVersion={6}, Registers={7}, Message={8}",
|
||||
"SlotId={0}, Success={1}, PcbId={2}, IsConnected={3}, Message={4}",
|
||||
SlotId,
|
||||
Success,
|
||||
IsLoggedOn,
|
||||
PcbId,
|
||||
FwVersion,
|
||||
InterfaceVersion,
|
||||
InterfaceSupportsFwVersion,
|
||||
Registers == null ? 0 : Registers.Count,
|
||||
IsConnected,
|
||||
Message);
|
||||
}
|
||||
}
|
||||
|
||||
public class GciLoginResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public int SlotId { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string PcbId { get; set; }
|
||||
public bool IsLoggedOn { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Slot={SlotId}, Success={Success}, IsLoggedOn={IsLoggedOn}, Message={Message ?? "<none>"}";
|
||||
return $"Slot={SlotId}, Success={Success}, PcbId={PcbId}, IsLoggedOn={IsLoggedOn}, Message={Message ?? "<none>"}";
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,10 +443,62 @@ namespace GenesisCordonelInterface.API
|
||||
"SlotId={0}, Success={1}, Password={2}, Message={3}",
|
||||
SlotId,
|
||||
Success,
|
||||
Password,
|
||||
FormatPassword(Password),
|
||||
Message);
|
||||
}
|
||||
}
|
||||
|
||||
public class PreAdjustmentProcessResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public int Slot { get; set; }
|
||||
public string ProcessName { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
$"Success={Success}, " +
|
||||
$"Slot={Slot}, " +
|
||||
$"Process={ProcessName}, " +
|
||||
$"Error={ErrorMessage ?? "None"}";
|
||||
}
|
||||
}
|
||||
|
||||
public class PreadjustmentDetectResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public int DetectedMeterCount { get; set; }
|
||||
public int DetectedThermometerCount { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
$"Success={Success}, " +
|
||||
$"Meters={DetectedMeterCount}, " +
|
||||
$"Thermometers={DetectedThermometerCount}, " +
|
||||
$"Error={ErrorMessage ?? "None"}";
|
||||
}
|
||||
}
|
||||
|
||||
public class PreAdjustmentInitializationResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Slots { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
$"Success={Success}, " +
|
||||
$"Slots={Slots}, " +
|
||||
$"Error={ErrorMessage ?? "None"}";
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
36
GenesisCordonelInterface/Config/gci_config.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"_Comment": "GCI DataStorage configuration",
|
||||
|
||||
"DataStorage": {
|
||||
|
||||
"MeterLoginPasswords": {
|
||||
|
||||
"___Documentation___": {
|
||||
"Description": "Reads login passwords for meters by PCB ID",
|
||||
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||
"DataSource": "Database connection string",
|
||||
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [PcbId]=QUERYPARAM -> PCB ID provided during GetPasswordAsync()."
|
||||
},
|
||||
|
||||
"Name": "MeterLoginPasswords",
|
||||
"Type": "LocalDatabase",
|
||||
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||
"QueryTemplate": "SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM"
|
||||
},
|
||||
|
||||
"PreAdjustmentCalibrationParams": {
|
||||
|
||||
"___Documentation___": {
|
||||
"Description": "Reads pre-adjustment calibration parameters by meter size",
|
||||
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||
"DataSource": "Database connection string",
|
||||
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [MeterSize]=QUERYPARAM -> meter size provided during ReadCalibrationParamsAsync()."
|
||||
},
|
||||
|
||||
"Name": "PreAdjustmentCalibrationParams",
|
||||
"Type": "LocalDatabase",
|
||||
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
|
||||
}
|
||||
}
|
||||
}
|
||||
32
GenesisCordonelInterface/Core/Config/GciConfig.cs
Normal file
@ -0,0 +1,32 @@
|
||||
namespace GenesisCordonelInterface.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Root GCI configuration object loaded from gci_config.json.
|
||||
///
|
||||
/// This class represents the top-level configuration structure
|
||||
/// and serves as an entry point for all configurable GCI features.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// {
|
||||
/// "DataStorageSection":
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
/// }
|
||||
/// </summary>
|
||||
public class GciConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Data storage configuration section.
|
||||
///
|
||||
/// Contains definitions for all configured readers and writers,
|
||||
/// such as:
|
||||
///
|
||||
/// - MeterLoginPasswords
|
||||
/// - PreAdjustmentCalibrationParams
|
||||
/// - future storage providers
|
||||
/// </summary>
|
||||
public DataStorage.Config.GciDataStorageConfig DataStorageSection { get; set; }
|
||||
}
|
||||
}
|
||||
177
GenesisCordonelInterface/Core/Config/GciConfigLoader.cs
Normal file
@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads GCI configuration from gci_config.json.
|
||||
///
|
||||
/// Responsibilities:
|
||||
/// - Locate configuration file
|
||||
/// - Deserialize JSON into strongly typed objects
|
||||
/// - Normalize relative file paths
|
||||
/// - Preserve database connection strings and REST URLs
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// Config/
|
||||
/// gci_config.json
|
||||
///
|
||||
/// Data/
|
||||
/// meter_passwords.csv
|
||||
///
|
||||
/// Relative paths:
|
||||
/// Data\file.csv
|
||||
///
|
||||
/// become:
|
||||
///
|
||||
/// C:\App\bin\Debug\Data\file.csv
|
||||
///
|
||||
/// Database sources remain unchanged:
|
||||
///
|
||||
/// Server=(localdb)\MojaDB;Database=...
|
||||
/// </summary>
|
||||
public static class GciConfigLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads default GCI configuration from:
|
||||
///
|
||||
/// Config\gci_config.json
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Loaded GCI configuration.
|
||||
/// </returns>
|
||||
public static GciConfig LoadDefault()
|
||||
{
|
||||
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
string configPath = Path.Combine(
|
||||
baseDirectory,
|
||||
"Config",
|
||||
"gci_config.json");
|
||||
|
||||
return Load(configPath, baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads GCI configuration from specified file.
|
||||
/// </summary>
|
||||
/// <param name="configPath">
|
||||
/// Path to configuration json file.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base directory used for resolving relative paths.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Loaded configuration object.
|
||||
/// </returns>
|
||||
public static GciConfig Load(
|
||||
string configPath,
|
||||
string baseDirectory = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(configPath))
|
||||
throw new ArgumentException(
|
||||
"Config path must not be empty.",
|
||||
nameof(configPath));
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
throw new FileNotFoundException(
|
||||
"GCI config file was not found.",
|
||||
configPath);
|
||||
|
||||
string json = File.ReadAllText(configPath);
|
||||
|
||||
GciConfig config =
|
||||
JsonConvert.DeserializeObject<GciConfig>(json);
|
||||
|
||||
if (config == null)
|
||||
throw new InvalidOperationException(
|
||||
"GCI config could not be loaded.");
|
||||
|
||||
NormalizeDataStoragePaths(
|
||||
config,
|
||||
baseDirectory ?? Path.GetDirectoryName(configPath));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes paths for all configured data storage entries.
|
||||
///
|
||||
/// Converts relative file paths into absolute paths.
|
||||
/// Database connection strings remain untouched.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Loaded configuration object.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base path used for relative resolution.
|
||||
/// </param>
|
||||
private static void NormalizeDataStoragePaths(
|
||||
GciConfig config,
|
||||
string baseDirectory)
|
||||
{
|
||||
if (config.DataStorageSection == null)
|
||||
return;
|
||||
|
||||
NormalizePath(
|
||||
config.DataStorageSection.MeterLoginPasswords,
|
||||
baseDirectory);
|
||||
|
||||
NormalizePath(
|
||||
config.DataStorageSection.PreAdjustmentCalibrationParams,
|
||||
baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts relative file paths into absolute paths.
|
||||
///
|
||||
/// Applies only to:
|
||||
/// - LocalCsv
|
||||
/// - RemoteCsv
|
||||
/// - LocalJson
|
||||
/// - RemoteJson
|
||||
///
|
||||
/// Does not modify:
|
||||
/// - Database connection strings
|
||||
/// - REST URLs
|
||||
/// </summary>
|
||||
/// <param name="storageConfig">
|
||||
/// Storage configuration.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base path for resolution.
|
||||
/// </param>
|
||||
private static void NormalizePath(
|
||||
DataStorageConfig storageConfig,
|
||||
string baseDirectory)
|
||||
{
|
||||
if (storageConfig == null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(storageConfig.DataSource))
|
||||
return;
|
||||
|
||||
// Database connection strings and URLs
|
||||
// must never be treated as file paths.
|
||||
if (storageConfig.Type == DataStorageType.LocalDatabase ||
|
||||
storageConfig.Type == DataStorageType.RemoteDatabase ||
|
||||
storageConfig.Type == DataStorageType.RestApi)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Path.IsPathRooted(storageConfig.DataSource))
|
||||
return;
|
||||
|
||||
storageConfig.DataSource =
|
||||
Path.GetFullPath(
|
||||
Path.Combine(
|
||||
baseDirectory,
|
||||
storageConfig.DataSource));
|
||||
}
|
||||
}
|
||||
}
|
||||
36
GenesisCordonelInterface/Core/Config/gci_config.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"_Comment": "GCI DataStorage configuration",
|
||||
|
||||
"DataStorageSection": {
|
||||
|
||||
"MeterLoginPasswords": {
|
||||
|
||||
"___Documentation___": {
|
||||
"Description": "Reads login passwords for meters by PCB ID",
|
||||
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||
"DataSource": "Database connection string",
|
||||
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [PcbId]=QUERYPARAM -> PCB ID provided during GetPasswordAsync()."
|
||||
},
|
||||
|
||||
"Name": "MeterLoginPasswords",
|
||||
"Type": "LocalDatabase",
|
||||
"DataSource": "Server=(localdb)\\SensusLocalDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||
"QueryTemplate": "SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM"
|
||||
},
|
||||
|
||||
"PreAdjustmentCalibrationParams": {
|
||||
|
||||
"___Documentation___": {
|
||||
"Description": "Reads pre-adjustment calibration parameters by meter size",
|
||||
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||
"DataSource": "Database connection string",
|
||||
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [MeterSize]=QUERYPARAM -> meter size provided during ReadCalibrationParamsAsync()."
|
||||
},
|
||||
|
||||
"Name": "PreAdjustmentCalibrationParams",
|
||||
"Type": "LocalDatabase",
|
||||
"DataSource": "Server=(localdb)\\SensusLocalDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Root GCI configuration object loaded from gci_config.json.
|
||||
///
|
||||
/// This class represents the top-level configuration structure
|
||||
/// and serves as an entry point for all configurable GCI features.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// {
|
||||
/// "DataStorage":
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
/// }
|
||||
/// </summary>
|
||||
public class GciConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Data storage configuration section.
|
||||
///
|
||||
/// Contains definitions for all configured readers and writers,
|
||||
/// such as:
|
||||
///
|
||||
/// - MeterLoginPasswords
|
||||
/// - PreAdjustmentCalibrationParams
|
||||
/// - future storage providers
|
||||
/// </summary>
|
||||
public GciDataStorageConfig DataStorage { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads GCI configuration from gci_config.json.
|
||||
///
|
||||
/// Responsibilities:
|
||||
/// - Locate configuration file
|
||||
/// - Deserialize JSON into strongly typed objects
|
||||
/// - Normalize relative file paths
|
||||
/// - Preserve database connection strings and REST URLs
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// Config/
|
||||
/// gci_config.json
|
||||
///
|
||||
/// Data/
|
||||
/// meter_passwords.csv
|
||||
///
|
||||
/// Relative paths:
|
||||
/// Data\file.csv
|
||||
///
|
||||
/// become:
|
||||
///
|
||||
/// C:\App\bin\Debug\Data\file.csv
|
||||
///
|
||||
/// Database sources remain unchanged:
|
||||
///
|
||||
/// Server=(localdb)\MojaDB;Database=...
|
||||
/// </summary>
|
||||
public static class GciConfigLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads default GCI configuration from:
|
||||
///
|
||||
/// Config\gci_config.json
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Loaded GCI configuration.
|
||||
/// </returns>
|
||||
public static GciConfig LoadDefault()
|
||||
{
|
||||
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
string configPath = Path.Combine(
|
||||
baseDirectory,
|
||||
"Config",
|
||||
"gci_config.json");
|
||||
|
||||
return Load(configPath, baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads GCI configuration from specified file.
|
||||
/// </summary>
|
||||
/// <param name="configPath">
|
||||
/// Path to configuration json file.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base directory used for resolving relative paths.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Loaded configuration object.
|
||||
/// </returns>
|
||||
public static GciConfig Load(
|
||||
string configPath,
|
||||
string baseDirectory = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(configPath))
|
||||
throw new ArgumentException(
|
||||
"Config path must not be empty.",
|
||||
nameof(configPath));
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
throw new FileNotFoundException(
|
||||
"GCI config file was not found.",
|
||||
configPath);
|
||||
|
||||
string json = File.ReadAllText(configPath);
|
||||
|
||||
GciConfig config =
|
||||
JsonConvert.DeserializeObject<GciConfig>(json);
|
||||
|
||||
if (config == null)
|
||||
throw new InvalidOperationException(
|
||||
"GCI config could not be loaded.");
|
||||
|
||||
NormalizeDataStoragePaths(
|
||||
config,
|
||||
baseDirectory ?? Path.GetDirectoryName(configPath));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes paths for all configured data storage entries.
|
||||
///
|
||||
/// Converts relative file paths into absolute paths.
|
||||
/// Database connection strings remain untouched.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Loaded configuration object.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base path used for relative resolution.
|
||||
/// </param>
|
||||
private static void NormalizeDataStoragePaths(
|
||||
GciConfig config,
|
||||
string baseDirectory)
|
||||
{
|
||||
if (config.DataStorage == null)
|
||||
return;
|
||||
|
||||
NormalizePath(
|
||||
config.DataStorage.MeterLoginPasswords,
|
||||
baseDirectory);
|
||||
|
||||
NormalizePath(
|
||||
config.DataStorage.PreAdjustmentCalibrationParams,
|
||||
baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts relative file paths into absolute paths.
|
||||
///
|
||||
/// Applies only to:
|
||||
/// - LocalCsv
|
||||
/// - RemoteCsv
|
||||
/// - LocalJson
|
||||
/// - RemoteJson
|
||||
///
|
||||
/// Does not modify:
|
||||
/// - Database connection strings
|
||||
/// - REST URLs
|
||||
/// </summary>
|
||||
/// <param name="storageConfig">
|
||||
/// Storage configuration.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base path for resolution.
|
||||
/// </param>
|
||||
private static void NormalizePath(
|
||||
DataStorageConfig storageConfig,
|
||||
string baseDirectory)
|
||||
{
|
||||
if (storageConfig == null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(storageConfig.DataSource))
|
||||
return;
|
||||
|
||||
// Database connection strings and URLs
|
||||
// must never be treated as file paths.
|
||||
if (storageConfig.Type == DataStorageType.LocalDatabase ||
|
||||
storageConfig.Type == DataStorageType.RemoteDatabase ||
|
||||
storageConfig.Type == DataStorageType.RestApi)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Path.IsPathRooted(storageConfig.DataSource))
|
||||
return;
|
||||
|
||||
storageConfig.DataSource =
|
||||
Path.GetFullPath(
|
||||
Path.Combine(
|
||||
baseDirectory,
|
||||
storageConfig.DataSource));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains all configured GCI data storage sources.
|
||||
///
|
||||
/// Each property represents one logical use-case
|
||||
/// and points to its storage configuration.
|
||||
///
|
||||
/// Example:
|
||||
/// MeterLoginPasswords
|
||||
/// -> SQL database
|
||||
///
|
||||
/// PreAdjustmentCalibrationParams
|
||||
/// -> CSV file
|
||||
/// </summary>
|
||||
public class GciDataStorageConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration for meter login password lookup.
|
||||
/// </summary>
|
||||
public DataStorageConfig MeterLoginPasswords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for pre-adjustment calibration data lookup.
|
||||
/// </summary>
|
||||
public DataStorageConfig PreAdjustmentCalibrationParams { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines common contract for all GCI data storage readers.
|
||||
///
|
||||
/// Implementations can read data from different storage types:
|
||||
/// - SQL database
|
||||
/// - CSV file
|
||||
/// - JSON file
|
||||
/// - REST API
|
||||
///
|
||||
/// Consumers should use this interface instead of concrete readers.
|
||||
/// </summary>
|
||||
public interface IDataStorageReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads data from configured storage using provided query.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Query object containing lookup parameters.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Storage-specific result object.
|
||||
/// </returns>
|
||||
object GetData(Models.DataQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether configured data source is accessible.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result of source test.
|
||||
/// </returns>
|
||||
Models.ReaderDiagnosticResult TestSource(bool enableDiagnostics);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether configured query is valid for the data source.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result of query test.
|
||||
/// </returns>
|
||||
Models.ReaderDiagnosticResult TestQuery(bool enableDiagnostics);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory responsible for creating appropriate
|
||||
/// data storage reader implementations.
|
||||
///
|
||||
/// The reader type is selected according to:
|
||||
///
|
||||
/// DataStorageConfig.Type
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// LocalDatabase
|
||||
/// -> DatabaseDataStorageReader
|
||||
///
|
||||
/// LocalCsv
|
||||
/// -> CsvDataStorageReader
|
||||
///
|
||||
/// LocalJson
|
||||
/// -> JsonDataStorageReader
|
||||
///
|
||||
/// RestApi
|
||||
/// -> RestApiDataStorageReader
|
||||
///
|
||||
/// This factory hides implementation details from
|
||||
/// higher layers and keeps consumers independent
|
||||
/// of storage technology.
|
||||
///
|
||||
/// Trace:
|
||||
///
|
||||
/// MeterLoginPasswordReader
|
||||
/// -> DataStorageReaderFactory.Create()
|
||||
/// -> DatabaseDataStorageReader
|
||||
/// -> CsvDataStorageReader
|
||||
/// -> JsonDataStorageReader
|
||||
/// -> RestApiDataStorageReader
|
||||
/// </summary>
|
||||
public static class DataStorageReaderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates appropriate reader implementation
|
||||
/// according to storage configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Data storage configuration loaded from gci_config.json.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Configured storage reader implementation.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Configuration is null.
|
||||
/// </exception>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// Unsupported storage type.
|
||||
/// </exception>
|
||||
public static IDataStorageReader Create(
|
||||
DataStorageConfig config)
|
||||
{
|
||||
if (config == null)
|
||||
throw new ArgumentNullException(nameof(config));
|
||||
|
||||
switch (config.Type)
|
||||
{
|
||||
case DataStorageType.RemoteDatabase:
|
||||
case DataStorageType.LocalDatabase:
|
||||
|
||||
return new Providers.DatabaseDataStorageReader(config);
|
||||
|
||||
case DataStorageType.RemoteCsv:
|
||||
case DataStorageType.LocalCsv:
|
||||
|
||||
return new Providers.CsvDataStorageReader(config);
|
||||
|
||||
case DataStorageType.RemoteJson:
|
||||
case DataStorageType.LocalJson:
|
||||
|
||||
return new Providers.JsonDataStorageReader(config);
|
||||
|
||||
case DataStorageType.RestApi:
|
||||
|
||||
return new Providers.RestApiDataStorageReader(config);
|
||||
|
||||
default:
|
||||
|
||||
throw new NotSupportedException(
|
||||
$"Unsupported DataStorageType: '{config.Type}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents query input for data storage readers.
|
||||
///
|
||||
/// Query parameters are used as lookup values
|
||||
/// during storage search operations.
|
||||
///
|
||||
/// A single parameter:
|
||||
///
|
||||
/// 231630279
|
||||
///
|
||||
/// executes one lookup.
|
||||
///
|
||||
/// Multiple parameters:
|
||||
///
|
||||
/// 231630279
|
||||
/// 231630243
|
||||
/// 231630148
|
||||
///
|
||||
/// can execute batch operations.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// QueryTemplate:
|
||||
///
|
||||
/// SELECT [Password]
|
||||
/// WHERE [PcbId]=QUERYPARAM
|
||||
///
|
||||
/// Query:
|
||||
///
|
||||
/// QueryParams:
|
||||
/// 231630279
|
||||
///
|
||||
/// Result:
|
||||
///
|
||||
/// Password belonging to 231630279.
|
||||
/// </summary>
|
||||
public class DataQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of query values.
|
||||
///
|
||||
/// Single item:
|
||||
///
|
||||
/// QueryParams[0]
|
||||
///
|
||||
/// represents one lookup.
|
||||
///
|
||||
/// Multiple items may be processed
|
||||
/// as batch requests.
|
||||
/// </summary>
|
||||
public List<string> QueryParams { get; }
|
||||
= new List<string>();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration for one storage source.
|
||||
/// Loaded from gci_config.json
|
||||
/// </summary>
|
||||
public class DataStorageConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Logical storage name.
|
||||
/// Example:
|
||||
/// MeterLoginPasswords
|
||||
/// CalibrationParameters
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Storage implementation type.
|
||||
/// </summary>
|
||||
public DataStorageType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Connection string,
|
||||
/// file path,
|
||||
/// URL etc.
|
||||
/// </summary>
|
||||
public string DataSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Query template.
|
||||
/// Example:
|
||||
///
|
||||
/// SQL:
|
||||
/// SELECT Password
|
||||
/// FROM Passwords
|
||||
/// WHERE PcbId=QUERYPARAM
|
||||
///
|
||||
/// CSV:
|
||||
/// SELECT [Password]
|
||||
/// WHERE [PcbId]=QUERYPARAM
|
||||
/// </summary>
|
||||
public string QueryTemplate { get; set; }
|
||||
|
||||
public DataStorageConfig(
|
||||
string name,
|
||||
DataStorageType type,
|
||||
string dataSource,
|
||||
string queryTemplate)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
DataSource = dataSource;
|
||||
QueryTemplate = queryTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies supported data storage implementations.
|
||||
///
|
||||
/// The type determines which reader implementation
|
||||
/// will be created by DataStorageReaderFactory.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// LocalDatabase
|
||||
/// -> DatabaseDataStorageReader
|
||||
///
|
||||
/// LocalCsv
|
||||
/// -> CsvDataStorageReader
|
||||
///
|
||||
/// RestApi
|
||||
/// -> RestApiDataStorageReader
|
||||
///
|
||||
/// Source:
|
||||
///
|
||||
/// gci_config.json
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// "Type":"LocalDatabase"
|
||||
/// </summary>
|
||||
public enum DataStorageType
|
||||
{
|
||||
/// <summary>
|
||||
/// REST API endpoint.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// https://api.company.com/passwords
|
||||
/// </summary>
|
||||
RestApi,
|
||||
|
||||
/// <summary>
|
||||
/// Database located on remote server.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SQL server on network machine.
|
||||
/// </summary>
|
||||
RemoteDatabase,
|
||||
|
||||
/// <summary>
|
||||
/// Database located on local machine.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SQL LocalDB
|
||||
/// SQLite
|
||||
/// local SQL Server instance
|
||||
/// </summary>
|
||||
LocalDatabase,
|
||||
|
||||
/// <summary>
|
||||
/// JSON source stored remotely.
|
||||
/// </summary>
|
||||
RemoteJson,
|
||||
|
||||
/// <summary>
|
||||
/// JSON file stored locally.
|
||||
/// </summary>
|
||||
LocalJson,
|
||||
|
||||
/// <summary>
|
||||
/// CSV source stored remotely.
|
||||
/// </summary>
|
||||
RemoteCsv,
|
||||
|
||||
/// <summary>
|
||||
/// CSV file stored locally.
|
||||
/// </summary>
|
||||
LocalCsv
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents result returned by database-based
|
||||
/// data storage readers.
|
||||
///
|
||||
/// Supports both:
|
||||
///
|
||||
/// - Single-row lookups
|
||||
/// (e.g. MeterLoginPasswords)
|
||||
///
|
||||
/// - Multi-row queries
|
||||
/// (e.g. PreAdjustmentCalibrationParams)
|
||||
/// </summary>
|
||||
internal class DatabaseSearchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether at least one record
|
||||
/// was found.
|
||||
/// </summary>
|
||||
public bool Found { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executed SQL query text.
|
||||
/// Mainly intended for diagnostics
|
||||
/// and troubleshooting.
|
||||
/// </summary>
|
||||
public string Query { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// First returned row represented as
|
||||
/// column/value pairs.
|
||||
///
|
||||
/// Preserved for backward compatibility
|
||||
/// with existing readers expecting
|
||||
/// a single database record.
|
||||
/// </summary>
|
||||
public Dictionary<string, object> Values { get; }
|
||||
= new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// All returned rows represented as
|
||||
/// a collection of column/value dictionaries.
|
||||
///
|
||||
/// Intended for queries returning
|
||||
/// multiple records.
|
||||
/// </summary>
|
||||
public List<Dictionary<string, object>> Rows { get; }
|
||||
= new List<Dictionary<string, object>>();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents diagnostic result of data storage operations.
|
||||
///
|
||||
/// Used for:
|
||||
/// - source validation
|
||||
/// - query validation
|
||||
/// - connection tests
|
||||
/// - detailed diagnostic logging
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// TestSource()
|
||||
/// -> connection successful
|
||||
///
|
||||
/// TestQuery()
|
||||
/// -> column validation
|
||||
///
|
||||
/// Can also carry additional payload data.
|
||||
/// </summary>
|
||||
public class ReaderDiagnosticResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether operation completed successfully.
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human readable summary message.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// "Connection successful."
|
||||
///
|
||||
/// or:
|
||||
///
|
||||
/// "Column not found."
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Detailed diagnostic output lines.
|
||||
///
|
||||
/// Used for debugging and troubleshooting.
|
||||
/// </summary>
|
||||
public List<string> Diagnostics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional operation payload.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// loaded CSV lines
|
||||
/// SQL test value
|
||||
/// parsed content
|
||||
/// </summary>
|
||||
public object Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates empty diagnostic result.
|
||||
/// </summary>
|
||||
public ReaderDiagnosticResult()
|
||||
{
|
||||
Diagnostics = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates successful diagnostic result.
|
||||
/// </summary>
|
||||
/// <param name="message">
|
||||
/// Success message.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Successful result object.
|
||||
/// </returns>
|
||||
public static ReaderDiagnosticResult SuccessResult(
|
||||
string message = "OK")
|
||||
{
|
||||
return new ReaderDiagnosticResult
|
||||
{
|
||||
Success = true,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates failed diagnostic result.
|
||||
/// </summary>
|
||||
/// <param name="message">
|
||||
/// Failure message.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Failed result object.
|
||||
/// </returns>
|
||||
public static ReaderDiagnosticResult Failure(
|
||||
string message)
|
||||
{
|
||||
return new ReaderDiagnosticResult
|
||||
{
|
||||
Success = false,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts diagnostic information into display text.
|
||||
///
|
||||
/// Output contains:
|
||||
///
|
||||
/// diagnostic lines
|
||||
/// +
|
||||
/// summary message
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Formatted text representation.
|
||||
/// </returns>
|
||||
public string ToDisplayDiag()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (Diagnostics != null &&
|
||||
Diagnostics.Count > 0)
|
||||
{
|
||||
foreach (string line in Diagnostics)
|
||||
sb.AppendLine(line);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Message))
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Message))
|
||||
sb.AppendLine(Message);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,394 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads and searches CSV based data sources.
|
||||
///
|
||||
/// Supported query syntax:
|
||||
/// SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM
|
||||
///
|
||||
/// Example:
|
||||
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// Supports:
|
||||
/// - column names: [ColumnName]
|
||||
/// - column indexes: COLUMN(n)
|
||||
///
|
||||
/// Trace:
|
||||
/// MeterLoginPasswordReader
|
||||
/// -> CsvDataStorageReader.GetData()
|
||||
/// -> ConnectToSource()
|
||||
/// -> ExecuteQuery()
|
||||
/// </summary>
|
||||
public class CsvDataStorageReader : IDataStorageReader
|
||||
{
|
||||
private readonly DataStorageConfig config;
|
||||
|
||||
private string resolvedPath;
|
||||
private string[] loadedLines;
|
||||
private string[] loadedHeaders;
|
||||
|
||||
/// <summary>
|
||||
/// Creates CSV data storage reader using provided configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Data storage configuration loaded from gci_config.json.
|
||||
/// </param>
|
||||
public CsvDataStorageReader(DataStorageConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads values from CSV using configured QueryTemplate.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Query parameters used for lookup.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// ReaderDiagnosticResult containing lookup result.
|
||||
/// </returns>
|
||||
public object GetData(DataQuery query)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
ReaderDiagnosticResult sourceResult = ConnectToSource(true);
|
||||
if (!sourceResult.Success)
|
||||
throw new InvalidOperationException(sourceResult.Message);
|
||||
|
||||
ReaderDiagnosticResult queryResult = ExecuteQuery(query, true);
|
||||
if (!queryResult.Success)
|
||||
throw new InvalidOperationException(queryResult.Message);
|
||||
|
||||
return queryResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates CSV source accessibility and content.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result.
|
||||
/// </returns>
|
||||
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||
{
|
||||
return ConnectToSource(enableDiagnostics);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates configured QueryTemplate against CSV header.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result.
|
||||
/// </returns>
|
||||
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult sourceResult = ConnectToSource(enableDiagnostics);
|
||||
if (!sourceResult.Success)
|
||||
return sourceResult;
|
||||
|
||||
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
Log(result, enableDiagnostics, "Starting QueryTemplate validation.");
|
||||
|
||||
SearchOrderDefinition definition =
|
||||
SearchOrderParser.Parse(config.QueryTemplate);
|
||||
|
||||
int selectIndex = ResolveColumnIndex(
|
||||
loadedHeaders,
|
||||
definition.SelectColumn);
|
||||
|
||||
int whereIndex = ResolveColumnIndex(
|
||||
loadedHeaders,
|
||||
definition.WhereColumn);
|
||||
|
||||
Log(result, enableDiagnostics, "QueryTemplate parsed successfully.");
|
||||
Log(result, enableDiagnostics, "Select column index: " + selectIndex);
|
||||
Log(result, enableDiagnostics, "Where column index: " + whereIndex);
|
||||
|
||||
result.Success = true;
|
||||
result.Message = "QueryTemplate validation finished successfully.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = ex.Message;
|
||||
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens CSV file and loads its content.
|
||||
/// Validates file existence and header.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Source validation result.
|
||||
/// </returns>
|
||||
private ReaderDiagnosticResult ConnectToSource(bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
Log(result, enableDiagnostics, "Starting CSV source connection test.");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(config.DataSource))
|
||||
throw new InvalidOperationException("CSV data source is empty.");
|
||||
|
||||
resolvedPath = Path.GetFullPath(config.DataSource);
|
||||
|
||||
Log(result, enableDiagnostics, "Resolved full path: " + resolvedPath);
|
||||
|
||||
if (!File.Exists(resolvedPath))
|
||||
throw new FileNotFoundException("CSV file was not found.", resolvedPath);
|
||||
|
||||
loadedLines = File.ReadAllLines(resolvedPath);
|
||||
|
||||
if (loadedLines == null || loadedLines.Length == 0)
|
||||
throw new InvalidOperationException("CSV file is empty.");
|
||||
|
||||
loadedHeaders = SplitCsvLine(loadedLines[0]);
|
||||
|
||||
if (loadedHeaders == null || loadedHeaders.Length == 0)
|
||||
throw new InvalidOperationException("CSV header is empty.");
|
||||
|
||||
Log(result, enableDiagnostics, "CSV line count: " + loadedLines.Length);
|
||||
Log(result, enableDiagnostics, "CSV header column count: " + loadedHeaders.Length);
|
||||
|
||||
result.Success = true;
|
||||
result.Message = "CSV source connection finished successfully.";
|
||||
result.Data = loadedLines;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = ex.Message;
|
||||
result.Data = null;
|
||||
|
||||
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes configured QueryTemplate against loaded CSV data.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Query values used for matching.
|
||||
/// </param>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Query result.
|
||||
/// </returns>
|
||||
private ReaderDiagnosticResult ExecuteQuery(
|
||||
DataQuery query,
|
||||
bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
if (query.QueryParams == null || query.QueryParams.Count == 0)
|
||||
throw new InvalidOperationException("DataQuery.QueryParams is empty.");
|
||||
|
||||
SearchOrderDefinition definition =
|
||||
SearchOrderParser.Parse(config.QueryTemplate);
|
||||
|
||||
int selectIndex = ResolveColumnIndex(
|
||||
loadedHeaders,
|
||||
definition.SelectColumn);
|
||||
|
||||
int whereIndex = ResolveColumnIndex(
|
||||
loadedHeaders,
|
||||
definition.WhereColumn);
|
||||
|
||||
List<string> matchedValues = new List<string>();
|
||||
|
||||
foreach (string param in query.QueryParams)
|
||||
{
|
||||
string queryValue = (param ?? string.Empty).Trim();
|
||||
bool found = false;
|
||||
|
||||
for (int lineIndex = 1; lineIndex < loadedLines.Length; lineIndex++)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(loadedLines[lineIndex]))
|
||||
continue;
|
||||
|
||||
string[] values = SplitCsvLine(loadedLines[lineIndex]);
|
||||
|
||||
if (whereIndex >= values.Length)
|
||||
continue;
|
||||
|
||||
string currentValue = (values[whereIndex] ?? string.Empty).Trim();
|
||||
|
||||
if (!string.Equals(
|
||||
currentValue,
|
||||
queryValue,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string returnValue =
|
||||
selectIndex < values.Length
|
||||
? values[selectIndex]
|
||||
: string.Empty;
|
||||
|
||||
matchedValues.Add(returnValue);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
Log(result, enableDiagnostics, "No match found for: " + queryValue);
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
|
||||
if (query.QueryParams.Count == 1)
|
||||
{
|
||||
result.Data = matchedValues.Count > 0 ? matchedValues[0] : null;
|
||||
result.Message = matchedValues.Count > 0
|
||||
? "Value found."
|
||||
: "No match found.";
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Data = matchedValues;
|
||||
result.Message =
|
||||
"Batch query finished. Matches found: " +
|
||||
matchedValues.Count +
|
||||
" of " +
|
||||
query.QueryParams.Count +
|
||||
".";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = ex.Message;
|
||||
result.Data = null;
|
||||
|
||||
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves column index either by name or by explicit index.
|
||||
/// </summary>
|
||||
/// <param name="headers">
|
||||
/// CSV header columns.
|
||||
/// </param>
|
||||
/// <param name="columnReference">
|
||||
/// Parsed QueryTemplate column definition.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Zero-based column index.
|
||||
/// </returns>
|
||||
private int ResolveColumnIndex(
|
||||
string[] headers,
|
||||
ColumnReference columnReference)
|
||||
{
|
||||
if (columnReference == null)
|
||||
throw new InvalidOperationException("Column reference is null.");
|
||||
|
||||
if (columnReference.HasIndex)
|
||||
{
|
||||
int index = columnReference.Index.Value;
|
||||
|
||||
if (index < 0 || index >= headers.Length)
|
||||
throw new InvalidOperationException("Column index is out of range.");
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
if (columnReference.HasName)
|
||||
{
|
||||
for (int i = 0; i < headers.Length; i++)
|
||||
{
|
||||
if (string.Equals(
|
||||
headers[i]?.Trim(),
|
||||
columnReference.Name?.Trim(),
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
"Column '" + columnReference.Name + "' was not found in CSV header.");
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Column reference is not defined.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits CSV line using common separators.
|
||||
/// Supports:
|
||||
/// ';'
|
||||
/// ','
|
||||
/// '\t'
|
||||
/// </summary>
|
||||
/// <param name="line">
|
||||
/// Input CSV line.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Parsed columns.
|
||||
/// </returns>
|
||||
private string[] SplitCsvLine(string line)
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
return new string[0];
|
||||
|
||||
if (line.Contains(";"))
|
||||
return line.Split(';');
|
||||
|
||||
if (line.Contains(","))
|
||||
return line.Split(',');
|
||||
|
||||
if (line.Contains("\t"))
|
||||
return line.Split('\t');
|
||||
|
||||
return new[] { line };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds diagnostic line when diagnostics are enabled.
|
||||
/// </summary>
|
||||
private void Log(
|
||||
ReaderDiagnosticResult result,
|
||||
bool enableDiagnostics,
|
||||
string message)
|
||||
{
|
||||
if (!enableDiagnostics || result == null)
|
||||
return;
|
||||
|
||||
result.Diagnostics.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,397 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads data from SQL Server based data storage.
|
||||
///
|
||||
/// The reader uses configuration loaded from gci_config.json.
|
||||
///
|
||||
/// QueryTemplate must contain QUERYPARAM placeholder.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SELECT [Password]
|
||||
/// FROM [dbo].[SkeletonKeys]
|
||||
/// WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// or
|
||||
///
|
||||
/// SELECT [ParameterName], [ParameterValue]
|
||||
/// FROM [dbo].[PreAdjustmentCalibrationParams]
|
||||
/// WHERE [Dn_InternalId] = QUERYPARAM
|
||||
///
|
||||
/// The placeholder is internally converted
|
||||
/// to SQL parameter @value.
|
||||
///
|
||||
/// Supports both:
|
||||
///
|
||||
/// - Single-row lookups
|
||||
/// - Multi-row result sets
|
||||
/// </summary>
|
||||
public class DatabaseDataStorageReader : IDataStorageReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Data storage configuration containing
|
||||
/// connection string and query template.
|
||||
/// </summary>
|
||||
private readonly DataStorageConfig config;
|
||||
|
||||
/// <summary>
|
||||
/// Creates SQL Server data storage reader
|
||||
/// using provided configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Data storage configuration loaded
|
||||
/// from gci_config.json.
|
||||
/// </param>
|
||||
public DatabaseDataStorageReader(
|
||||
DataStorageConfig config)
|
||||
{
|
||||
this.config =
|
||||
config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes configured SQL query and returns
|
||||
/// matching database records.
|
||||
///
|
||||
/// Single-row queries populate:
|
||||
/// DatabaseSearchResult.Values
|
||||
///
|
||||
/// Multi-row queries populate:
|
||||
/// DatabaseSearchResult.Rows
|
||||
///
|
||||
/// For backward compatibility, the first row
|
||||
/// is also stored in Values.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Query object containing lookup parameter.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// DatabaseSearchResult containing returned
|
||||
/// database records.
|
||||
///
|
||||
/// Values contains the first returned row.
|
||||
///
|
||||
/// Rows contains the complete result set.
|
||||
/// </returns>
|
||||
public object GetData(DataQuery query)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
ReaderDiagnosticResult sourceResult =
|
||||
TestSource(true);
|
||||
|
||||
if (!sourceResult.Success)
|
||||
throw new InvalidOperationException(sourceResult.Message);
|
||||
|
||||
string sqlText =
|
||||
PrepareSqlText(config.QueryTemplate);
|
||||
|
||||
object queryValue =
|
||||
ExtractQueryValue(query);
|
||||
|
||||
using (SqlConnection connection =
|
||||
new SqlConnection(config.DataSource))
|
||||
|
||||
using (SqlCommand command =
|
||||
new SqlCommand(sqlText, connection))
|
||||
{
|
||||
AddQueryParameter(command, queryValue);
|
||||
|
||||
connection.Open();
|
||||
|
||||
// Full result set is required because some
|
||||
// storage definitions return multiple records
|
||||
// (e.g. PreAdjustmentCalibrationParams).
|
||||
using (SqlDataReader reader =
|
||||
command.ExecuteReader())
|
||||
{
|
||||
DatabaseSearchResult result =
|
||||
new DatabaseSearchResult
|
||||
{
|
||||
Query = sqlText
|
||||
};
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Found = true;
|
||||
|
||||
// Represents one database row.
|
||||
Dictionary<string, object> row =
|
||||
new Dictionary<string, object>();
|
||||
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
object value =
|
||||
reader.GetValue(i);
|
||||
|
||||
object normalizedValue =
|
||||
value == DBNull.Value
|
||||
? null
|
||||
: value;
|
||||
|
||||
row[reader.GetName(i)] =
|
||||
normalizedValue;
|
||||
}
|
||||
|
||||
// Preserve first row for legacy consumers
|
||||
// expecting a single returned database record
|
||||
// (e.g. MeterLoginPasswordReader).
|
||||
if (result.Rows.Count == 0)
|
||||
{
|
||||
foreach (var item in row)
|
||||
{
|
||||
result.Values[item.Key] =
|
||||
item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Store complete database result set.
|
||||
result.Rows.Add(row);
|
||||
}
|
||||
|
||||
if (result.Rows.Count == 0)
|
||||
{
|
||||
result.Found = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether SQL Server connection
|
||||
/// can be opened.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result of SQL connection test.
|
||||
/// </returns>
|
||||
public ReaderDiagnosticResult TestSource(
|
||||
bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult result =
|
||||
new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.DataSource))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Data source is empty.");
|
||||
}
|
||||
|
||||
Log(
|
||||
result,
|
||||
enableDiagnostics,
|
||||
"Opening SQL connection.");
|
||||
|
||||
using (SqlConnection connection =
|
||||
new SqlConnection(config.DataSource))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
Log(
|
||||
result,
|
||||
enableDiagnostics,
|
||||
"Connection opened successfully.");
|
||||
|
||||
using (SqlCommand command =
|
||||
new SqlCommand("SELECT 1", connection))
|
||||
{
|
||||
object value =
|
||||
command.ExecuteScalar();
|
||||
|
||||
Log(
|
||||
result,
|
||||
enableDiagnostics,
|
||||
"Test query result: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
result.Message =
|
||||
"Connection to SQL Server OK.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message =
|
||||
"Failed to connect to SQL Server. " + ex.Message;
|
||||
|
||||
Log(
|
||||
result,
|
||||
enableDiagnostics,
|
||||
ex.ToString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether configured SQL query
|
||||
/// can be prepared and executed.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result of query execution test.
|
||||
/// </returns>
|
||||
public ReaderDiagnosticResult TestQuery(
|
||||
bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult result =
|
||||
new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.QueryTemplate))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Query template is empty.");
|
||||
}
|
||||
|
||||
string sqlText =
|
||||
PrepareSqlText(config.QueryTemplate);
|
||||
|
||||
Log(result, enableDiagnostics, "Original template:");
|
||||
Log(result, enableDiagnostics, config.QueryTemplate);
|
||||
|
||||
Log(result, enableDiagnostics, "Prepared SQL:");
|
||||
Log(result, enableDiagnostics, sqlText);
|
||||
|
||||
using (SqlConnection connection =
|
||||
new SqlConnection(config.DataSource))
|
||||
|
||||
using (SqlCommand command =
|
||||
new SqlCommand(sqlText, connection))
|
||||
{
|
||||
AddQueryParameter(command, "TEST");
|
||||
|
||||
connection.Open();
|
||||
|
||||
object value =
|
||||
command.ExecuteScalar();
|
||||
|
||||
result.Data = value;
|
||||
|
||||
Log(
|
||||
result,
|
||||
enableDiagnostics,
|
||||
"Query executed successfully.");
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
result.Message =
|
||||
"Query executed successfully.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message =
|
||||
"Query execution failed. " + ex.Message;
|
||||
|
||||
Log(
|
||||
result,
|
||||
enableDiagnostics,
|
||||
ex.ToString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts configured QueryTemplate
|
||||
/// to executable SQL text.
|
||||
/// </summary>
|
||||
/// <param name="queryTemplate">
|
||||
/// SQL query template containing
|
||||
/// QUERYPARAM placeholder.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// SQL text with QUERYPARAM replaced
|
||||
/// by @value parameter.
|
||||
/// </returns>
|
||||
private static string PrepareSqlText(
|
||||
string queryTemplate)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(queryTemplate))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Query template must not be empty.",
|
||||
nameof(queryTemplate));
|
||||
}
|
||||
|
||||
if (!queryTemplate.Contains("QUERYPARAM"))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Query template must contain QUERYPARAM placeholder.");
|
||||
}
|
||||
|
||||
return queryTemplate.Replace(
|
||||
"QUERYPARAM",
|
||||
"@value");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts first query parameter value.
|
||||
/// </summary>
|
||||
private static object ExtractQueryValue(
|
||||
DataQuery query)
|
||||
{
|
||||
if (query.QueryParams == null ||
|
||||
query.QueryParams.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"DataQuery does not contain any query parameter.");
|
||||
}
|
||||
|
||||
return query.QueryParams[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds lookup parameter to SQL command.
|
||||
/// </summary>
|
||||
private static void AddQueryParameter(
|
||||
SqlCommand command,
|
||||
object value)
|
||||
{
|
||||
command.Parameters.Clear();
|
||||
|
||||
SqlParameter parameter =
|
||||
command.Parameters.Add(
|
||||
"@value",
|
||||
SqlDbType.Variant);
|
||||
|
||||
parameter.Value =
|
||||
value ?? DBNull.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds diagnostic line when diagnostics
|
||||
/// are enabled.
|
||||
/// </summary>
|
||||
private static void Log(
|
||||
ReaderDiagnosticResult result,
|
||||
bool enableDiagnostics,
|
||||
string message)
|
||||
{
|
||||
if (!enableDiagnostics || result == null)
|
||||
return;
|
||||
|
||||
result.Diagnostics.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||
{
|
||||
public class JsonDataStorageReader : IDataStorageReader
|
||||
{
|
||||
private readonly DataStorageConfig config;
|
||||
|
||||
public JsonDataStorageReader(DataStorageConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
public object GetData(DataQuery query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||
{
|
||||
public class RestApiDataStorageReader : IDataStorageReader
|
||||
{
|
||||
private readonly DataStorageConfig config;
|
||||
|
||||
public RestApiDataStorageReader(DataStorageConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
public object GetData(DataQuery query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
using System;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching
|
||||
{
|
||||
/// <summary>
|
||||
/// Parsed representation of QueryTemplate expression.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// becomes:
|
||||
///
|
||||
/// SelectColumn:
|
||||
/// [Password]
|
||||
///
|
||||
/// WhereColumn:
|
||||
/// [PcbId]
|
||||
/// </summary>
|
||||
public class SearchOrderDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Column to be returned from lookup result.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SELECT [Password]
|
||||
///
|
||||
/// returns:
|
||||
///
|
||||
/// [Password]
|
||||
/// </summary>
|
||||
public ColumnReference SelectColumn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Column used for row matching.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// uses:
|
||||
///
|
||||
/// [PcbId]
|
||||
/// </summary>
|
||||
public ColumnReference WhereColumn { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents one column definition inside QueryTemplate.
|
||||
///
|
||||
/// Supports two forms:
|
||||
///
|
||||
/// [ColumnName]
|
||||
///
|
||||
/// or:
|
||||
///
|
||||
/// COLUMN(number)
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// [Password]
|
||||
///
|
||||
/// COLUMN(2)
|
||||
/// </summary>
|
||||
public class ColumnReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Column name used in named lookup mode.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// [Password]
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Zero-based column index used in indexed lookup mode.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// COLUMN(2)
|
||||
/// </summary>
|
||||
public int? Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether column uses name-based lookup.
|
||||
/// </summary>
|
||||
public bool HasName
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether column uses index-based lookup.
|
||||
/// </summary>
|
||||
public bool HasIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return Index.HasValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns human readable representation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Column expression text.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (HasName)
|
||||
return "[" + Name + "]";
|
||||
|
||||
if (HasIndex)
|
||||
return "COLUMN(" + Index.Value + ")";
|
||||
|
||||
return "<undefined column reference>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses SQL-like QueryTemplate expressions used by GCI data storage readers.
|
||||
///
|
||||
/// Supported syntax:
|
||||
///
|
||||
/// SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// SELECT COLUMN(1) WHERE COLUMN(0) = QUERYPARAM
|
||||
///
|
||||
/// Mixed forms are also supported:
|
||||
///
|
||||
/// SELECT [Password] WHERE COLUMN(0)=QUERYPARAM
|
||||
///
|
||||
/// The parser converts text expressions into structured
|
||||
/// SearchOrderDefinition objects which are later consumed
|
||||
/// by CSV and other readers.
|
||||
/// </summary>
|
||||
public static class SearchOrderParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Full QueryTemplate validation pattern.
|
||||
///
|
||||
/// Expected syntax:
|
||||
///
|
||||
/// SELECT [Column] WHERE [Column] = QUERYPARAM
|
||||
/// </summary>
|
||||
private static readonly Regex FullPattern = new Regex(
|
||||
@"^\s*SELECT\s+(?<select>\[[^\]]+\]|COLUMN\(\d+\))\s+WHERE\s+(?<where>\[[^\]]+\]|COLUMN\(\d+\))\s*=\s*QUERYPARAM\s*$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Matches:
|
||||
///
|
||||
/// [ColumnName]
|
||||
/// </summary>
|
||||
private static readonly Regex NamedColumnPattern = new Regex(
|
||||
@"^\[(?<name>[^\]]+)\]$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Matches:
|
||||
///
|
||||
/// COLUMN(number)
|
||||
/// </summary>
|
||||
private static readonly Regex IndexedColumnPattern = new Regex(
|
||||
@"^COLUMN\((?<index>\d+)\)$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Parses QueryTemplate text into structured definition.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// Result:
|
||||
///
|
||||
/// SelectColumn:
|
||||
/// Password
|
||||
///
|
||||
/// WhereColumn:
|
||||
/// PcbId
|
||||
/// </summary>
|
||||
/// <param name="searchOrder">
|
||||
/// QueryTemplate expression.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Parsed query definition.
|
||||
/// </returns>
|
||||
public static SearchOrderDefinition Parse(string searchOrder)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(searchOrder))
|
||||
throw new InvalidOperationException(
|
||||
"QueryTemplate is empty.");
|
||||
|
||||
Match match = FullPattern.Match(searchOrder);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Invalid QueryTemplate syntax. Expected: SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM");
|
||||
}
|
||||
|
||||
return new SearchOrderDefinition
|
||||
{
|
||||
SelectColumn =
|
||||
ParseColumnReference(
|
||||
match.Groups["select"].Value),
|
||||
|
||||
WhereColumn =
|
||||
ParseColumnReference(
|
||||
match.Groups["where"].Value)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses single column definition.
|
||||
///
|
||||
/// Supported forms:
|
||||
///
|
||||
/// [ColumnName]
|
||||
///
|
||||
/// COLUMN(number)
|
||||
/// </summary>
|
||||
/// <param name="token">
|
||||
/// Raw token extracted from QueryTemplate.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Parsed column reference.
|
||||
/// </returns>
|
||||
private static ColumnReference ParseColumnReference(
|
||||
string token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
throw new InvalidOperationException(
|
||||
"Column reference token is empty.");
|
||||
|
||||
Match nameMatch =
|
||||
NamedColumnPattern.Match(token);
|
||||
|
||||
if (nameMatch.Success)
|
||||
{
|
||||
return new ColumnReference
|
||||
{
|
||||
Name =
|
||||
nameMatch
|
||||
.Groups["name"]
|
||||
.Value
|
||||
.Trim(),
|
||||
|
||||
Index = null
|
||||
};
|
||||
}
|
||||
|
||||
Match indexMatch =
|
||||
IndexedColumnPattern.Match(token);
|
||||
|
||||
if (indexMatch.Success)
|
||||
{
|
||||
return new ColumnReference
|
||||
{
|
||||
Name = null,
|
||||
|
||||
Index =
|
||||
int.Parse(
|
||||
indexMatch
|
||||
.Groups["index"]
|
||||
.Value)
|
||||
};
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
"Invalid column reference '" +
|
||||
token +
|
||||
"'. Use [ColumnName] or COLUMN(number).");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines contract for reading meter login passwords
|
||||
/// from configured GCI data storage.
|
||||
///
|
||||
/// Implementations hide storage details and provide
|
||||
/// a unified API for password lookup.
|
||||
///
|
||||
/// Supported storage types:
|
||||
/// - SQL database
|
||||
/// - CSV
|
||||
/// - JSON
|
||||
/// - REST API
|
||||
/// </summary>
|
||||
public interface IMeterLoginPasswordReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads meter login password synchronously.
|
||||
/// </summary>
|
||||
/// <param name="queryParam">
|
||||
/// PCB ID or another configured lookup value.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Password if found; otherwise null.
|
||||
/// </returns>
|
||||
string GetPassword(DataQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Reads meter login password asynchronously.
|
||||
/// Thread-safe implementation may serialize
|
||||
/// access to the underlying storage.
|
||||
/// </summary>
|
||||
/// <param name="queryParam">
|
||||
/// PCB ID or another configured lookup value.
|
||||
/// </param>
|
||||
/// <param name="token">
|
||||
/// Cancellation token.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Password if found; otherwise null.
|
||||
/// </returns>
|
||||
Task<string> ReadMeterLoginPasswordAsync(
|
||||
DataQuery query,
|
||||
CancellationToken token = default);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads meter login password from configured GCI data storage.
|
||||
/// </summary>
|
||||
public class MeterLoginPasswordReader : IMeterLoginPasswordReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Universal data storage reader selected by configuration.
|
||||
/// Can represent database, CSV, JSON or REST reader.
|
||||
/// </summary>
|
||||
private readonly IDataStorageReader reader;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that only one password lookup is executed at a time.
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim readLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Creates password reader using provided data storage configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">Data storage configuration loaded from gci_config.json.</param>
|
||||
public MeterLoginPasswordReader(DataStorageConfig config)
|
||||
{
|
||||
reader = DataStorageReaderFactory.Create(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously reads meter login password using
|
||||
/// the provided data query.
|
||||
///
|
||||
/// Thread-safe implementation may serialize
|
||||
/// access to the underlying storage.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing lookup parameter values.
|
||||
/// </param>
|
||||
/// <param name="token">
|
||||
/// Cancellation token.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Password if found; otherwise null.
|
||||
/// </returns>
|
||||
public async Task<string> ReadMeterLoginPasswordAsync(
|
||||
DataQuery query,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
if (query.QueryParams.Count == 0)
|
||||
throw new ArgumentException(
|
||||
"Query does not contain any parameter.",
|
||||
nameof(query));
|
||||
|
||||
await readLock.WaitAsync(token);
|
||||
|
||||
try
|
||||
{
|
||||
return await Task.Run(
|
||||
() => GetPassword(query),
|
||||
token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
readLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads meter login password using
|
||||
/// the provided data query.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing lookup parameter values.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Password if found; otherwise null.
|
||||
/// </returns>
|
||||
public string GetPassword(
|
||||
DataQuery query)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
if (query.QueryParams.Count == 0)
|
||||
throw new ArgumentException(
|
||||
"Query does not contain any parameter.",
|
||||
nameof(query));
|
||||
|
||||
object result =
|
||||
reader.GetData(query);
|
||||
|
||||
if (result is ReaderDiagnosticResult diagnosticResult)
|
||||
{
|
||||
return diagnosticResult.Data?.ToString();
|
||||
}
|
||||
|
||||
if (result is DatabaseSearchResult dbResult)
|
||||
{
|
||||
if (!dbResult.Found ||
|
||||
dbResult.Values == null ||
|
||||
dbResult.Values.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (object value in dbResult.Values.Values)
|
||||
{
|
||||
return value?.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return result?.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to pre-adjustment calibration parameters
|
||||
/// stored in the configured GCI data source.
|
||||
///
|
||||
/// Expected structure:
|
||||
///
|
||||
/// MeterSize | ParameterName | ParameterValue
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// 2 | GENESISFLOW_MinValidToF | 21990232
|
||||
/// 2 | GENESISFLOW_Timeout | 1
|
||||
///
|
||||
/// Parameters are grouped by MeterSize and returned
|
||||
/// as key/value pairs where:
|
||||
///
|
||||
/// Key = ParameterName
|
||||
/// Value = ParameterValue
|
||||
/// </summary>
|
||||
public interface IPreAdjustmentCalibrationParamsReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads all pre-adjustment calibration parameters
|
||||
/// using the provided data query synchronously.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing meter size as lookup parameter.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Dictionary where key is GENESISFLOW parameter name
|
||||
/// and value is stored parameter value.
|
||||
/// </returns>
|
||||
Dictionary<string, UInt32> GetCalibrationParams(
|
||||
DataQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Reads all pre-adjustment calibration parameters
|
||||
/// using the provided data query asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing meter size as lookup parameter.
|
||||
/// </param>
|
||||
/// <param name="token">
|
||||
/// Cancellation token.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Dictionary where key is GENESISFLOW parameter name
|
||||
/// and value is stored parameter value.
|
||||
/// </returns>
|
||||
Task<Dictionary<string, UInt32>> ReadCalibrationParamsAsync(
|
||||
DataQuery query,
|
||||
CancellationToken token = default);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,305 @@
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads pre-adjustment calibration parameters
|
||||
/// from configured GCI data storage.
|
||||
///
|
||||
/// The underlying storage type is selected
|
||||
/// through IDataStorageReader and may represent:
|
||||
///
|
||||
/// - SQL database
|
||||
/// - CSV
|
||||
/// - JSON
|
||||
/// - REST API
|
||||
///
|
||||
/// Expected storage structure:
|
||||
///
|
||||
/// MeterSize | ParameterName | ParameterValue
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// 2 | GENESISFLOW_MinValidToF | 21990232
|
||||
/// 2 | GENESISFLOW_Timeout | 1
|
||||
///
|
||||
/// Returned data are represented as:
|
||||
///
|
||||
/// Key = ParameterName
|
||||
/// Value = ParameterValue
|
||||
/// </summary>
|
||||
public class PreAdjustmentCalibrationParamsReader
|
||||
: IPreAdjustmentCalibrationParamsReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Universal data storage reader selected by configuration.
|
||||
/// Can represent database, CSV, JSON or REST reader.
|
||||
/// </summary>
|
||||
private readonly IDataStorageReader reader;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that only one calibration parameter lookup
|
||||
/// is executed at a time.
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim readLock =
|
||||
new SemaphoreSlim(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Creates calibration parameter reader using
|
||||
/// provided data storage configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Data storage configuration loaded from gci_config.json.
|
||||
/// </param>
|
||||
public PreAdjustmentCalibrationParamsReader(
|
||||
DataStorageConfig config)
|
||||
{
|
||||
if (config == null)
|
||||
throw new ArgumentNullException(nameof(config));
|
||||
|
||||
reader = DataStorageReaderFactory.Create(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all pre-adjustment calibration parameters
|
||||
/// using the provided data query asynchronously.
|
||||
///
|
||||
/// The query is expected to contain meter size
|
||||
/// as the first lookup parameter.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing lookup parameter values.
|
||||
/// </param>
|
||||
/// <param name="token">
|
||||
/// Cancellation token.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Dictionary:
|
||||
///
|
||||
/// Key:
|
||||
/// GENESISFLOW parameter name
|
||||
///
|
||||
/// Value:
|
||||
/// Stored parameter value
|
||||
/// </returns>
|
||||
public async Task<Dictionary<string, UInt32>> ReadCalibrationParamsAsync(
|
||||
DataQuery query,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
ValidateQuery(query);
|
||||
|
||||
return await Task.Run(
|
||||
() => GetCalibrationParams(query),
|
||||
token).ConfigureAwait(false); ;
|
||||
|
||||
/*await readLock.WaitAsync(token);
|
||||
|
||||
try
|
||||
{
|
||||
return await Task.Run(
|
||||
() => GetCalibrationParams(query),
|
||||
token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
readLock.Release();
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all pre-adjustment calibration parameters
|
||||
/// using the provided data query.
|
||||
///
|
||||
/// The query is expected to contain meter size
|
||||
/// as the first lookup parameter.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing lookup parameter values.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Dictionary:
|
||||
///
|
||||
/// Key:
|
||||
/// GENESISFLOW parameter name
|
||||
///
|
||||
/// Value:
|
||||
/// Stored parameter value
|
||||
/// </returns>
|
||||
public Dictionary<string, UInt32> GetCalibrationParams(
|
||||
DataQuery query)
|
||||
{
|
||||
ValidateQuery(query);
|
||||
|
||||
object result =
|
||||
reader.GetData(query);
|
||||
|
||||
return ConvertResultToDictionary(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that data query exists
|
||||
/// and contains at least one lookup parameter.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query to validate.
|
||||
/// </param>
|
||||
private static void ValidateQuery(
|
||||
DataQuery query)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
if (query.QueryParams == null ||
|
||||
query.QueryParams.Count == 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Query does not contain any parameter.",
|
||||
nameof(query));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts provider-specific result objects
|
||||
/// returned by IDataStorageReader into a unified
|
||||
/// dictionary representation.
|
||||
/// </summary>
|
||||
/// <param name="result">
|
||||
/// Raw result returned by the configured storage reader.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Dictionary containing calibration parameter
|
||||
/// name/value pairs.
|
||||
/// </returns>
|
||||
private Dictionary<string, UInt32> ConvertResultToDictionary(
|
||||
object result)
|
||||
{
|
||||
Dictionary<string, UInt32> values =
|
||||
new Dictionary<string, UInt32>();
|
||||
|
||||
if (result == null)
|
||||
return values;
|
||||
|
||||
if (result is DatabaseSearchResult dbResult)
|
||||
{
|
||||
if (!dbResult.Found)
|
||||
return values;
|
||||
|
||||
foreach (Dictionary<string, object> row in dbResult.Rows)
|
||||
{
|
||||
if (!row.TryGetValue("ParameterName", out object parameterNameObject))
|
||||
continue;
|
||||
|
||||
if (!row.TryGetValue("ParameterValue", out object parameterValueObject))
|
||||
continue;
|
||||
|
||||
string parameterName =
|
||||
parameterNameObject?.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(parameterName))
|
||||
continue;
|
||||
|
||||
if (TryConvertToUInt32(parameterValueObject, out UInt32 parameterValue))
|
||||
{
|
||||
values[parameterName] =
|
||||
parameterValue;
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
if (result is ReaderDiagnosticResult diagnosticResult)
|
||||
{
|
||||
if (diagnosticResult.Data is Dictionary<string, UInt32> uintDictionary)
|
||||
return uintDictionary;
|
||||
|
||||
if (diagnosticResult.Data is Dictionary<string, string> stringDictionary)
|
||||
{
|
||||
foreach (var item in stringDictionary)
|
||||
{
|
||||
if (TryConvertToUInt32(item.Value, out UInt32 value))
|
||||
values[item.Key] = value;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
if (diagnosticResult.Data is Dictionary<string, object> objectDictionary)
|
||||
{
|
||||
foreach (var item in objectDictionary)
|
||||
{
|
||||
if (TryConvertToUInt32(item.Value, out UInt32 value))
|
||||
values[item.Key] = value;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
if (result is Dictionary<string, UInt32> directUIntDictionary)
|
||||
return directUIntDictionary;
|
||||
|
||||
if (result is Dictionary<string, string> directStringDictionary)
|
||||
{
|
||||
foreach (var item in directStringDictionary)
|
||||
{
|
||||
if (TryConvertToUInt32(item.Value, out UInt32 value))
|
||||
values[item.Key] = value;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
if (result is Dictionary<string, object> directObjectDictionary)
|
||||
{
|
||||
foreach (var item in directObjectDictionary)
|
||||
{
|
||||
if (TryConvertToUInt32(item.Value, out UInt32 value))
|
||||
values[item.Key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static bool TryConvertToUInt32(
|
||||
object value,
|
||||
out UInt32 result)
|
||||
{
|
||||
result = 0;
|
||||
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
if (value is UInt32 uintValue)
|
||||
{
|
||||
result = uintValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is int intValue && intValue >= 0)
|
||||
{
|
||||
result = Convert.ToUInt32(intValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is long longValue &&
|
||||
longValue >= 0 &&
|
||||
longValue <= UInt32.MaxValue)
|
||||
{
|
||||
result = Convert.ToUInt32(longValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
return UInt32.TryParse(
|
||||
value.ToString(),
|
||||
out result);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
GenesisCordonelInterface/Core/Engine/Engine.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using GenesisCordonelInterface.API;
|
||||
using GenesisCordonelInterface.Core.Config;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using UdsReaderType_CalibrationParams = GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams.PreAdjustmentCalibrationParamsReader;
|
||||
using UdsReaderType_LoginPasswords = GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords.MeterLoginPasswordReader;
|
||||
using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
|
||||
using GciEnums = GenesisCordonelInterface.API.Enums;
|
||||
using GciType = GenesisCordonelInterface.API.InterfaceOutsideToGCI;
|
||||
using GciDataStorageReadingModels = GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GciGUIType = GenesisCordonelInterface.UI.MainView;
|
||||
|
||||
namespace GenesisCordonelInterface.Core
|
||||
{
|
||||
public class Engine
|
||||
{
|
||||
GciConfig gciConfig;
|
||||
|
||||
public UdsReaderType_LoginPasswords loginPasswordsDataStorageReader;
|
||||
public UdsReaderType_CalibrationParams calibrationParamsStorageReader;
|
||||
//readonly UdsWriterType writer;
|
||||
|
||||
//diag GUI for GCI
|
||||
GciGUIType gciGUI;
|
||||
Form gciGuiHostForm;
|
||||
|
||||
//diag GUI for GciBridge
|
||||
public UserControl gciBridgeGUIUserControl;
|
||||
public UI.MainForm gciBridgeGuiForm;
|
||||
|
||||
public InterfaceOutsideToGCI gciExternalInterface;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the public GCI facade.
|
||||
/// </summary>
|
||||
public Engine()
|
||||
{
|
||||
gciConfig = GciConfigLoader.LoadDefault();
|
||||
|
||||
loginPasswordsDataStorageReader = new UdsReaderType_LoginPasswords
|
||||
(
|
||||
new GciDataStorageReadingModels.DataStorageConfig
|
||||
(
|
||||
nameof(GciEnums.DataStorageReaderTypes.LoginPasswordsReader),
|
||||
GciDataStorageReadingModels.DataStorageType.LocalDatabase,
|
||||
gciConfig.DataStorageSection.MeterLoginPasswords.DataSource,
|
||||
gciConfig.DataStorageSection.MeterLoginPasswords.QueryTemplate
|
||||
)
|
||||
);
|
||||
calibrationParamsStorageReader = new UdsReaderType_CalibrationParams
|
||||
(
|
||||
new GciDataStorageReadingModels.DataStorageConfig
|
||||
(
|
||||
nameof(GciEnums.DataStorageReaderTypes.CalibrationParamsReader),
|
||||
GciDataStorageReadingModels.DataStorageType.LocalDatabase,
|
||||
gciConfig.DataStorageSection.PreAdjustmentCalibrationParams.DataSource,
|
||||
gciConfig.DataStorageSection.PreAdjustmentCalibrationParams.QueryTemplate
|
||||
)
|
||||
);
|
||||
|
||||
gciExternalInterface = new InterfaceOutsideToGCI(
|
||||
new InterfaceGCIToLaatzen(),
|
||||
loginPasswordsDataStorageReader,
|
||||
calibrationParamsStorageReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -173,6 +174,7 @@ namespace GenesisCordonelInterface.Core.Threading
|
||||
|
||||
public sealed class ApiWorker : IDisposable
|
||||
{
|
||||
private static readonly NLog.ILogger Logger = NLog.LogManager.GetLogger("GenesisCordonelInterface-threading");
|
||||
/// <summary>
|
||||
/// Thread-safe FIFO queue holding work items.
|
||||
/// </summary>
|
||||
@ -236,9 +238,9 @@ namespace GenesisCordonelInterface.Core.Threading
|
||||
/// Enqueues a function returning a value for sequential execution.
|
||||
/// </summary>
|
||||
public Task<T> RunAsync<T>(
|
||||
Func<T> action,
|
||||
CancellationToken token = default(CancellationToken),
|
||||
string operationName = null)
|
||||
Func<T> action,
|
||||
CancellationToken token = default,
|
||||
string operationName = null)
|
||||
{
|
||||
if (action == null)
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
@ -246,58 +248,52 @@ namespace GenesisCordonelInterface.Core.Threading
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException(nameof(ApiWorker));
|
||||
|
||||
var tcs = new TaskCompletionSource<T>();
|
||||
var tcs = new TaskCompletionSource<T>(
|
||||
TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
queue.Add(() =>
|
||||
try
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
Debug.WriteLine($"ENQUEUE {operationName} slot worker={Name} time={DateTime.Now:HH:mm:ss.fff}");
|
||||
queue.Add(() =>
|
||||
{
|
||||
tcs.TrySetCanceled();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
queue.Add(() =>
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
tcs.TrySetCanceled();
|
||||
return;
|
||||
}
|
||||
tcs.TrySetCanceled();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
CurrentOperation = operationName ?? action.Method.Name;
|
||||
LastActivity = DateTime.Now;
|
||||
LastError = null;
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
CurrentOperation = operationName ?? action.Method.Name;
|
||||
LastActivity = DateTime.Now;
|
||||
LastError = null;
|
||||
|
||||
var result = action();
|
||||
tcs.TrySetResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LastError = ex.Message;
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
CurrentOperation = null;
|
||||
LastActivity = DateTime.Now;
|
||||
}
|
||||
}, token);
|
||||
}
|
||||
catch (ObjectDisposedException ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
tcs.TrySetException(new ObjectDisposedException(nameof(ApiWorker), ex));
|
||||
}
|
||||
}, token);
|
||||
var result = action();
|
||||
|
||||
tcs.TrySetResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LastError = ex.Message;
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
CurrentOperation = null;
|
||||
LastActivity = DateTime.Now;
|
||||
}
|
||||
}, token);
|
||||
}
|
||||
catch (ObjectDisposedException ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
tcs.TrySetException(new ObjectDisposedException(nameof(ApiWorker), ex));
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.Threading
|
||||
{
|
||||
public sealed class RetryResult<T>
|
||||
{
|
||||
public T Result { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public int Attempts { get; set; }
|
||||
public TimeSpan Duration { get; set; }
|
||||
public bool TimedOut { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"Success={0}, Attempts={1}, Duration={2} ms, TimedOut={3}, Result={4}",
|
||||
Success,
|
||||
Attempts,
|
||||
(int)Duration.TotalMilliseconds,
|
||||
TimedOut,
|
||||
Result == null ? "<null>" : Result.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public static class RetryWorker
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes an asynchronous operation with retry and timeout protection.
|
||||
///
|
||||
/// Important:
|
||||
/// The timeout here protects the caller from waiting forever, but it does not forcibly abort
|
||||
/// the underlying hardware operation. Because hardware/COM calls may continue running after
|
||||
/// the timeout signal, this method waits for the original action to finish before starting
|
||||
/// the next retry. This prevents overlapping COM requests on the same device.
|
||||
/// </summary>
|
||||
public static async Task<RetryResult<T>> RunWithRetryAsync<T>(
|
||||
Func<Task<T>> action,
|
||||
Func<T, bool> isSuccess,
|
||||
Action<string> log,
|
||||
Action<string, T> logResult,
|
||||
string operationName,
|
||||
int maxAttempts = 3,
|
||||
int delayMs = 500,
|
||||
int timeoutMs = 30000)
|
||||
{
|
||||
var started = DateTime.Now;
|
||||
T lastResult = default(T);
|
||||
bool timedOut = false;
|
||||
|
||||
for (int attempt = 1; attempt <= maxAttempts; attempt++)
|
||||
{
|
||||
Task<T> actionTask = null;
|
||||
|
||||
try
|
||||
{
|
||||
log?.Invoke($"{operationName} started. Attempt {attempt}/{maxAttempts}");
|
||||
|
||||
// Start real operation, for example Connect/GetPcbId/Login.
|
||||
actionTask = action();
|
||||
|
||||
// Start independent timeout timer.
|
||||
var timeoutTask = Task.Delay(timeoutMs);
|
||||
|
||||
// Wait until either operation completes or timeout expires.
|
||||
var completedTask = await Task.WhenAny(actionTask, timeoutTask)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (completedTask == timeoutTask)
|
||||
{
|
||||
timedOut = true;
|
||||
|
||||
log?.Invoke(
|
||||
$"{operationName} timed out. Attempt {attempt}/{maxAttempts}. " +
|
||||
"Waiting for the running operation to finish before retry.");
|
||||
|
||||
// Critical part:
|
||||
// Do NOT immediately start another retry.
|
||||
// The hardware operation may still be active in ApiWorker.
|
||||
// Starting another attempt immediately could corrupt communication.
|
||||
try
|
||||
{
|
||||
lastResult = await actionTask.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log?.Invoke(
|
||||
$"{operationName} finished after timeout with exception: " +
|
||||
$"{ex.GetType().Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Operation completed before timeout.
|
||||
lastResult = await actionTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Decide whether the returned result means success.
|
||||
if (lastResult != null && isSuccess(lastResult))
|
||||
{
|
||||
return new RetryResult<T>
|
||||
{
|
||||
Result = lastResult,
|
||||
Success = true,
|
||||
Attempts = attempt,
|
||||
Duration = DateTime.Now - started,
|
||||
TimedOut = timedOut
|
||||
};
|
||||
}
|
||||
|
||||
logResult?.Invoke(
|
||||
$"{operationName} failed. Attempt {attempt}/{maxAttempts}",
|
||||
lastResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Covers exceptions thrown before timeout handling or by action startup.
|
||||
log?.Invoke(
|
||||
$"{operationName} exception on attempt {attempt}/{maxAttempts}: " +
|
||||
$"{ex.GetType().Name}: {ex.Message}");
|
||||
}
|
||||
|
||||
// Delay before next retry, except after the final attempt.
|
||||
if (attempt < maxAttempts)
|
||||
{
|
||||
log?.Invoke($"{operationName} waiting {delayMs} ms before retry.");
|
||||
|
||||
await Task.Delay(delayMs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return new RetryResult<T>
|
||||
{
|
||||
Result = lastResult,
|
||||
Success = false,
|
||||
Attempts = maxAttempts,
|
||||
Duration = DateTime.Now - started,
|
||||
TimedOut = timedOut
|
||||
};
|
||||
}
|
||||
|
||||
public static void EnsureSuccess<T>(
|
||||
RetryResult<T> retryResult,
|
||||
string operationName)
|
||||
{
|
||||
if (!retryResult.Success)
|
||||
throw new Exception($"{operationName} failed after {retryResult.Attempts} attempts.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,9 +120,32 @@
|
||||
<Compile Include="API\InterfaceOutsideToGCI.cs" />
|
||||
<Compile Include="API\InterfaceGCIToLaatzen.cs" />
|
||||
<Compile Include="API\PublicModels.cs" />
|
||||
<Compile Include="Core\Config\GciConfig.cs" />
|
||||
<Compile Include="Core\Config\GciConfigLoader.cs" />
|
||||
<Compile Include="Core\DataStorage\Config\GciDataStorageConfig.cs" />
|
||||
<Compile Include="API\Enums.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Providers\CsvDataStorageReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Providers\DatabaseDataStorageReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\DatabaseSearchResult.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\DataQuery.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\DataStorageConfig.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\DataStorageReaderFactory.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\DataStorageType.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Contracts\IDataStorageReader..cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Providers\JsonDataStorageReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\ReaderDiagnosticResult.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Providers\RestApiDataStorageReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Searching\SearchOrderDefinition.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Searching\SearchOrderParser.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Implementation\MeterLoginPasswords\IMeterLoginPasswordReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Implementation\MeterLoginPasswords\MeterLoginPasswordReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Implementation\PreAdjustmentCalibrationParams\IPreAdjustmentCalibrationParamsReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Implementation\PreAdjustmentCalibrationParams\PreAdjustmentCalibrationParamsReader.cs" />
|
||||
<Compile Include="Core\Engine\Engine.cs" />
|
||||
<Compile Include="Core\Logging\UiLogBus.cs" />
|
||||
<Compile Include="Core\Logging\UiTarget.cs" />
|
||||
<Compile Include="Core\Threading\ApiWorker\ApiWorker.cs" />
|
||||
<Compile Include="Core\Threading\RetryWorker\RetryWorker.cs" />
|
||||
<Compile Include="UI\Debug\MeterBatchConfigPanel.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
@ -201,6 +224,17 @@
|
||||
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MetersActionView.Designer.cs">
|
||||
<DependentUpon>MetersActionView.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Content Include="docs\images\GciBridge_component_GUI.png" />
|
||||
<Content Include="docs\images\GCI__Onboarding_Overview_drawio.svg" />
|
||||
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__API_architecture__Current_state.svg" />
|
||||
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__API_architecture__Target_state.svg" />
|
||||
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__Data_collection_domain.svg" />
|
||||
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__Meter_management_domain.svg" />
|
||||
<Content Include="docs\images\logo\logo.png" />
|
||||
<Content Include="docs\images\logo\logo.svg" />
|
||||
<Content Include="docs\images\logo\sensus-logo-white-green-rgb.png" />
|
||||
<Content Include="docs\images\logo\sensus-logo-white-green-rgb.svg" />
|
||||
<Content Include="docs\styles\main.css" />
|
||||
<Content Include="RuntimePackage\Build\Copy.targets.xml" />
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
@ -228,6 +262,38 @@
|
||||
<EmbeddedResource Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.resx">
|
||||
<DependentUpon>FrmGCIAPI.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<Content Include="Core\Config\gci_config.json">
|
||||
<Link>Config\gci_config.json</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="Core\Config\gci_config.json" />
|
||||
<None Include="docs\articles\API\index.md" />
|
||||
<None Include="docs\articles\API\PublicModels.md" />
|
||||
<None Include="docs\articles\API\InterfaceGCIToLaatzen.md" />
|
||||
<None Include="docs\articles\API\InterfaceOutsideToGCI.md" />
|
||||
<None Include="docs\docfx.json" />
|
||||
<None Include="docs\index.md" />
|
||||
<None Include="docs\pages\development\page_dev__current_state.md" />
|
||||
<None Include="docs\pages\development\page_dev__home.md" />
|
||||
<None Include="docs\pages\development\page_dev__target_state.md" />
|
||||
<None Include="docs\pages\development\page_dev__migration.md" />
|
||||
<None Include="docs\pages\development\page_dev__refactoring.md" />
|
||||
<None Include="docs\pages\development\page_dev__target_architecture.md" />
|
||||
<None Include="docs\pages\gci\page_gci__app_environment.md" />
|
||||
<None Include="docs\pages\gci\page_gci__internal_architecture.md" />
|
||||
<None Include="docs\pages\gci\page_gci__datastorage.md" />
|
||||
<None Include="docs\pages\gci\page_gci__home.md" />
|
||||
<None Include="docs\pages\gci\page_gci__interfaces.md" />
|
||||
<None Include="docs\pages\gci\page_gci__runtime.md" />
|
||||
<None Include="docs\pages\gci\page_gci__implementation_to_tbf.md" />
|
||||
<None Include="docs\pages\gci\page_gci__implementation_universal.md" />
|
||||
<None Include="docs\pages\gci\page_gci__workers.md" />
|
||||
<None Include="docs\pages\platform\page_platform__configuration_domain.md" />
|
||||
<None Include="docs\pages\platform\page_platform__data_collection_domain.md" />
|
||||
<None Include="docs\pages\platform\page_platform__tbf_implementation.md" />
|
||||
<None Include="docs\pages\platform\page_platform__current_state.md" />
|
||||
<None Include="docs\pages\platform\page_platform__home.md" />
|
||||
<None Include="docs\toc.yml" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
@ -243,6 +309,8 @@
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Config\" />
|
||||
<Folder Include="Core\DataStorage\Writing\NewFolder1\" />
|
||||
<Folder Include="RuntimePackage\Package\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.dll" />
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.pdb" />
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.xml" />
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.exe" />
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.config" />
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.json" />
|
||||
|
||||
@ -122,22 +122,23 @@ namespace GenesisCordonelInterface.UI.Debug
|
||||
{
|
||||
isRefreshing = true;
|
||||
|
||||
foreach (var meter in data)
|
||||
{
|
||||
EnsurePortValueExists(meter.RequestPort);
|
||||
EnsurePortValueExists(meter.StreamingPort);
|
||||
if(data != null)
|
||||
foreach (var meter in data)
|
||||
{
|
||||
EnsurePortValueExists(meter.RequestPort);
|
||||
EnsurePortValueExists(meter.StreamingPort);
|
||||
|
||||
var row = FindOrCreateRow(meter.Slot);
|
||||
var row = FindOrCreateRow(meter.Slot);
|
||||
|
||||
Set(row, "Slot", meter.Slot);
|
||||
Set(row, "Selected", meter.Selected);
|
||||
Set(row, "PcbId", meter.PcbId);
|
||||
Set(row, "IsLoggedOn", meter.IsLoggedOn);
|
||||
Set(row, "RequestPort", meter.RequestPort);
|
||||
Set(row, "StreamingPort", meter.StreamingPort);
|
||||
Set(row, "FwVersion", meter.FwVersion);
|
||||
Set(row, "InterfaceVersion", meter.InterfaceVersion);
|
||||
}
|
||||
Set(row, "Slot", meter.Slot);
|
||||
Set(row, "Selected", meter.Selected);
|
||||
Set(row, "PcbId", meter.PcbId);
|
||||
Set(row, "IsLoggedOn", meter.IsLoggedOn);
|
||||
Set(row, "RequestPort", meter.RequestPort);
|
||||
Set(row, "StreamingPort", meter.StreamingPort);
|
||||
Set(row, "FwVersion", meter.FwVersion);
|
||||
Set(row, "InterfaceVersion", meter.InterfaceVersion);
|
||||
}
|
||||
|
||||
isRefreshing = false;
|
||||
}
|
||||
|
||||
@ -9,9 +9,10 @@ namespace GenesisCordonelInterface.UI.Grid
|
||||
return new List<MeterGridColumnConfig>
|
||||
{
|
||||
new MeterGridColumnConfig { Name = "Slot", HeaderText = "Slot", DisplayIndex = 0, Width = 50, ReadOnly = true },
|
||||
new MeterGridColumnConfig { Name = "Selected", HeaderText = "Selected", DisplayIndex = 1, Width = 60 },
|
||||
new MeterGridColumnConfig { Name = "Selected", HeaderText = "Selected", DisplayIndex = 1, Width = 40 },
|
||||
new MeterGridColumnConfig { Name = "PcbId", HeaderText = "PcbId", DisplayIndex = 2, Width = 80 },
|
||||
new MeterGridColumnConfig { Name = "IsLoggedOn", HeaderText = "IsLoggedOn", DisplayIndex = 3, Width = 80 },
|
||||
new MeterGridColumnConfig { Name = "IsConnected", HeaderText = "IsConnected", DisplayIndex = 3, Width = 40 },
|
||||
new MeterGridColumnConfig { Name = "IsLoggedOn", HeaderText = "IsLoggedOn", DisplayIndex = 3, Width = 40 },
|
||||
new MeterGridColumnConfig { Name = "RequestPort", HeaderText = "RequestPort", DisplayIndex = 4, Width = 90 },
|
||||
new MeterGridColumnConfig { Name = "StreamingPort", HeaderText = "StreamingPort", DisplayIndex = 5, Width = 100 },
|
||||
new MeterGridColumnConfig { Name = "FwVersion", HeaderText = "FwVersion", DisplayIndex = 6, Width = 80 },
|
||||
|
||||
@ -59,6 +59,7 @@ namespace GenesisCordonelInterface.UI.Grid
|
||||
SetCell(row, "Slot", meter.Slot);
|
||||
SetCell(row, "Selected", meter.Selected);
|
||||
SetCell(row, "PcbId", meter.PcbId);
|
||||
SetCell(row, "IsConnected", meter.IsConnected);
|
||||
SetCell(row, "IsLoggedOn", meter.IsLoggedOn);
|
||||
SetCell(row, "RequestPort", meter.RequestPort);
|
||||
SetCell(row, "StreamingPort", meter.StreamingPort);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
public int Slot { get; set; }
|
||||
public bool Selected { get; set; }
|
||||
public string PcbId { get; set; }
|
||||
public bool IsConnected { get; set; }
|
||||
public bool IsLoggedOn { get; set; }
|
||||
public string RequestPort { get; set; }
|
||||
public string StreamingPort { get; set; }
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Newtonsoft.Json;
|
||||
using CordonelPreadjustmentUi;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@ -10,18 +11,26 @@ using System.Windows.Forms;
|
||||
using Xylem.Common.CommonCore.Consts;
|
||||
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
||||
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
|
||||
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
|
||||
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
||||
using Xylem.Common.Ui.CordonelPreadjustmentUi;
|
||||
using CordonelPreadjustmentUi;
|
||||
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
|
||||
using System.Linq;//...MF
|
||||
|
||||
namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
|
||||
{
|
||||
public partial class FrmCordonelPreadjustmentUI : Form
|
||||
{
|
||||
private PreAdjustmentControl preadjustCtl;
|
||||
public PreAdjustmentControl preadjustCtl;//...MF
|
||||
private PreAdjustmentSettingsContainer mainSettings = new PreAdjustmentSettingsContainer();
|
||||
public MeterBatch _externMetersBatch;
|
||||
|
||||
public FrmCordonelPreadjustmentUI()
|
||||
{
|
||||
}
|
||||
public FrmCordonelPreadjustmentUI(MeterBatch externMetersBatch)
|
||||
{
|
||||
_externMetersBatch = externMetersBatch;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@ -29,7 +38,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
|
||||
{
|
||||
|
||||
|
||||
preadjustCtl = new PreAdjustmentControl(mainSettings);
|
||||
preadjustCtl = new PreAdjustmentControl(mainSettings, _externMetersBatch);
|
||||
tab_ZeroFlowCal.Controls.Add(preadjustCtl);
|
||||
|
||||
|
||||
@ -71,7 +80,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
|
||||
{
|
||||
if (e.TabPage.Name == tab_ZeroFlowCal.Name)
|
||||
{
|
||||
try
|
||||
/*try
|
||||
{
|
||||
var _serialConfigFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Genesis", ProgramConfig.SerialConfigFileName);
|
||||
|
||||
@ -99,11 +108,81 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"use default meters because of {ex.Message}");
|
||||
}*/
|
||||
|
||||
try //...MF
|
||||
{
|
||||
mainSettings.Meters = new List<int>();
|
||||
mainSettings.TempMeters = new List<int>();
|
||||
|
||||
// ==========================================
|
||||
// Try loading configuration from GCI meters
|
||||
// ==========================================
|
||||
|
||||
bool loadedFromInterface = false;
|
||||
|
||||
if (_externMetersBatch != null &&
|
||||
_externMetersBatch.ListOfMeters.Any())
|
||||
{
|
||||
foreach (GenesisMeter meter in _externMetersBatch.ListOfMeters)
|
||||
{
|
||||
// Skip meters that should use file configuration
|
||||
if (meter.useConfigSource != ConfigSource.InterfaceInputConfig)
|
||||
continue;
|
||||
|
||||
loadedFromInterface = true;
|
||||
|
||||
// Split normal and temperature meters
|
||||
//if (meter.Type == SlotType.TemperatureMeter)
|
||||
//{
|
||||
// mainSettings.TempMeters.Add(meter.Slot);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
mainSettings.Meters.Add(meter.Slot);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Fallback to configuration file
|
||||
// ==========================================
|
||||
|
||||
if (!loadedFromInterface)
|
||||
{
|
||||
var _serialConfigFile =
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"Genesis",
|
||||
ProgramConfig.SerialConfigFileName);
|
||||
|
||||
SlotConfig[] meterConfigList;
|
||||
|
||||
using (var tr = new StreamReader(_serialConfigFile))
|
||||
{
|
||||
var _fileString = tr.ReadToEnd();
|
||||
|
||||
meterConfigList =
|
||||
JsonConvert.DeserializeObject<SlotConfig[]>(_fileString);
|
||||
}
|
||||
|
||||
foreach (var item in meterConfigList)
|
||||
{
|
||||
if (item.Type != SlotType.TemperatureMeter)
|
||||
{
|
||||
mainSettings.Meters.Add(item.Slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
mainSettings.TempMeters.Add(item.Slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Use default meters because of {ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mainSettings.NumberOfPaths = cB_SinglePath.Checked ? 1 : 3;
|
||||
mainSettings.LowerTempLimit = (double)nUD_SettingsTempMonitorLowerValue.Value;
|
||||
@ -156,7 +235,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
|
||||
|
||||
}
|
||||
|
||||
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
/*private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
if (preadjustCtl != null)
|
||||
{
|
||||
@ -177,7 +256,68 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI
|
||||
}
|
||||
|
||||
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Raised when user closes the preadjustment window
|
||||
/// using the window close button (X).
|
||||
///
|
||||
/// Note:
|
||||
/// Form is hidden, not disposed.
|
||||
/// This event allows external code to continue
|
||||
/// workflow asynchronously.
|
||||
/// </summary>
|
||||
public event EventHandler OnUserClosed;
|
||||
|
||||
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
// User clicked X button - hide form only
|
||||
if (e.CloseReason == CloseReason.UserClosing)
|
||||
{
|
||||
e.Cancel = true;
|
||||
this.Hide();
|
||||
|
||||
OnUserClosed?.Invoke(this, EventArgs.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
// Real application shutdown / dispose
|
||||
DisposeResources();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose all internal resources that should
|
||||
/// only be released during real application shutdown.
|
||||
///
|
||||
/// Do not call when form is hidden.
|
||||
/// </summary>
|
||||
private void DisposeResources()
|
||||
{
|
||||
if (preadjustCtl != null)
|
||||
{
|
||||
preadjustCtl.CloseConnections();
|
||||
preadjustCtl.Dispose();
|
||||
preadjustCtl = null;
|
||||
}
|
||||
|
||||
if (ThermoMeterBatch != null)
|
||||
{
|
||||
ThermoMeterBatch.Dispose();
|
||||
ThermoMeterBatch = null;
|
||||
}
|
||||
|
||||
if (TempMeterStateCtls != null)
|
||||
{
|
||||
foreach (var item in TempMeterStateCtls)
|
||||
{
|
||||
item.Dispose();
|
||||
}
|
||||
|
||||
TempMeterStateCtls.Clear();
|
||||
TempMeterStateCtls = null;
|
||||
}
|
||||
}
|
||||
|
||||
private MeterBatch ThermoMeterBatch = new MeterBatch();
|
||||
private List<MeterStateControl> TempMeterStateCtls = new List<MeterStateControl>();
|
||||
private void tmpStart(int slot, bool RaspiMode = false)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using CordonelPreadjustmentUi.Processes;
|
||||
using CordonelPreadjustmentUi;
|
||||
using CordonelPreadjustmentUi.Processes;
|
||||
using CordonelPreadjustmentUi.Processes.Actions;
|
||||
using CordonelPreadjustmentUi.Processes.Itinerary;
|
||||
using System;
|
||||
@ -6,16 +7,18 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
|
||||
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
||||
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
|
||||
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
|
||||
using Xylem.Common.Ui.CordonelPreadjustmentUi;
|
||||
using Xylem.Common.Ui.CordonelPreadjustmentUi.Parameters;
|
||||
using CordonelPreadjustmentUi;
|
||||
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
|
||||
|
||||
namespace GenesisCordonelInterface.UI
|
||||
{
|
||||
@ -41,11 +44,18 @@ namespace GenesisCordonelInterface.UI
|
||||
private Boolean abortIndicator = false;
|
||||
public Boolean AbortIndicator { get { return abortIndicator; } set { abortIndicator = value; } }
|
||||
public int TestRunNumber;
|
||||
public PreAdjustmentControl(PreAdjustmentSettingsContainer Settings = null)
|
||||
public PreAdjustmentControl(PreAdjustmentSettingsContainer Settings = null, MeterBatch externalMetersBatch = null)//...MF
|
||||
{
|
||||
InitializeComponent();
|
||||
SetSettings(Settings);
|
||||
rTB_ZeroFlowCal.AutoSize = true;
|
||||
|
||||
//...MF
|
||||
// Use external batch only if supplied
|
||||
if (externalMetersBatch != null)
|
||||
{
|
||||
GlobalMeterBatch = externalMetersBatch;
|
||||
}
|
||||
}
|
||||
public void SetSettings(PreAdjustmentSettingsContainer Settings = null)
|
||||
{
|
||||
@ -120,6 +130,49 @@ namespace GenesisCordonelInterface.UI
|
||||
}
|
||||
cb_Metersize.SelectedItem = setM;
|
||||
|
||||
//...MF
|
||||
// Create UI controls for configured meter slots.
|
||||
//
|
||||
// Flow:
|
||||
//
|
||||
// settings.Meters
|
||||
// ↓
|
||||
// create MeterStateControl
|
||||
// ↓
|
||||
// position control in UI
|
||||
// ↓
|
||||
// check whether slot exists in externally supplied MeterBatch
|
||||
// ↓
|
||||
// automatically enable corresponding checkbox
|
||||
// ↓
|
||||
// register UI events
|
||||
// ↓
|
||||
// add control into internal collection and group box
|
||||
//
|
||||
// Notes:
|
||||
// - allows external GCI workflow to preselect meters
|
||||
// - keeps UI synchronized with externally injected MeterBatch
|
||||
// - slots contained in GlobalMeterBatch are automatically checked
|
||||
//
|
||||
foreach (var Meter in settings.Meters)
|
||||
{
|
||||
var ctl = new MeterStateControl(Meter);
|
||||
ctl.Location = new Point(5 + ((tmpI - 1) * ctl.Width), 15);
|
||||
|
||||
// Check meter if it exists in external MeterBatch
|
||||
if (GlobalMeterBatch != null &&
|
||||
GlobalMeterBatch.ListOfMeters != null &&
|
||||
GlobalMeterBatch.ListOfMeters.Any(m => m.Slot == Meter))
|
||||
{
|
||||
ctl.SetChecked(true);
|
||||
}
|
||||
|
||||
MeterStateCtls.Add(ctl);
|
||||
gB_Meters.Controls.Add(ctl);
|
||||
ctl.OnRequestDetails += Ctl_MouseEnter;
|
||||
tmpI = tmpI + 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1694,13 +1747,46 @@ namespace GenesisCordonelInterface.UI
|
||||
|
||||
List<IProcess> listOfProgrammParts = new List<IProcess>();
|
||||
|
||||
//...MF
|
||||
bool requiresInternalLoginFlow = true;
|
||||
requiresInternalLoginFlow =
|
||||
GlobalMeterBatch.ListOfMeters.All(
|
||||
m =>
|
||||
{
|
||||
var meter = m as GenesisMeter;
|
||||
|
||||
return meter == null ||
|
||||
meter.usePasswordSource !=
|
||||
PasswordSource.InterfaceInputPassword;
|
||||
});
|
||||
|
||||
listOfProgrammParts.Add(new PrepareTestProcess("PrepareTest", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, string.Empty, string.Empty, 1 * 60));
|
||||
//...MF
|
||||
bool requiresInternalConfigFlow = true;
|
||||
requiresInternalConfigFlow =
|
||||
GlobalMeterBatch.ListOfMeters.All(
|
||||
m =>
|
||||
{
|
||||
var meter = m as GenesisMeter;
|
||||
|
||||
listOfProgrammParts.Add(new LoginProcess("Login", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, PredefinedMessages.WaitUntilLoginFinished(pp.Setting.Culture), PredefinedMessages.LoginFailed(pp.Setting.Culture), 1 * 60));
|
||||
return meter == null ||
|
||||
meter.useConfigSource !=
|
||||
ConfigSource.InterfaceInputConfig;
|
||||
});
|
||||
|
||||
listOfProgrammParts.Add(new FlushProcess("First Flush", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, PredefinedMessages.WaitUntilFlushFinished(pp.Setting.Culture), string.Empty, 2 * 60));
|
||||
if (requiresInternalConfigFlow)//...MF
|
||||
{
|
||||
listOfProgrammParts.Add(new PrepareTestProcess("PrepareTest", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, string.Empty, string.Empty, 1 * 60));
|
||||
}
|
||||
|
||||
if (requiresInternalLoginFlow)//...MF
|
||||
{
|
||||
listOfProgrammParts.Add(new LoginProcess("Login", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, PredefinedMessages.WaitUntilLoginFinished(pp.Setting.Culture), PredefinedMessages.LoginFailed(pp.Setting.Culture), 1 * 60));
|
||||
}
|
||||
|
||||
if (requiresInternalConfigFlow)//...MF
|
||||
{
|
||||
listOfProgrammParts.Add(new FlushProcess("First Flush", CordonelPreadjustmentUi.PreAdjustmentControl.StatusPanelItems.Detect, PredefinedMessages.WaitUntilFlushFinished(pp.Setting.Culture), string.Empty, 2 * 60));
|
||||
}
|
||||
|
||||
//listOfProgrammParts.Add(new PressureTestProcess("PreussureTest", StatusPanelItems.Prepare, PredefinedMessages.WaitUntilPreparationFinished(pp.Setting.Culture), PredefinedMessages.PreparationFailed(pp.Setting.Culture), 1 * 60));
|
||||
|
||||
|
||||
@ -477,115 +477,6 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
}
|
||||
}
|
||||
|
||||
private async void __Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (cbComSlot.SelectedItem == null ||
|
||||
string.IsNullOrEmpty(cbComSlot.SelectedItem.ToString()) ||
|
||||
!int.TryParse(cbComSlot.SelectedItem.ToString(), out var slotNo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetBusy(true, "Connect");
|
||||
DisableAllButtons();
|
||||
_dataTable.Rows.Clear();
|
||||
|
||||
var result = await interfaceToLaatzen.ConnectOneSlotAsync(slotNo);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
lblState.ForeColor = Color.Green;
|
||||
lblState.Text = $@"Connected to PCB {result.PcbId}";
|
||||
lblConfigVersion.Text = @"Configuration Version: " + result.InterfaceVersion;
|
||||
lblConfigVersion.ForeColor = result.InterfaceSupportsFwVersion ? Color.Green : Color.Red;
|
||||
|
||||
if (!result.InterfaceSupportsFwVersion)
|
||||
{
|
||||
var text = "CONFIGURATION OUTDATED!\n\n" +
|
||||
"The loaded \"configuration.json\" " +
|
||||
$"version: {result.InterfaceVersion}\n" +
|
||||
$"does NOT support the Cordonel FW version: {result.FwVersion}!";
|
||||
|
||||
MessageBox.Show(text, @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
foreach (var item in result.Registers)
|
||||
{
|
||||
var row = _dataTable.NewRow();
|
||||
row["Name"] = item.Name;
|
||||
row["Type"] = item.Type;
|
||||
row["isChecked"] = false;
|
||||
row["Value"] = "";
|
||||
row["RawValue"] = item.RawValue;
|
||||
row["RawValueFile"] = item.RawValue;
|
||||
row["Min"] = item.Min;
|
||||
row["Max"] = item.Max;
|
||||
row["Description"] = item.Description;
|
||||
row["Version"] = item.Version;
|
||||
row["IsAvailable"] = item.IsAvailable;
|
||||
row["Privilege"] = item.Privilege;
|
||||
row["btnHistoryText"] = "View History";
|
||||
|
||||
_dataTable.Rows.Add(row);
|
||||
}
|
||||
|
||||
registerGridView.DataSource = _dataTable.DefaultView;
|
||||
|
||||
var column = registerGridView?.Columns["isChecked"];
|
||||
if (column != null)
|
||||
{
|
||||
column.SortMode = DataGridViewColumnSortMode.Automatic;
|
||||
registerGridView.Sort(column, ListSortDirection.Descending);
|
||||
|
||||
var viewColumn = registerGridView.Columns["RawValueFile"];
|
||||
if (viewColumn != null)
|
||||
viewColumn.Visible = false;
|
||||
}
|
||||
|
||||
column = registerGridView?.Columns["Name"];
|
||||
if (column != null)
|
||||
{
|
||||
registerGridView.Sort(column, ListSortDirection.Ascending);
|
||||
}
|
||||
|
||||
if (registerGridView.Columns["btnHistory"] == null)
|
||||
{
|
||||
var btnHistory = new DataGridViewButtonColumn
|
||||
{
|
||||
Name = "btnHistory",
|
||||
DataPropertyName = "btnHistoryText"
|
||||
};
|
||||
|
||||
registerGridView.Columns.Add(btnHistory);
|
||||
}
|
||||
|
||||
registerGridView.Visible = true;
|
||||
btnConnect.Enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
btnConnect.Enabled = false;
|
||||
lblState.ForeColor = Color.Red;
|
||||
lblState.Text = $@"Not Connected to PcbId:{result.PcbId}";
|
||||
registerGridView.Visible = false;
|
||||
|
||||
MessageBox.Show(result.Message ?? "Connect failed.", @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
btnConnect.Enabled = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetBusy(false);
|
||||
EnableAllButtons();
|
||||
}
|
||||
}
|
||||
|
||||
private void GetPcbId(Int32 slotNR)
|
||||
{
|
||||
try
|
||||
|
||||
1
GenesisCordonelInterface/UI/MainView.Designer.cs
generated
@ -188,7 +188,6 @@
|
||||
this.btnMeterInit.Size = new System.Drawing.Size(150, 30);
|
||||
this.btnMeterInit.Text = "Meter Init";
|
||||
this.btnMeterInit.UseVisualStyleBackColor = true;
|
||||
this.btnMeterInit.Click += new System.EventHandler(this.btnMeterInit_Click);
|
||||
|
||||
// btnMetersAction
|
||||
this.btnMetersAction.Location = new System.Drawing.Point(6, 58);
|
||||
|
||||
@ -192,13 +192,6 @@ namespace GenesisCordonelInterface.UI
|
||||
Logger.Trace($"FORM: GCI VIEW -> {name} LOADED");
|
||||
}
|
||||
|
||||
private void btnMeterInit_Click(object sender, EventArgs e)
|
||||
{
|
||||
SwitchGciView(
|
||||
"MeterInit",
|
||||
new MeterInitView(_api, AddSlotRow, SaveSlots));
|
||||
}
|
||||
|
||||
private void btnMetersAction_Click(object sender, EventArgs e)
|
||||
{
|
||||
SwitchGciView(
|
||||
@ -329,11 +322,5 @@ namespace GenesisCordonelInterface.UI
|
||||
view.Dock = DockStyle.Fill;
|
||||
pnlGciViewHost.Controls.Add(view);
|
||||
}
|
||||
|
||||
public void SaveSlots()
|
||||
{
|
||||
var data = _batchPanel.GetGridData();
|
||||
_api.SaveSlotSetup(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -268,7 +268,7 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
if (!result.Success)
|
||||
throw new Exception(result.Message);
|
||||
|
||||
MessageBox.Show($"Connected.\r\nPCB ID: {result.PcbId}");
|
||||
MessageBox.Show($"Connected.\r\n");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -26,7 +26,6 @@
|
||||
|
||||
this.btnReloadSetup.SetBounds(20, 20, 140, 32);
|
||||
this.btnReloadSetup.Text = "Reload Setup";
|
||||
this.btnReloadSetup.Click += new System.EventHandler(this.btnReloadSetup_Click);
|
||||
|
||||
this.btnSaveSetup.SetBounds(170, 20, 140, 32);
|
||||
this.btnSaveSetup.Text = "Save Setup";
|
||||
|
||||
@ -22,10 +22,6 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
#region BUTTONS
|
||||
// ----------------------------------------------------
|
||||
|
||||
private void btnReloadSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() => _api.ReloadSlotSetup());
|
||||
}
|
||||
|
||||
private void btnSaveSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
36
GenesisCordonelInterface/bin/Debug/Config/gci_config.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"_Comment": "GCI DataStorage configuration",
|
||||
|
||||
"DataStorageSection": {
|
||||
|
||||
"MeterLoginPasswords": {
|
||||
|
||||
"___Documentation___": {
|
||||
"Description": "Reads login passwords for meters by PCB ID",
|
||||
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||
"DataSource": "Database connection string",
|
||||
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [PcbId]=QUERYPARAM -> PCB ID provided during GetPasswordAsync()."
|
||||
},
|
||||
|
||||
"Name": "MeterLoginPasswords",
|
||||
"Type": "LocalDatabase",
|
||||
"DataSource": "Server=(localdb)\\SensusLocalDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||
"QueryTemplate": "SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM"
|
||||
},
|
||||
|
||||
"PreAdjustmentCalibrationParams": {
|
||||
|
||||
"___Documentation___": {
|
||||
"Description": "Reads pre-adjustment calibration parameters by meter size",
|
||||
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||
"DataSource": "Database connection string",
|
||||
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [MeterSize]=QUERYPARAM -> meter size provided during ReadCalibrationParamsAsync()."
|
||||
},
|
||||
|
||||
"Name": "PreAdjustmentCalibrationParams",
|
||||
"Type": "LocalDatabase",
|
||||
"DataSource": "Server=(localdb)\\SensusLocalDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
GenesisCordonelInterface/docs/_site.zip
Normal file
@ -0,0 +1,337 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>InterfaceOutsideToGCI | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="InterfaceOutsideToGCI | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="interfaceoutsidetogci">InterfaceOutsideToGCI</h1>
|
||||
|
||||
<h2 id="high-level-overview">High-Level Overview</h2>
|
||||
<p><img src="../../images/GCI__Onboarding_Overview_drawio.svg" alt="GCI Overview"></p>
|
||||
<h2 id="purpose">Purpose</h2>
|
||||
<p>Public API facade for external applications.</p>
|
||||
<h2 id="architecture">Architecture</h2>
|
||||
<p>External App
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
Meter</p>
|
||||
<h2 id="purpose-1">Purpose</h2>
|
||||
<p><code>InterfaceOutsideToGCI</code> is the public-facing API entry point of Genesis Cordonel Interface (GCI).</p>
|
||||
<p>The class exposes a simplified and controlled interface intended for external applications while hiding internal implementation details.</p>
|
||||
<p>Responsibilities:</p>
|
||||
<ul>
|
||||
<li>input validation</li>
|
||||
<li>public API exposure</li>
|
||||
<li>request model mapping</li>
|
||||
<li>forwarding calls into internal GCI services</li>
|
||||
<li>status notifications</li>
|
||||
<li>preadjustment workflow access</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="architecture-position">Architecture Position</h2>
|
||||
<pre><code class="lang-text">External Application
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
MeterBatch
|
||||
↓
|
||||
GenesisMeter
|
||||
</code></pre>
|
||||
<p>This layer intentionally acts as a facade and should contain minimal business logic.</p>
|
||||
<hr>
|
||||
<h2 id="design-goals">Design Goals</h2>
|
||||
<p>The API layer exists to:</p>
|
||||
<ul>
|
||||
<li>expose stable external contracts</li>
|
||||
<li>isolate external consumers from internal changes</li>
|
||||
<li>centralize validation</li>
|
||||
<li>simplify integration</li>
|
||||
<li>hide internal meter implementation details</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="request-flow">Request Flow</h2>
|
||||
<p>Typical operation flow:</p>
|
||||
<pre><code class="lang-text">External caller
|
||||
↓
|
||||
parameter validation
|
||||
↓
|
||||
model mapping
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
worker execution
|
||||
↓
|
||||
meter communication
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="slot-management">Slot Management</h2>
|
||||
<p>Supports:</p>
|
||||
<ul>
|
||||
<li>slot initialization</li>
|
||||
<li>slot update</li>
|
||||
<li>slot cleanup</li>
|
||||
<li>slot information retrieval</li>
|
||||
</ul>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>InitSlotAsync()</li>
|
||||
<li>UpdateSlotAsync()</li>
|
||||
<li>GetSlotAsync()</li>
|
||||
<li>GetAllSlotsAsync()</li>
|
||||
<li>CleanSlotAsync()</li>
|
||||
<li>CleanAllSlotsAsync()</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="port-detection">Port Detection</h2>
|
||||
<p>Supports automatic communication port detection.</p>
|
||||
<p>Available operations:</p>
|
||||
<h3 id="detectrequestportasync">DetectRequestPortAsync()</h3>
|
||||
<p>Attempts PCB communication through request channel.</p>
|
||||
<h3 id="detectstreamingportasync">DetectStreamingPortAsync()</h3>
|
||||
<p>Attempts streaming communication detection.</p>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>automatic setup</li>
|
||||
<li>communication diagnostics</li>
|
||||
<li>hardware discovery</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="meter-lifecycle">Meter Lifecycle</h2>
|
||||
<p>Supported lifecycle operations:</p>
|
||||
<pre><code class="lang-text">Initialize
|
||||
↓
|
||||
Login
|
||||
↓
|
||||
Connect
|
||||
↓
|
||||
Read/Write
|
||||
↓
|
||||
Disconnect
|
||||
↓
|
||||
Clean
|
||||
</code></pre>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>LoginOneSlotAsync()</li>
|
||||
<li>ConnectOneSlotAsync()</li>
|
||||
<li>DisconnectAsync()</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="register-access">Register Access</h2>
|
||||
<p>Provides firmware register access.</p>
|
||||
<h3 id="readregisterasync">ReadRegisterAsync()</h3>
|
||||
<p>Reads register values from meter firmware.</p>
|
||||
<h3 id="writeregisterasync">WriteRegisterAsync()</h3>
|
||||
<p>Writes register values.</p>
|
||||
<p>Optional behavior:</p>
|
||||
<ul>
|
||||
<li>storeToDevice</li>
|
||||
<li>refreshSystemState</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="password-management">Password Management</h2>
|
||||
<p>Supports runtime password changes.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>SetPasswordAsync()</li>
|
||||
<li>SetMeterPasswordAsync()</li>
|
||||
</ul>
|
||||
<p>Validation:</p>
|
||||
<ul>
|
||||
<li>slot id required</li>
|
||||
<li>password cannot be empty</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="pcb-operations">PCB Operations</h2>
|
||||
<p>Supports meter PCB identification.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>GetPcbIdAsync()</li>
|
||||
</ul>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>hardware identification</li>
|
||||
<li>diagnostics</li>
|
||||
<li>meter pairing</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="debug-support">Debug Support</h2>
|
||||
<p>Provides runtime diagnostics.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>GetWorkerDebugStatuses()</li>
|
||||
<li>GetMeterBatchDebugStatuses()</li>
|
||||
</ul>
|
||||
<p>Exposes:</p>
|
||||
<ul>
|
||||
<li>worker queues</li>
|
||||
<li>activity states</li>
|
||||
<li>slot status</li>
|
||||
<li>connection state</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="slot-selection-support">Slot Selection Support</h2>
|
||||
<p>Supports selection state management.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>SetSlotSelected()</li>
|
||||
<li>IsSlotSelected()</li>
|
||||
<li>GetSelectedSlots()</li>
|
||||
</ul>
|
||||
<p>Purpose:</p>
|
||||
<p>Used by UI and batch operations.</p>
|
||||
<hr>
|
||||
<h2 id="meter-batch-notifications">Meter Batch Notifications</h2>
|
||||
<p>Event:</p>
|
||||
<pre><code class="lang-csharp">MeterBatchStatusChanged
|
||||
</code></pre>
|
||||
<p>Purpose:</p>
|
||||
<p>Notify external consumers whenever meter batch state changes.</p>
|
||||
<p>Typical usage:</p>
|
||||
<pre><code class="lang-csharp">api.MeterBatchStatusChanged += UpdateUi;
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="preadjustment-support">PreAdjustment Support</h2>
|
||||
<p>Exposes standalone preadjustment workflow execution.</p>
|
||||
<p>Supported processes:</p>
|
||||
<ul>
|
||||
<li>Detect</li>
|
||||
<li>Preparation</li>
|
||||
<li>Amplitude Test</li>
|
||||
<li>Temperature Calibration</li>
|
||||
<li>Offset Test</li>
|
||||
<li>Completion</li>
|
||||
</ul>
|
||||
<p>Execution flow:</p>
|
||||
<pre><code class="lang-text">Detect
|
||||
↓
|
||||
Preparation
|
||||
↓
|
||||
Amplitude Test
|
||||
↓
|
||||
Temperature Calibration
|
||||
↓
|
||||
Offset Test
|
||||
↓
|
||||
Completion
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="threading">Threading</h2>
|
||||
<p>Public operations are asynchronous.</p>
|
||||
<p>Pattern:</p>
|
||||
<pre><code class="lang-csharp">await api.ConnectOneSlotAsync(slot);
|
||||
</code></pre>
|
||||
<p>Internally:</p>
|
||||
<pre><code class="lang-text">API
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
slot-specific execution
|
||||
</code></pre>
|
||||
<p>This prevents concurrent access conflicts.</p>
|
||||
<hr>
|
||||
<h2 id="notes">Notes</h2>
|
||||
<p>Important constraints:</p>
|
||||
<ul>
|
||||
<li>API should not contain business logic</li>
|
||||
<li>validation belongs here</li>
|
||||
<li>execution belongs to internal GCI</li>
|
||||
<li>external applications should use this layer only</li>
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,337 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>InterfaceOutsideToGCI | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="InterfaceOutsideToGCI | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="interfaceoutsidetogci">InterfaceOutsideToGCI</h1>
|
||||
|
||||
<h2 id="high-level-overview">High-Level Overview</h2>
|
||||
<p><img src="../../images/GCI__Onboarding_Overview_drawio.svg" alt="GCI Overview"></p>
|
||||
<h2 id="purpose">Purpose</h2>
|
||||
<p>Public API facade for external applications.</p>
|
||||
<h2 id="architecture">Architecture</h2>
|
||||
<p>External App
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
Meter</p>
|
||||
<h2 id="purpose-1">Purpose</h2>
|
||||
<p><code>InterfaceOutsideToGCI</code> is the public-facing API entry point of Genesis Cordonel Interface (GCI).</p>
|
||||
<p>The class exposes a simplified and controlled interface intended for external applications while hiding internal implementation details.</p>
|
||||
<p>Responsibilities:</p>
|
||||
<ul>
|
||||
<li>input validation</li>
|
||||
<li>public API exposure</li>
|
||||
<li>request model mapping</li>
|
||||
<li>forwarding calls into internal GCI services</li>
|
||||
<li>status notifications</li>
|
||||
<li>preadjustment workflow access</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="architecture-position">Architecture Position</h2>
|
||||
<pre><code class="lang-text">External Application
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
MeterBatch
|
||||
↓
|
||||
GenesisMeter
|
||||
</code></pre>
|
||||
<p>This layer intentionally acts as a facade and should contain minimal business logic.</p>
|
||||
<hr>
|
||||
<h2 id="design-goals">Design Goals</h2>
|
||||
<p>The API layer exists to:</p>
|
||||
<ul>
|
||||
<li>expose stable external contracts</li>
|
||||
<li>isolate external consumers from internal changes</li>
|
||||
<li>centralize validation</li>
|
||||
<li>simplify integration</li>
|
||||
<li>hide internal meter implementation details</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="request-flow">Request Flow</h2>
|
||||
<p>Typical operation flow:</p>
|
||||
<pre><code class="lang-text">External caller
|
||||
↓
|
||||
parameter validation
|
||||
↓
|
||||
model mapping
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
worker execution
|
||||
↓
|
||||
meter communication
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="slot-management">Slot Management</h2>
|
||||
<p>Supports:</p>
|
||||
<ul>
|
||||
<li>slot initialization</li>
|
||||
<li>slot update</li>
|
||||
<li>slot cleanup</li>
|
||||
<li>slot information retrieval</li>
|
||||
</ul>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>InitSlotAsync()</li>
|
||||
<li>UpdateSlotAsync()</li>
|
||||
<li>GetSlotAsync()</li>
|
||||
<li>GetAllSlotsAsync()</li>
|
||||
<li>CleanSlotAsync()</li>
|
||||
<li>CleanAllSlotsAsync()</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="port-detection">Port Detection</h2>
|
||||
<p>Supports automatic communication port detection.</p>
|
||||
<p>Available operations:</p>
|
||||
<h3 id="detectrequestportasync">DetectRequestPortAsync()</h3>
|
||||
<p>Attempts PCB communication through request channel.</p>
|
||||
<h3 id="detectstreamingportasync">DetectStreamingPortAsync()</h3>
|
||||
<p>Attempts streaming communication detection.</p>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>automatic setup</li>
|
||||
<li>communication diagnostics</li>
|
||||
<li>hardware discovery</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="meter-lifecycle">Meter Lifecycle</h2>
|
||||
<p>Supported lifecycle operations:</p>
|
||||
<pre><code class="lang-text">Initialize
|
||||
↓
|
||||
Login
|
||||
↓
|
||||
Connect
|
||||
↓
|
||||
Read/Write
|
||||
↓
|
||||
Disconnect
|
||||
↓
|
||||
Clean
|
||||
</code></pre>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>LoginOneSlotAsync()</li>
|
||||
<li>ConnectOneSlotAsync()</li>
|
||||
<li>DisconnectAsync()</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="register-access">Register Access</h2>
|
||||
<p>Provides firmware register access.</p>
|
||||
<h3 id="readregisterasync">ReadRegisterAsync()</h3>
|
||||
<p>Reads register values from meter firmware.</p>
|
||||
<h3 id="writeregisterasync">WriteRegisterAsync()</h3>
|
||||
<p>Writes register values.</p>
|
||||
<p>Optional behavior:</p>
|
||||
<ul>
|
||||
<li>storeToDevice</li>
|
||||
<li>refreshSystemState</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="password-management">Password Management</h2>
|
||||
<p>Supports runtime password changes.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>SetPasswordAsync()</li>
|
||||
<li>SetMeterPasswordAsync()</li>
|
||||
</ul>
|
||||
<p>Validation:</p>
|
||||
<ul>
|
||||
<li>slot id required</li>
|
||||
<li>password cannot be empty</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="pcb-operations">PCB Operations</h2>
|
||||
<p>Supports meter PCB identification.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>GetPcbIdAsync()</li>
|
||||
</ul>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>hardware identification</li>
|
||||
<li>diagnostics</li>
|
||||
<li>meter pairing</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="debug-support">Debug Support</h2>
|
||||
<p>Provides runtime diagnostics.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>GetWorkerDebugStatuses()</li>
|
||||
<li>GetMeterBatchDebugStatuses()</li>
|
||||
</ul>
|
||||
<p>Exposes:</p>
|
||||
<ul>
|
||||
<li>worker queues</li>
|
||||
<li>activity states</li>
|
||||
<li>slot status</li>
|
||||
<li>connection state</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="slot-selection-support">Slot Selection Support</h2>
|
||||
<p>Supports selection state management.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>SetSlotSelected()</li>
|
||||
<li>IsSlotSelected()</li>
|
||||
<li>GetSelectedSlots()</li>
|
||||
</ul>
|
||||
<p>Purpose:</p>
|
||||
<p>Used by UI and batch operations.</p>
|
||||
<hr>
|
||||
<h2 id="meter-batch-notifications">Meter Batch Notifications</h2>
|
||||
<p>Event:</p>
|
||||
<pre><code class="lang-csharp">MeterBatchStatusChanged
|
||||
</code></pre>
|
||||
<p>Purpose:</p>
|
||||
<p>Notify external consumers whenever meter batch state changes.</p>
|
||||
<p>Typical usage:</p>
|
||||
<pre><code class="lang-csharp">api.MeterBatchStatusChanged += UpdateUi;
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="preadjustment-support">PreAdjustment Support</h2>
|
||||
<p>Exposes standalone preadjustment workflow execution.</p>
|
||||
<p>Supported processes:</p>
|
||||
<ul>
|
||||
<li>Detect</li>
|
||||
<li>Preparation</li>
|
||||
<li>Amplitude Test</li>
|
||||
<li>Temperature Calibration</li>
|
||||
<li>Offset Test</li>
|
||||
<li>Completion</li>
|
||||
</ul>
|
||||
<p>Execution flow:</p>
|
||||
<pre><code class="lang-text">Detect
|
||||
↓
|
||||
Preparation
|
||||
↓
|
||||
Amplitude Test
|
||||
↓
|
||||
Temperature Calibration
|
||||
↓
|
||||
Offset Test
|
||||
↓
|
||||
Completion
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="threading">Threading</h2>
|
||||
<p>Public operations are asynchronous.</p>
|
||||
<p>Pattern:</p>
|
||||
<pre><code class="lang-csharp">await api.ConnectOneSlotAsync(slot);
|
||||
</code></pre>
|
||||
<p>Internally:</p>
|
||||
<pre><code class="lang-text">API
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
slot-specific execution
|
||||
</code></pre>
|
||||
<p>This prevents concurrent access conflicts.</p>
|
||||
<hr>
|
||||
<h2 id="notes">Notes</h2>
|
||||
<p>Important constraints:</p>
|
||||
<ul>
|
||||
<li>API should not contain business logic</li>
|
||||
<li>validation belongs here</li>
|
||||
<li>execution belongs to internal GCI</li>
|
||||
<li>external applications should use this layer only</li>
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,337 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>InterfaceOutsideToGCI | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="InterfaceOutsideToGCI | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="interfaceoutsidetogci">InterfaceOutsideToGCI</h1>
|
||||
|
||||
<h2 id="high-level-overview">High-Level Overview</h2>
|
||||
<p><img src="../../images/GCI__Onboarding_Overview_drawio.svg" alt="GCI Overview"></p>
|
||||
<h2 id="purpose">Purpose</h2>
|
||||
<p>Public API facade for external applications.</p>
|
||||
<h2 id="architecture">Architecture</h2>
|
||||
<p>External App
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
Meter</p>
|
||||
<h2 id="purpose-1">Purpose</h2>
|
||||
<p><code>InterfaceOutsideToGCI</code> is the public-facing API entry point of Genesis Cordonel Interface (GCI).</p>
|
||||
<p>The class exposes a simplified and controlled interface intended for external applications while hiding internal implementation details.</p>
|
||||
<p>Responsibilities:</p>
|
||||
<ul>
|
||||
<li>input validation</li>
|
||||
<li>public API exposure</li>
|
||||
<li>request model mapping</li>
|
||||
<li>forwarding calls into internal GCI services</li>
|
||||
<li>status notifications</li>
|
||||
<li>preadjustment workflow access</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="architecture-position">Architecture Position</h2>
|
||||
<pre><code class="lang-text">External Application
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
MeterBatch
|
||||
↓
|
||||
GenesisMeter
|
||||
</code></pre>
|
||||
<p>This layer intentionally acts as a facade and should contain minimal business logic.</p>
|
||||
<hr>
|
||||
<h2 id="design-goals">Design Goals</h2>
|
||||
<p>The API layer exists to:</p>
|
||||
<ul>
|
||||
<li>expose stable external contracts</li>
|
||||
<li>isolate external consumers from internal changes</li>
|
||||
<li>centralize validation</li>
|
||||
<li>simplify integration</li>
|
||||
<li>hide internal meter implementation details</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="request-flow">Request Flow</h2>
|
||||
<p>Typical operation flow:</p>
|
||||
<pre><code class="lang-text">External caller
|
||||
↓
|
||||
parameter validation
|
||||
↓
|
||||
model mapping
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
worker execution
|
||||
↓
|
||||
meter communication
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="slot-management">Slot Management</h2>
|
||||
<p>Supports:</p>
|
||||
<ul>
|
||||
<li>slot initialization</li>
|
||||
<li>slot update</li>
|
||||
<li>slot cleanup</li>
|
||||
<li>slot information retrieval</li>
|
||||
</ul>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>InitSlotAsync()</li>
|
||||
<li>UpdateSlotAsync()</li>
|
||||
<li>GetSlotAsync()</li>
|
||||
<li>GetAllSlotsAsync()</li>
|
||||
<li>CleanSlotAsync()</li>
|
||||
<li>CleanAllSlotsAsync()</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="port-detection">Port Detection</h2>
|
||||
<p>Supports automatic communication port detection.</p>
|
||||
<p>Available operations:</p>
|
||||
<h3 id="detectrequestportasync">DetectRequestPortAsync()</h3>
|
||||
<p>Attempts PCB communication through request channel.</p>
|
||||
<h3 id="detectstreamingportasync">DetectStreamingPortAsync()</h3>
|
||||
<p>Attempts streaming communication detection.</p>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>automatic setup</li>
|
||||
<li>communication diagnostics</li>
|
||||
<li>hardware discovery</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="meter-lifecycle">Meter Lifecycle</h2>
|
||||
<p>Supported lifecycle operations:</p>
|
||||
<pre><code class="lang-text">Initialize
|
||||
↓
|
||||
Login
|
||||
↓
|
||||
Connect
|
||||
↓
|
||||
Read/Write
|
||||
↓
|
||||
Disconnect
|
||||
↓
|
||||
Clean
|
||||
</code></pre>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>LoginOneSlotAsync()</li>
|
||||
<li>ConnectOneSlotAsync()</li>
|
||||
<li>DisconnectAsync()</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="register-access">Register Access</h2>
|
||||
<p>Provides firmware register access.</p>
|
||||
<h3 id="readregisterasync">ReadRegisterAsync()</h3>
|
||||
<p>Reads register values from meter firmware.</p>
|
||||
<h3 id="writeregisterasync">WriteRegisterAsync()</h3>
|
||||
<p>Writes register values.</p>
|
||||
<p>Optional behavior:</p>
|
||||
<ul>
|
||||
<li>storeToDevice</li>
|
||||
<li>refreshSystemState</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="password-management">Password Management</h2>
|
||||
<p>Supports runtime password changes.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>SetPasswordAsync()</li>
|
||||
<li>SetMeterPasswordAsync()</li>
|
||||
</ul>
|
||||
<p>Validation:</p>
|
||||
<ul>
|
||||
<li>slot id required</li>
|
||||
<li>password cannot be empty</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="pcb-operations">PCB Operations</h2>
|
||||
<p>Supports meter PCB identification.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>GetPcbIdAsync()</li>
|
||||
</ul>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>hardware identification</li>
|
||||
<li>diagnostics</li>
|
||||
<li>meter pairing</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="debug-support">Debug Support</h2>
|
||||
<p>Provides runtime diagnostics.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>GetWorkerDebugStatuses()</li>
|
||||
<li>GetMeterBatchDebugStatuses()</li>
|
||||
</ul>
|
||||
<p>Exposes:</p>
|
||||
<ul>
|
||||
<li>worker queues</li>
|
||||
<li>activity states</li>
|
||||
<li>slot status</li>
|
||||
<li>connection state</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="slot-selection-support">Slot Selection Support</h2>
|
||||
<p>Supports selection state management.</p>
|
||||
<p>Methods:</p>
|
||||
<ul>
|
||||
<li>SetSlotSelected()</li>
|
||||
<li>IsSlotSelected()</li>
|
||||
<li>GetSelectedSlots()</li>
|
||||
</ul>
|
||||
<p>Purpose:</p>
|
||||
<p>Used by UI and batch operations.</p>
|
||||
<hr>
|
||||
<h2 id="meter-batch-notifications">Meter Batch Notifications</h2>
|
||||
<p>Event:</p>
|
||||
<pre><code class="lang-csharp">MeterBatchStatusChanged
|
||||
</code></pre>
|
||||
<p>Purpose:</p>
|
||||
<p>Notify external consumers whenever meter batch state changes.</p>
|
||||
<p>Typical usage:</p>
|
||||
<pre><code class="lang-csharp">api.MeterBatchStatusChanged += UpdateUi;
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="preadjustment-support">PreAdjustment Support</h2>
|
||||
<p>Exposes standalone preadjustment workflow execution.</p>
|
||||
<p>Supported processes:</p>
|
||||
<ul>
|
||||
<li>Detect</li>
|
||||
<li>Preparation</li>
|
||||
<li>Amplitude Test</li>
|
||||
<li>Temperature Calibration</li>
|
||||
<li>Offset Test</li>
|
||||
<li>Completion</li>
|
||||
</ul>
|
||||
<p>Execution flow:</p>
|
||||
<pre><code class="lang-text">Detect
|
||||
↓
|
||||
Preparation
|
||||
↓
|
||||
Amplitude Test
|
||||
↓
|
||||
Temperature Calibration
|
||||
↓
|
||||
Offset Test
|
||||
↓
|
||||
Completion
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="threading">Threading</h2>
|
||||
<p>Public operations are asynchronous.</p>
|
||||
<p>Pattern:</p>
|
||||
<pre><code class="lang-csharp">await api.ConnectOneSlotAsync(slot);
|
||||
</code></pre>
|
||||
<p>Internally:</p>
|
||||
<pre><code class="lang-text">API
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
slot-specific execution
|
||||
</code></pre>
|
||||
<p>This prevents concurrent access conflicts.</p>
|
||||
<hr>
|
||||
<h2 id="notes">Notes</h2>
|
||||
<p>Important constraints:</p>
|
||||
<ul>
|
||||
<li>API should not contain business logic</li>
|
||||
<li>validation belongs here</li>
|
||||
<li>execution belongs to internal GCI</li>
|
||||
<li>external applications should use this layer only</li>
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
145
GenesisCordonelInterface/docs/_site/articles/API/index.html
Normal file
@ -0,0 +1,145 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Genesis Cordonel Interface Documentation | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Genesis Cordonel Interface Documentation | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="genesis-cordonel-interface-documentation">Genesis Cordonel Interface Documentation</h1>
|
||||
|
||||
<h2 id="gci-onboarding-overview">GCI Onboarding Overview</h2>
|
||||
<p><img src="../../images/GCI__Onboarding_Overview_drawio.svg" alt="GCI Onboarding Overview"></p>
|
||||
<hr>
|
||||
<h2 id="what-is-gci">What is GCI?</h2>
|
||||
<p>Genesis Cordonel Interface (GCI) is a software layer providing controlled access to:</p>
|
||||
<ul>
|
||||
<li>meter communication</li>
|
||||
<li>firmware interaction</li>
|
||||
<li>runtime slot management</li>
|
||||
<li>register access</li>
|
||||
<li>preadjustment workflows</li>
|
||||
<li>diagnostics and worker execution</li>
|
||||
</ul>
|
||||
<p>GCI acts as an integration bridge between external applications and internal meter infrastructure.</p>
|
||||
<hr>
|
||||
<h2 id="main-interface-layers">Main Interface Layers</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="InterfaceOutsideToGCI.html">InterfaceOutsideToGCI</a></td>
|
||||
<td>External API facade used by applications outside GCI.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="InterfaceGCIToLaatzen.html">InterfaceGCIToLaatzen</a></td>
|
||||
<td>Internal bridge and orchestration layer.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="PublicModels.html">PublicModels</a></td>
|
||||
<td>Public API models, request DTOs and result DTOs.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h2 id="main-areas">Main Areas</h2>
|
||||
<ul>
|
||||
<li>Slot lifecycle management</li>
|
||||
<li>Meter login and connection</li>
|
||||
<li>Register read/write access</li>
|
||||
<li>PCB identification</li>
|
||||
<li>Port detection</li>
|
||||
<li>Worker diagnostics</li>
|
||||
<li>Preadjustment workflows</li>
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
BIN
GenesisCordonelInterface/docs/_site/favicon.ico
Normal file
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 7.5 MiB |
|
After Width: | Height: | Size: 7.8 MiB |
|
After Width: | Height: | Size: 10 MiB |
|
After Width: | Height: | Size: 12 MiB |
|
After Width: | Height: | Size: 22 KiB |
BIN
GenesisCordonelInterface/docs/_site/images/logo/logo.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
24
GenesisCordonelInterface/docs/_site/images/logo/logo.svg
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Ebene_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 277.8 72">
|
||||
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #003799;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
fill: #72d54a;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="st1" d="M85.84,0h-38.33v7.2h38.33S85.84,0,85.84,0Z"/>
|
||||
<path class="st1" d="M85.84,11.75h-38.33v7.2h38.33v-7.2Z"/>
|
||||
<path class="st1" d="M85.84,23.51h-38.33v7.2h38.33v-7.2Z"/>
|
||||
<path class="st0" d="M126.12,35.27h-22.49c-7.2,0-9.39,2.1-9.39,8.98v27.75h9.22v-28.91h22.84v28.91h9.22v-27.75c0-6.88-2.2-8.98-9.39-8.98,0,0,0,0,0,0Z"/>
|
||||
<path class="st0" d="M221.89,35.27v28.92h-22.84v-28.92h-9.22v27.75c0,6.88,2.2,8.98,9.39,8.98h22.5c7.19,0,9.39-2.1,9.39-8.98v-27.75h-9.22Z"/>
|
||||
<path class="st0" d="M55.94,35.27c-6.88,0-8.99,2.2-8.99,9.39v17.94c0,7.2,2.1,9.39,8.99,9.39h29.89v-7.82h-30.29v-7.01h30.29v-7.53h-30.29v-7.04h30.29v-7.33h-29.89Z"/>
|
||||
<path class="st0" d="M38.79,42.62v-7.34H9.45C2.25,35.28.06,37.38.06,44.26v3.92c0,6.88,2.2,8.98,9.39,8.98h21.65v7.01H.04v7.82h30.03c7.16,0,9.46-2.18,9.46-8.98v-4.9c0-6.8-2.3-8.98-9.46-8.98H8.42v-6.53h30.37v.02Z"/>
|
||||
<path class="st0" d="M181.58,42.62v-7.34h-29.34c-7.2,0-9.4,2.1-9.4,8.98v3.92c0,6.88,2.2,8.98,9.4,8.98h21.65v7.01h-31.06v7.82h30.03c7.16,0,9.46-2.18,9.46-8.98v-4.89c0-6.8-2.3-8.98-9.46-8.98h-21.65v-6.53h30.37Z"/>
|
||||
<path class="st0" d="M277.04,42.62v-7.34h-29.35c-7.19,0-9.39,2.1-9.39,8.98v3.92c0,6.88,2.19,8.98,9.39,8.98h21.65v7.01h-31.05v7.82h30.02c7.16,0,9.46-2.18,9.46-8.98v-4.89c0-6.8-2.3-8.98-9.46-8.98h-21.65v-6.53h30.38Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Ebene_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 277.8 72">
|
||||
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #003799;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
fill: #72d54a;
|
||||
}
|
||||
|
||||
.st2 {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="st0" width="277.8" height="72"/>
|
||||
<path class="st1" d="M85.84,0h-38.33v7.2h38.33V0Z"/>
|
||||
<path class="st1" d="M85.84,11.75h-38.33v7.2h38.33v-7.2Z"/>
|
||||
<path class="st1" d="M85.84,23.51h-38.33v7.2h38.33s0-7.2,0-7.2Z"/>
|
||||
<path class="st2" d="M126.12,35.27h-22.49c-7.2,0-9.39,2.1-9.39,8.98v27.75h9.22v-28.91h22.84v28.91h9.22v-27.75c0-6.88-2.2-8.98-9.39-8.98h-.01Z"/>
|
||||
<path class="st2" d="M221.89,35.27v28.92h-22.84v-28.92h-9.22v27.75c0,6.88,2.2,8.98,9.39,8.98h22.5c7.19,0,9.39-2.1,9.39-8.98v-27.75h-9.22Z"/>
|
||||
<path class="st2" d="M55.94,35.27c-6.88,0-8.99,2.2-8.99,9.39v17.94c0,7.2,2.1,9.39,8.99,9.39h29.89v-7.82h-30.29v-7.01h30.29v-7.53h-30.29v-7.04h30.29v-7.33h-29.89,0Z"/>
|
||||
<path class="st2" d="M38.79,42.62v-7.34H9.45C2.25,35.28.06,37.38.06,44.26v3.92c0,6.88,2.2,8.98,9.39,8.98h21.65v7.01H.04v7.82h30.03c7.16,0,9.46-2.18,9.46-8.98v-4.9c0-6.8-2.3-8.98-9.46-8.98H8.42v-6.53h30.37v.02Z"/>
|
||||
<path class="st2" d="M181.58,42.62v-7.34h-29.34c-7.2,0-9.4,2.1-9.4,8.98v3.92c0,6.88,2.2,8.98,9.4,8.98h21.65v7.01h-31.06v7.82h30.03c7.16,0,9.46-2.18,9.46-8.98v-4.89c0-6.8-2.3-8.98-9.46-8.98h-21.65v-6.53h30.37Z"/>
|
||||
<path class="st2" d="M277.04,42.62v-7.34h-29.35c-7.19,0-9.39,2.1-9.39,8.98v3.92c0,6.88,2.19,8.98,9.39,8.98h21.65v7.01h-31.05v7.82h30.02c7.16,0,9.46-2.18,9.46-8.98v-4.89c0-6.8-2.3-8.98-9.46-8.98h-21.65v-6.53h30.38Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
144
GenesisCordonelInterface/docs/_site/index.html
Normal file
@ -0,0 +1,144 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Genesis Meter Platform Documentation | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Genesis Meter Platform Documentation | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="public/docfx.min.css">
|
||||
<link rel="stylesheet" href="public/main.css">
|
||||
<meta name="docfx:navrel" content="toc.html">
|
||||
<meta name="docfx:tocrel" content="toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="index.html">
|
||||
<img id="logo" class="svg" src="images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="genesis-meter-platform-documentation">Genesis Meter Platform Documentation</h1>
|
||||
|
||||
<p>This documentation describes the architecture, implementation, and evolution of the Genesis Meter Platform.</p>
|
||||
<p>It covers:</p>
|
||||
<ul>
|
||||
<li>platform architecture</li>
|
||||
<li>configuration and data collection domains</li>
|
||||
<li>current implementation state</li>
|
||||
<li>integration into TestBenchFramework</li>
|
||||
<li>development evolution</li>
|
||||
<li>migration and refactoring activities</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="main-documentation-areas">Main Documentation Areas</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Area</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="pages/platform/page_platform__home.html">Platform</a></td>
|
||||
<td>Platform architecture, functional domains, current implementation, and TestBenchFramework integration.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="pages/development/page_dev__home.html">Continuous Development</a></td>
|
||||
<td>Current and Target architecture comparison, migration strategy, refactoring activities, and future development direction.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h2 id="main-topics">Main Topics</h2>
|
||||
<h3 id="platform">Platform</h3>
|
||||
<ul>
|
||||
<li>platform overview</li>
|
||||
<li>current state</li>
|
||||
<li>configuration domain</li>
|
||||
<li>data collection domain</li>
|
||||
<li>TestBenchFramework implementation</li>
|
||||
</ul>
|
||||
<h3 id="continuous-development">Continuous Development</h3>
|
||||
<ul>
|
||||
<li>target state</li>
|
||||
<li>target architecture</li>
|
||||
<li>migration</li>
|
||||
<li>refactoring</li>
|
||||
<li>future concepts</li>
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
25
GenesisCordonelInterface/docs/_site/logo.svg
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="38.000000pt" height="38.000000pt" viewBox="0 0 172.000000 172.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by Docfx
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,172.000000) scale(0.100000,-0.100000)"
|
||||
fill="#dddddd" stroke="none">
|
||||
<path d="M230 1359 c0 -18 11 -30 44 -48 80 -42 81 -45 81 -441 0 -400 -1
|
||||
-404 -79 -436 -36 -15 -46 -24 -46 -43 0 -23 2 -24 61 -17 34 3 88 6 120 6
|
||||
l59 0 0 495 0 495 -82 0 c-46 0 -100 3 -120 6 -35 6 -38 5 -38 -17z"/>
|
||||
<path d="M618 1373 l-118 -4 0 -493 0 -494 154 -7 c181 -9 235 -3 313 34 68
|
||||
33 168 130 207 202 75 136 75 384 1 536 -71 145 -234 240 -399 231 -23 -1 -94
|
||||
-4 -158 -5z m287 -119 c68 -24 144 -101 176 -179 22 -54 24 -75 24 -210 0
|
||||
-141 -2 -153 -26 -206 -36 -76 -89 -132 -152 -160 -45 -21 -68 -24 -164 -24
|
||||
-71 0 -116 4 -123 11 -22 22 -31 175 -28 463 2 208 6 293 15 302 32 32 188 33
|
||||
278 3z"/>
|
||||
<path d="M1170 1228 c75 -104 110 -337 76 -508 -21 -100 -56 -178 -105 -233
|
||||
l-36 -41 34 20 c75 43 160 133 198 212 37 75 38 78 38 191 -1 129 -18 191 -75
|
||||
270 -28 38 -136 131 -153 131 -4 0 6 -19 23 -42z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
394
GenesisCordonelInterface/docs/_site/manifest.json
Normal file
@ -0,0 +1,394 @@
|
||||
{
|
||||
"source_base_path": "C:/Sensus_projects/New/LocalBranch_start_at_6.3.2026/tbf-local/tbf/GenesisCordonelInterface/docs",
|
||||
"xrefmap": "xrefmap.yml",
|
||||
"files": [
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "articles/API/InterfaceGCIToLaatzen.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "articles/API/InterfaceGCIToLaatzen.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "articles/API/InterfaceOutsideToGCI.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "articles/API/InterfaceOutsideToGCI.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "articles/API/PublicModels.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "articles/API/PublicModels.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "articles/API/index.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "articles/API/index.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/Cordonel Management and Data collection-API arch. - Target state.drawio.svg",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/Cordonel Management and Data collection-API arch. - Target state.drawio.svg"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/GCI__Onboarding_Overview_drawio.svg",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/GCI__Onboarding_Overview_drawio.svg"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/GCI__Onboarding_Overview_drawio__API_architecture__Current_state.svg",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/GCI__Onboarding_Overview_drawio__API_architecture__Current_state.svg"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/GCI__Onboarding_Overview_drawio__API_architecture__Target_state.svg",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/GCI__Onboarding_Overview_drawio__API_architecture__Target_state.svg"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/GCI__Onboarding_Overview_drawio__Data_collection_domain.svg",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/GCI__Onboarding_Overview_drawio__Data_collection_domain.svg"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/GCI__Onboarding_Overview_drawio__Meter_management_domain.svg",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/GCI__Onboarding_Overview_drawio__Meter_management_domain.svg"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/GciBridge_component_GUI.png",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/GciBridge_component_GUI.png"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/logo/logo.png",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/logo/logo.png"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/logo/logo.svg",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/logo/logo.svg"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/logo/sensus-logo-white-green-rgb.png",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/logo/sensus-logo-white-green-rgb.png"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "images/logo/sensus-logo-white-green-rgb.svg",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "images/logo/sensus-logo-white-green-rgb.svg"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "index.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "index.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/development/page_dev__current_state.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/development/page_dev__current_state.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/development/page_dev__home.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/development/page_dev__home.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/development/page_dev__migration.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/development/page_dev__migration.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/development/page_dev__refactoring.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/development/page_dev__refactoring.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/development/page_dev__target_architecture.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/development/page_dev__target_architecture.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/development/page_dev__target_state.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/development/page_dev__target_state.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__app_environment.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__app_environment.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__datastorage.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__datastorage.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__home.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__home.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__implementation_to_tbf.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__implementation_to_tbf.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__implementation_universal.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__implementation_universal.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__interfaces.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__interfaces.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__internal_architecture.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__internal_architecture.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__runtime.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__runtime.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/gci/page_gci__workers.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/gci/page_gci__workers.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/platform/page_platform__configuration_domain.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/platform/page_platform__configuration_domain.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/platform/page_platform__current_state.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/platform/page_platform__current_state.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/platform/page_platform__data_collection_domain.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/platform/page_platform__data_collection_domain.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/platform/page_platform__home.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/platform/page_platform__home.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Conceptual",
|
||||
"source_relative_path": "pages/platform/page_platform__tbf_implementation.md",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "pages/platform/page_platform__tbf_implementation.html"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Resource",
|
||||
"source_relative_path": "styles/main.css",
|
||||
"output": {
|
||||
"resource": {
|
||||
"relative_path": "styles/main.css"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "Toc",
|
||||
"source_relative_path": "toc.yml",
|
||||
"output": {
|
||||
".html": {
|
||||
"relative_path": "toc.html"
|
||||
},
|
||||
".json": {
|
||||
"relative_path": "toc.json"
|
||||
}
|
||||
},
|
||||
"version": ""
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"xrefmap": "xrefmap.yml"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,346 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Current Development State | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Current Development State | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="current-development-state">Current Development State</h1>
|
||||
|
||||
<h2 id="current-system-overview">Current System Overview</h2>
|
||||
<p><a href="../../images/GCI__Onboarding_Overview_drawio.svg"><img src="../../images/GCI__Onboarding_Overview_drawio.svg" alt="Current Architecture Overview"></a></p>
|
||||
<hr>
|
||||
<h1 id="introduction">Introduction</h1>
|
||||
<p>This page describes the current development and architectural state of the Genesis Cordonel Interface (GCI) project.</p>
|
||||
<p>The current implementation represents a transitional state between the original TBF-oriented architecture and the future modular runtime-oriented platform.</p>
|
||||
<p>The system already contains a significant amount of separated runtime infrastructure, worker isolation and reusable interface layers, but several legacy dependencies and historical design decisions are still present.</p>
|
||||
<hr>
|
||||
<h1 id="current-functional-scope">Current Functional Scope</h1>
|
||||
<p>The current implementation of GCI already provides the foundational runtime infrastructure for:</p>
|
||||
<ul>
|
||||
<li>meter management</li>
|
||||
<li>slot lifecycle handling</li>
|
||||
<li>worker-based execution</li>
|
||||
<li>firmware communication</li>
|
||||
<li>register access</li>
|
||||
<li>workflow execution</li>
|
||||
<li>diagnostics</li>
|
||||
<li>runtime orchestration</li>
|
||||
</ul>
|
||||
<p>However, the overall system architecture is still in transition.</p>
|
||||
<p>At the current stage of development, continuous streaming data acquisition and measurement collection are still handled outside of GCI.</p>
|
||||
<p>Streaming-related functionality currently remains partially implemented in external or historical TBF-oriented runtime layers.</p>
|
||||
<p>As a result, the current system operates as a hybrid environment:</p>
|
||||
<pre><code class="lang-text">GCI
|
||||
↓
|
||||
meter runtime management
|
||||
↓
|
||||
register access
|
||||
↓
|
||||
workflow execution
|
||||
|
||||
external streaming infrastructure
|
||||
↓
|
||||
continuous measurement acquisition
|
||||
↓
|
||||
measurement processing
|
||||
</code></pre>
|
||||
<p>The long-term architectural direction is to gradually evaluate and potentially consolidate streaming-related infrastructure into a more unified runtime-oriented architecture.</p>
|
||||
<hr>
|
||||
<h1 id="current-development-philosophy">Current Development Philosophy</h1>
|
||||
<p>The current development stage focuses primarily on establishing stable runtime foundations.</p>
|
||||
<p>Priority areas currently include:</p>
|
||||
<ul>
|
||||
<li>runtime isolation</li>
|
||||
<li>worker orchestration</li>
|
||||
<li>reusable interfaces</li>
|
||||
<li>communication abstraction</li>
|
||||
<li>DataStorage abstraction</li>
|
||||
<li>architectural separation</li>
|
||||
<li>maintainable runtime infrastructure</li>
|
||||
</ul>
|
||||
<p>At this stage, architectural stabilization is prioritized over complete feature centralization.</p>
|
||||
<hr>
|
||||
<h1 id="current-architectural-state">Current Architectural State</h1>
|
||||
<p>The current architecture is based on several major runtime layers:</p>
|
||||
<pre><code class="lang-text">External Applications
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker Layer
|
||||
↓
|
||||
MeterBatch Runtime
|
||||
↓
|
||||
GenesisMeter
|
||||
↓
|
||||
Firmware Communication
|
||||
</code></pre>
|
||||
<p>The system already supports:</p>
|
||||
<ul>
|
||||
<li>reusable runtime orchestration</li>
|
||||
<li>slot-based execution</li>
|
||||
<li>worker isolation</li>
|
||||
<li>runtime diagnostics</li>
|
||||
<li>DataStorage abstraction</li>
|
||||
<li>API-based communication</li>
|
||||
<li>external application integration</li>
|
||||
<li>preadjustment workflow execution</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="current-runtime-characteristics">Current Runtime Characteristics</h1>
|
||||
<p>The runtime currently behaves as a hybrid system combining:</p>
|
||||
<ul>
|
||||
<li>legacy TBF concepts</li>
|
||||
<li>newer runtime-oriented orchestration</li>
|
||||
<li>modular communication abstraction</li>
|
||||
<li>reusable integration interfaces</li>
|
||||
</ul>
|
||||
<p>Several components are already partially decoupled from the original TBF environment.</p>
|
||||
<hr>
|
||||
<h1 id="existing-strengths">Existing Strengths</h1>
|
||||
<p>The current implementation already provides several strong architectural foundations.</p>
|
||||
<h2 id="runtime-isolation">Runtime Isolation</h2>
|
||||
<ul>
|
||||
<li>slot-based execution model</li>
|
||||
<li>separated worker execution</li>
|
||||
<li>runtime lifecycle handling</li>
|
||||
<li>operation serialization</li>
|
||||
</ul>
|
||||
<h2 id="communication-infrastructure">Communication Infrastructure</h2>
|
||||
<ul>
|
||||
<li>separated request and streaming channels</li>
|
||||
<li>meter communication abstraction</li>
|
||||
<li>runtime communication monitoring</li>
|
||||
<li>firmware access encapsulation</li>
|
||||
</ul>
|
||||
<h2 id="integration-layer">Integration Layer</h2>
|
||||
<ul>
|
||||
<li>reusable external interfaces</li>
|
||||
<li>API-oriented runtime access</li>
|
||||
<li>separation between UI and runtime logic</li>
|
||||
<li>support for multiple integration environments</li>
|
||||
</ul>
|
||||
<h2 id="data-infrastructure">Data Infrastructure</h2>
|
||||
<ul>
|
||||
<li>configurable DataStorage abstraction</li>
|
||||
<li>runtime data access separation</li>
|
||||
<li>support for multiple storage providers</li>
|
||||
<li>evolving generic reader architecture</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="current-technical-challenges">Current Technical Challenges</h1>
|
||||
<p>Several areas still require refactoring and architectural cleanup.</p>
|
||||
<h2 id="legacy-dependencies">Legacy Dependencies</h2>
|
||||
<p>Some runtime parts are still tightly coupled to historical TBF infrastructure.</p>
|
||||
<p>Typical examples:</p>
|
||||
<ul>
|
||||
<li>legacy initialization flows</li>
|
||||
<li>historical runtime assumptions</li>
|
||||
<li>direct component dependencies</li>
|
||||
<li>non-unified configuration handling</li>
|
||||
</ul>
|
||||
<h2 id="runtime-complexity">Runtime Complexity</h2>
|
||||
<p>The current system evolved incrementally over time.</p>
|
||||
<p>As a result:</p>
|
||||
<ul>
|
||||
<li>responsibilities are not always fully separated</li>
|
||||
<li>some workflows still contain duplicated logic</li>
|
||||
<li>worker orchestration can be simplified</li>
|
||||
<li>communication handling still contains historical layers</li>
|
||||
</ul>
|
||||
<h2 id="configuration-fragmentation">Configuration Fragmentation</h2>
|
||||
<p>Configuration currently exists across multiple mechanisms:</p>
|
||||
<ul>
|
||||
<li>runtime configuration</li>
|
||||
<li>historical TBF configuration</li>
|
||||
<li>workflow-specific configuration</li>
|
||||
<li>communication-specific setup</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="current-refactoring-direction">Current Refactoring Direction</h1>
|
||||
<p>The current development direction focuses on:</p>
|
||||
<ul>
|
||||
<li>runtime modularization</li>
|
||||
<li>interface stabilization</li>
|
||||
<li>worker isolation improvements</li>
|
||||
<li>removal of legacy dependencies</li>
|
||||
<li>generic infrastructure abstraction</li>
|
||||
<li>reusable standalone integration architecture</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="active-development-areas">Active Development Areas</h1>
|
||||
<p>The project is currently evolving mainly in the following areas:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Area</th>
|
||||
<th>Current Focus</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Runtime</td>
|
||||
<td>Slot lifecycle cleanup and orchestration simplification</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Workers</td>
|
||||
<td>Isolation, serialization and cancellation improvements</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DataStorage</td>
|
||||
<td>Generic provider architecture and unified readers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Interfaces</td>
|
||||
<td>Stable external integration API</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Communication</td>
|
||||
<td>Separation of request and streaming infrastructure</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Architecture</td>
|
||||
<td>Reduction of TBF coupling</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Documentation</td>
|
||||
<td>Runtime and architectural transparency</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h1 id="long-term-goal">Long-Term Goal</h1>
|
||||
<p>The long-term goal is to transform GCI into:</p>
|
||||
<ul>
|
||||
<li>a reusable runtime platform</li>
|
||||
<li>independent integration middleware</li>
|
||||
<li>standalone communication infrastructure</li>
|
||||
<li>modular orchestration environment</li>
|
||||
<li>stable external API layer</li>
|
||||
</ul>
|
||||
<p>The future architecture should minimize direct dependencies on historical TBF-specific runtime assumptions while preserving compatibility with existing systems.</p>
|
||||
<hr>
|
||||
<h1 id="related-pages">Related Pages</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="page_dev__target_architecture.html">Target Architecture</a></td>
|
||||
<td>Planned future runtime architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_dev__migration.html">Migration</a></td>
|
||||
<td>Migration strategy and evolution path.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_dev__refactoring.html">Refactoring</a></td>
|
||||
<td>Refactoring activities and cleanup goals.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_dev__target_state.html">Ideas</a></td>
|
||||
<td>Future concepts and experimental directions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="../gci/page_gci__runtime.html">GCI Runtime</a></td>
|
||||
<td>Current runtime lifecycle implementation.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="../gci/page_gci__workers.html">Workers</a></td>
|
||||
<td>Worker execution architecture.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,213 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Continuous Development | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Continuous Development | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="continuous-development">Continuous Development</h1>
|
||||
|
||||
<h2 id="api-architecture-evolution">API Architecture Evolution</h2>
|
||||
<h3 id="current-state">Current State</h3>
|
||||
<p><img src="../../images/GCI__Onboarding_Overview_drawio__API_architecture__Current_state.svg" alt="Current State"></p>
|
||||
<h3 id="target-state">Target State</h3>
|
||||
<p><img src="../../images/GCI__Onboarding_Overview_drawio__API_architecture__Target_state.svg" alt="Target State"></p>
|
||||
<hr>
|
||||
<h2 id="architectural-context">Architectural Context</h2>
|
||||
<p>The diagrams above represent two stages of the Genesis Cordonel Interface (GCI) evolution.</p>
|
||||
<p>The current architecture was developed to support Genesis meter management within the TestBenchFramework environment. It provides the runtime infrastructure required for meter configuration, workflow execution, diagnostics, and firmware communication.</p>
|
||||
<p>The target architecture illustrates a possible future direction in which the same principles are extended beyond a single application environment, enabling a reusable and integration-independent platform for meter management and data acquisition.</p>
|
||||
<hr>
|
||||
<h2 id="why-api-architecture-comes-first">Why API Architecture Comes First</h2>
|
||||
<p>The primary architectural concern is not how meter communication is implemented internally, but how meter functionality is exposed to the surrounding software ecosystem.</p>
|
||||
<p>GCI operates within an environment composed of multiple existing applications, services, and runtime components. As a result, the architecture is evaluated from an API perspective before lower-level implementation details are considered.</p>
|
||||
<p>This approach emphasizes:</p>
|
||||
<ul>
|
||||
<li>clear integration boundaries</li>
|
||||
<li>stable external interfaces</li>
|
||||
<li>reusable functionality</li>
|
||||
<li>predictable behavior</li>
|
||||
<li>minimal coupling between systems</li>
|
||||
</ul>
|
||||
<p>The API architecture therefore defines the foundation on which all implementation decisions are built.</p>
|
||||
<hr>
|
||||
<h2 id="functional-domains">Functional Domains</h2>
|
||||
<p>From an API perspective, meter interaction can be divided into two independent domains.</p>
|
||||
<h3 id="configuration-domain">Configuration Domain</h3>
|
||||
<p>Responsible for active meter management through request-response communication:</p>
|
||||
<ul>
|
||||
<li>meter initialization</li>
|
||||
<li>login procedures</li>
|
||||
<li>register access</li>
|
||||
<li>parameter configuration</li>
|
||||
<li>calibration workflows</li>
|
||||
<li>diagnostics</li>
|
||||
</ul>
|
||||
<p>Communication is performed through:</p>
|
||||
<pre><code class="lang-text">RequestPort
|
||||
</code></pre>
|
||||
<h3 id="data-collection-domain">Data Collection Domain</h3>
|
||||
<p>Responsible for continuous acquisition of measurement data:</p>
|
||||
<ul>
|
||||
<li>measurement streaming</li>
|
||||
<li>runtime monitoring</li>
|
||||
<li>consumption data collection</li>
|
||||
<li>online diagnostics</li>
|
||||
</ul>
|
||||
<p>Communication is performed through:</p>
|
||||
<pre><code class="lang-text">StreamPort
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="current-state-1">Current State</h2>
|
||||
<p>The current GCI implementation focuses primarily on the Configuration Domain and provides a stable runtime architecture for meter management and workflow execution.</p>
|
||||
<p>The architecture was designed to satisfy the integration requirements of the TestBenchFramework environment developed by Sensus International Stará Turá.</p>
|
||||
<p>Core functionality currently includes:</p>
|
||||
<ul>
|
||||
<li>meter communication</li>
|
||||
<li>meter configuration</li>
|
||||
<li>firmware register access</li>
|
||||
<li>calibration workflows</li>
|
||||
<li>diagnostics</li>
|
||||
<li>runtime orchestration</li>
|
||||
<li>worker-based execution</li>
|
||||
</ul>
|
||||
<p>Streaming and measurement acquisition currently remain outside the main GCI runtime and are handled by dedicated external components.</p>
|
||||
<p>This approach allowed the project to establish a stable and reusable foundation while fulfilling the original project requirements.</p>
|
||||
<hr>
|
||||
<h2 id="target-state-1">Target State</h2>
|
||||
<p>The target architecture represents a potential future evolution of the current solution.</p>
|
||||
<p>The objective is to gradually extend the architecture beyond a single integration environment and move towards a unified platform capable of supporting both configuration and measurement acquisition within a common runtime framework.</p>
|
||||
<p>Potential benefits include:</p>
|
||||
<ul>
|
||||
<li>unified runtime infrastructure</li>
|
||||
<li>reusable communication abstractions</li>
|
||||
<li>simplified integration</li>
|
||||
<li>centralized diagnostics</li>
|
||||
<li>improved maintainability</li>
|
||||
<li>reduced duplication of functionality</li>
|
||||
</ul>
|
||||
<p>The target architecture should therefore be viewed as a long-term direction built upon the foundations established by the current implementation rather than as an immediate implementation goal.</p>
|
||||
<hr>
|
||||
<h2 id="development-philosophy">Development Philosophy</h2>
|
||||
<p>The purpose of the current development effort is not to directly achieve the target architecture.</p>
|
||||
<p>Instead, the goal is to establish architectural foundations that allow future evolution without major redesigns.</p>
|
||||
<p>For this reason, the current implementation should be viewed as the first practical realization of a broader architectural concept and an important milestone in the continuous evolution of GCI.</p>
|
||||
<hr>
|
||||
<h2 id="related-pages">Related Pages</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="page_dev__current_state.html">Current State</a></td>
|
||||
<td>Detailed description of the current implementation state.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_dev__target_architecture.html">Target Architecture</a></td>
|
||||
<td>Long-term architectural direction and goals.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_dev__migration.html">Migration</a></td>
|
||||
<td>Migration path between architectural phases.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_dev__refactoring.html">Refactoring</a></td>
|
||||
<td>Current refactoring activities and priorities.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="../gci/page_gci__home.html">GCI Documentation</a></td>
|
||||
<td>Runtime architecture and implementation details.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content=" | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content=" | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,372 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Target Architecture | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Target Architecture | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="target-architecture">Target Architecture</h1>
|
||||
|
||||
<h2 id="purpose">Purpose</h2>
|
||||
<p>This page describes the long-term architectural direction of Genesis Cordonel Interface (GCI).</p>
|
||||
<p>The goal is to gradually evolve GCI from a historically grown integration layer into a modular, maintainable and runtime-oriented platform.</p>
|
||||
<p>This document serves as:</p>
|
||||
<ul>
|
||||
<li>architecture vision reference</li>
|
||||
<li>refactoring direction guide</li>
|
||||
<li>migration planning reference</li>
|
||||
<li>engineering alignment document</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="current-architectural-challenges">Current Architectural Challenges</h1>
|
||||
<p>The current system evolved incrementally over time.</p>
|
||||
<p>As a result, several architectural challenges exist.</p>
|
||||
<h2 id="tight-coupling">Tight Coupling</h2>
|
||||
<p>Some runtime layers still contain mixed responsibilities:</p>
|
||||
<ul>
|
||||
<li>UI interaction</li>
|
||||
<li>orchestration</li>
|
||||
<li>runtime execution</li>
|
||||
<li>communication handling</li>
|
||||
<li>workflow execution</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="large-runtime-classes">Large Runtime Classes</h2>
|
||||
<p>Some classes currently act as orchestration hubs with many responsibilities.</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>InterfaceGCIToLaatzen</li>
|
||||
<li>runtime coordination layers</li>
|
||||
<li>workflow execution layers</li>
|
||||
</ul>
|
||||
<p>Target direction:</p>
|
||||
<ul>
|
||||
<li>smaller isolated runtime services</li>
|
||||
<li>dedicated orchestration layers</li>
|
||||
<li>cleaner responsibility boundaries</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="runtime-responsibility-mixing">Runtime Responsibility Mixing</h2>
|
||||
<p>Some components currently combine:</p>
|
||||
<ul>
|
||||
<li>communication logic</li>
|
||||
<li>business logic</li>
|
||||
<li>workflow execution</li>
|
||||
<li>diagnostics</li>
|
||||
<li>runtime state management</li>
|
||||
</ul>
|
||||
<p>Future architecture should isolate these concerns.</p>
|
||||
<hr>
|
||||
<h2 id="historical-tbf-dependencies">Historical TBF Dependencies</h2>
|
||||
<p>Some legacy TBF structures and design decisions are still present.</p>
|
||||
<p>Goal:</p>
|
||||
<ul>
|
||||
<li>isolate legacy dependencies</li>
|
||||
<li>gradually remove historical coupling</li>
|
||||
<li>improve modularity</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="long-term-architectural-vision">Long-Term Architectural Vision</h1>
|
||||
<p>The target architecture focuses on:</p>
|
||||
<ul>
|
||||
<li>runtime isolation</li>
|
||||
<li>modularity</li>
|
||||
<li>stable interfaces</li>
|
||||
<li>worker-based execution</li>
|
||||
<li>maintainability</li>
|
||||
<li>scalability</li>
|
||||
<li>reusable integration layers</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="target-runtime-architecture">Target Runtime Architecture</h1>
|
||||
<pre><code class="lang-text">External Applications
|
||||
↓
|
||||
Public API Layer
|
||||
↓
|
||||
Runtime Orchestration Layer
|
||||
↓
|
||||
Worker Execution Layer
|
||||
↓
|
||||
Runtime Services
|
||||
↓
|
||||
Communication Layer
|
||||
↓
|
||||
Firmware Access
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="architectural-layers">Architectural Layers</h1>
|
||||
<h2 id="public-api-layer">Public API Layer</h2>
|
||||
<p>Responsibilities:</p>
|
||||
<ul>
|
||||
<li>stable external interfaces</li>
|
||||
<li>request validation</li>
|
||||
<li>DTO mapping</li>
|
||||
<li>external integration</li>
|
||||
</ul>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>InterfaceOutsideToGCI</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="runtime-orchestration-layer">Runtime Orchestration Layer</h2>
|
||||
<p>Responsibilities:</p>
|
||||
<ul>
|
||||
<li>runtime coordination</li>
|
||||
<li>slot lifecycle management</li>
|
||||
<li>workflow orchestration</li>
|
||||
<li>runtime monitoring</li>
|
||||
</ul>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>InterfaceGCIToLaatzen</li>
|
||||
</ul>
|
||||
<p>Target direction:</p>
|
||||
<ul>
|
||||
<li>split orchestration responsibilities into smaller services</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="worker-execution-layer">Worker Execution Layer</h2>
|
||||
<p>Responsibilities:</p>
|
||||
<ul>
|
||||
<li>slot isolation</li>
|
||||
<li>operation serialization</li>
|
||||
<li>cancellation support</li>
|
||||
<li>runtime stability</li>
|
||||
</ul>
|
||||
<p>Goals:</p>
|
||||
<ul>
|
||||
<li>fully isolated slot execution</li>
|
||||
<li>predictable runtime behavior</li>
|
||||
<li>improved diagnostics</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="runtime-services">Runtime Services</h2>
|
||||
<p>Responsibilities:</p>
|
||||
<ul>
|
||||
<li>register access</li>
|
||||
<li>diagnostics</li>
|
||||
<li>workflow execution</li>
|
||||
<li>meter operations</li>
|
||||
</ul>
|
||||
<p>Target direction:</p>
|
||||
<ul>
|
||||
<li>smaller focused services</li>
|
||||
<li>reusable runtime components</li>
|
||||
<li>service isolation</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="communication-layer">Communication Layer</h2>
|
||||
<p>Responsibilities:</p>
|
||||
<ul>
|
||||
<li>serial communication</li>
|
||||
<li>request handling</li>
|
||||
<li>streaming communication</li>
|
||||
<li>firmware transport</li>
|
||||
</ul>
|
||||
<p>Goals:</p>
|
||||
<ul>
|
||||
<li>communication abstraction</li>
|
||||
<li>transport separation</li>
|
||||
<li>protocol isolation</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="datastorage-vision">DataStorage Vision</h1>
|
||||
<p>The DataStorage subsystem should evolve toward a provider-oriented architecture.</p>
|
||||
<p>Target goals:</p>
|
||||
<ul>
|
||||
<li>unified reader interfaces</li>
|
||||
<li>source abstraction</li>
|
||||
<li>interchangeable storage providers</li>
|
||||
<li>configuration-driven runtime selection</li>
|
||||
</ul>
|
||||
<p>Target providers:</p>
|
||||
<ul>
|
||||
<li>LocalDatabase</li>
|
||||
<li>RemoteDatabase</li>
|
||||
<li>CSV</li>
|
||||
<li>JSON</li>
|
||||
<li>REST API</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="runtime-isolation-goals">Runtime Isolation Goals</h1>
|
||||
<p>The runtime model should guarantee:</p>
|
||||
<ul>
|
||||
<li>slot isolation</li>
|
||||
<li>predictable execution</li>
|
||||
<li>no concurrent meter access</li>
|
||||
<li>worker ownership per slot</li>
|
||||
<li>cancellation-safe execution</li>
|
||||
</ul>
|
||||
<p>Target model:</p>
|
||||
<pre><code class="lang-text">Slot
|
||||
↓
|
||||
Dedicated Worker
|
||||
↓
|
||||
Dedicated Runtime Context
|
||||
↓
|
||||
Dedicated Meter Access
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="api-evolution-goals">API Evolution Goals</h1>
|
||||
<p>Public API goals:</p>
|
||||
<ul>
|
||||
<li>stable external contracts</li>
|
||||
<li>backward compatibility</li>
|
||||
<li>simplified integration</li>
|
||||
<li>isolated DTO models</li>
|
||||
<li>reduced external dependencies</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="diagnostics-vision">Diagnostics Vision</h1>
|
||||
<p>Future diagnostics should provide:</p>
|
||||
<ul>
|
||||
<li>runtime monitoring</li>
|
||||
<li>worker state inspection</li>
|
||||
<li>communication tracing</li>
|
||||
<li>execution timelines</li>
|
||||
<li>runtime statistics</li>
|
||||
</ul>
|
||||
<p>Potential future areas:</p>
|
||||
<ul>
|
||||
<li>runtime dashboards</li>
|
||||
<li>structured telemetry</li>
|
||||
<li>execution tracing</li>
|
||||
<li>performance monitoring</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="refactoring-direction">Refactoring Direction</h1>
|
||||
<p>Current refactoring priorities:</p>
|
||||
<ul>
|
||||
<li>isolate runtime layers</li>
|
||||
<li>reduce class responsibilities</li>
|
||||
<li>separate orchestration from execution</li>
|
||||
<li>reduce historical coupling</li>
|
||||
<li>improve interface separation</li>
|
||||
<li>improve documentation coverage</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="migration-philosophy">Migration Philosophy</h1>
|
||||
<p>Migration should be gradual.</p>
|
||||
<p>The system must remain operational during architecture evolution.</p>
|
||||
<p>Preferred strategy:</p>
|
||||
<pre><code class="lang-text">existing implementation
|
||||
↓
|
||||
introduce abstraction
|
||||
↓
|
||||
introduce new isolated component
|
||||
↓
|
||||
migrate usage gradually
|
||||
↓
|
||||
remove historical implementation
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="future-goals">Future Goals</h1>
|
||||
<p>Long-term target characteristics:</p>
|
||||
<ul>
|
||||
<li>modular runtime platform</li>
|
||||
<li>reusable integration framework</li>
|
||||
<li>scalable runtime execution</li>
|
||||
<li>maintainable architecture</li>
|
||||
<li>clear responsibility boundaries</li>
|
||||
<li>isolated runtime services</li>
|
||||
<li>improved onboarding</li>
|
||||
<li>strong engineering documentation</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="important-principle">Important Principle</h1>
|
||||
<p>Architecture evolution should prioritize:</p>
|
||||
<ul>
|
||||
<li>runtime stability</li>
|
||||
<li>maintainability</li>
|
||||
<li>clarity</li>
|
||||
<li>isolation</li>
|
||||
<li>incremental migration</li>
|
||||
</ul>
|
||||
<p>over large-scale rewrites.</p>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content=" | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,349 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Application Environment | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Application Environment | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="application-environment">Application Environment</h1>
|
||||
|
||||
<h2 id="purpose">Purpose</h2>
|
||||
<p>This page describes the application environment in which Genesis Cordonel Interface (GCI) operates.</p>
|
||||
<p>GCI does not exist as an isolated application. It operates inside a broader ecosystem of cooperating applications, runtime services, configuration components, diagnostic tools and meter-related technologies.</p>
|
||||
<hr>
|
||||
<h1 id="high-level-environment-overview">High-Level Environment Overview</h1>
|
||||
<pre><code class="lang-text">TBF Environment
|
||||
│
|
||||
├── User Interfaces
|
||||
├── Technologies
|
||||
├── Peripherals
|
||||
├── Test Methods
|
||||
├── Diagnostic Tools
|
||||
├── Data Readers/Writers
|
||||
├── Cooperating Devices
|
||||
│
|
||||
└── GciBridge
|
||||
↓
|
||||
GCI
|
||||
↓
|
||||
Meter Runtime
|
||||
↓
|
||||
Meter Firmware
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="environment-philosophy">Environment Philosophy</h1>
|
||||
<p>The TBF ecosystem is based on cooperating runtime components.</p>
|
||||
<p>Each component provides a specific runtime responsibility.</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>communication technologies</li>
|
||||
<li>peripheral integrations</li>
|
||||
<li>runtime tools</li>
|
||||
<li>diagnostics</li>
|
||||
<li>workflow execution</li>
|
||||
<li>test methods</li>
|
||||
<li>measurement processing</li>
|
||||
<li>data import/export</li>
|
||||
</ul>
|
||||
<p>Within this environment, GCI acts as the meter runtime communication and orchestration component.</p>
|
||||
<hr>
|
||||
<h1 id="main-environment-areas">Main Environment Areas</h1>
|
||||
<h2 id="user-interface-layer">User Interface Layer</h2>
|
||||
<p>Provides:</p>
|
||||
<ul>
|
||||
<li>operator interaction</li>
|
||||
<li>runtime visualization</li>
|
||||
<li>diagnostics</li>
|
||||
<li>workflow triggering</li>
|
||||
<li>configuration management</li>
|
||||
</ul>
|
||||
<p>Typical applications:</p>
|
||||
<ul>
|
||||
<li>CordonelToolBox</li>
|
||||
<li>PreadjustmentUI</li>
|
||||
<li>engineering tools</li>
|
||||
<li>diagnostic utilities</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="gci-integration-layer">GCI Integration Layer</h2>
|
||||
<p>Implemented through:</p>
|
||||
<pre><code class="lang-text">GciBridge
|
||||
</code></pre>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>integrate GCI into TBF</li>
|
||||
<li>expose runtime operations</li>
|
||||
<li>provide simplified access to meter infrastructure</li>
|
||||
<li>isolate TBF from GCI internals</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="gci-runtime-layer">GCI Runtime Layer</h2>
|
||||
<p>Provides:</p>
|
||||
<ul>
|
||||
<li>runtime orchestration</li>
|
||||
<li>worker execution</li>
|
||||
<li>slot management</li>
|
||||
<li>diagnostics</li>
|
||||
<li>communication handling</li>
|
||||
<li>workflow execution</li>
|
||||
</ul>
|
||||
<p>Main runtime interfaces:</p>
|
||||
<ul>
|
||||
<li>InterfaceOutsideToGCI</li>
|
||||
<li>InterfaceGCIToLaatzen</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="meter-runtime-layer">Meter Runtime Layer</h2>
|
||||
<p>Responsible for:</p>
|
||||
<ul>
|
||||
<li>meter communication</li>
|
||||
<li>firmware interaction</li>
|
||||
<li>register access</li>
|
||||
<li>PCB identification</li>
|
||||
<li>streaming communication</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="runtime-cooperation-model">Runtime Cooperation Model</h1>
|
||||
<p>The environment is cooperative rather than monolithic.</p>
|
||||
<p>Typical interaction flow:</p>
|
||||
<pre><code class="lang-text">UI Tool
|
||||
↓
|
||||
TBF Component Environment
|
||||
↓
|
||||
GciBridge
|
||||
↓
|
||||
GCI Runtime
|
||||
↓
|
||||
Worker Execution
|
||||
↓
|
||||
Meter Access
|
||||
</code></pre>
|
||||
<p>Each layer focuses on its own responsibility.</p>
|
||||
<hr>
|
||||
<h1 id="runtime-isolation">Runtime Isolation</h1>
|
||||
<p>The environment is designed to isolate runtime responsibilities.</p>
|
||||
<p>Examples:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Layer</th>
|
||||
<th>Responsibility</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>UI</td>
|
||||
<td>visualization and operator interaction</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TBF</td>
|
||||
<td>component orchestration</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GciBridge</td>
|
||||
<td>integration abstraction</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GCI</td>
|
||||
<td>runtime orchestration</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Workers</td>
|
||||
<td>execution isolation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Meter Runtime</td>
|
||||
<td>firmware communication</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h1 id="gci-as-a-tbf-component">GCI as a TBF Component</h1>
|
||||
<p>Within the TBF environment, GCI behaves as a cooperating runtime component.</p>
|
||||
<p>GCI provides:</p>
|
||||
<ul>
|
||||
<li>meter runtime access</li>
|
||||
<li>workflow execution</li>
|
||||
<li>diagnostics</li>
|
||||
<li>runtime state management</li>
|
||||
<li>communication infrastructure</li>
|
||||
</ul>
|
||||
<p>The integration is performed through:</p>
|
||||
<ul>
|
||||
<li>GciBridge</li>
|
||||
<li>GciBridgeCfgCtrl</li>
|
||||
<li>TBF component contracts</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="configuration-environment">Configuration Environment</h1>
|
||||
<p>The environment supports configurable runtime components.</p>
|
||||
<p>Example configuration responsibilities:</p>
|
||||
<ul>
|
||||
<li>communication setup</li>
|
||||
<li>slot configuration</li>
|
||||
<li>runtime parameters</li>
|
||||
<li>DataStorage setup</li>
|
||||
<li>diagnostics configuration</li>
|
||||
</ul>
|
||||
<p>Configuration integration is exposed through:</p>
|
||||
<pre><code class="lang-csharp">IComponentCfgCtrl
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="runtime-technologies">Runtime Technologies</h1>
|
||||
<p>The environment may contain multiple technologies cooperating together.</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>serial communication</li>
|
||||
<li>streaming communication</li>
|
||||
<li>firmware access</li>
|
||||
<li>calibration workflows</li>
|
||||
<li>measurement processing</li>
|
||||
<li>runtime diagnostics</li>
|
||||
</ul>
|
||||
<p>GCI acts as the central meter communication runtime within this ecosystem.</p>
|
||||
<hr>
|
||||
<h1 id="data-flow-model">Data Flow Model</h1>
|
||||
<p>Typical runtime data flow:</p>
|
||||
<pre><code class="lang-text">Measurement Source
|
||||
↓
|
||||
Meter Runtime
|
||||
↓
|
||||
GCI
|
||||
↓
|
||||
TBF Components
|
||||
↓
|
||||
Data Readers/Writers
|
||||
↓
|
||||
Storage / Visualization
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="long-term-direction">Long-Term Direction</h1>
|
||||
<p>The long-term goal is to evolve GCI into a more isolated and reusable runtime platform while preserving compatibility with the TBF component ecosystem.</p>
|
||||
<p>Future goals:</p>
|
||||
<ul>
|
||||
<li>modular runtime services</li>
|
||||
<li>improved isolation</li>
|
||||
<li>reusable integration interfaces</li>
|
||||
<li>provider-based architecture</li>
|
||||
<li>improved diagnostics</li>
|
||||
<li>scalable runtime orchestration</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="related-pages">Related Pages</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="page_gci__home.html">GCI Documentation</a></td>
|
||||
<td>Main onboarding and overview page.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__implementation_to_tbf.html">Implementation into TBF</a></td>
|
||||
<td>Integration of GCI into the TBF environment.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__internal_architecture.html">Internal Architecture</a></td>
|
||||
<td>Runtime layers and orchestration model.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__interfaces.html">Interfaces</a></td>
|
||||
<td>Public and internal GCI interfaces.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__workers.html">Workers</a></td>
|
||||
<td>Worker execution and isolation model.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content=" | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,320 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>GCI Documentation | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="GCI Documentation | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="gci-documentation">GCI Documentation</h1>
|
||||
|
||||
<h2 id="gci-onboarding-overview">GCI Onboarding Overview</h2>
|
||||
<p><a href="../../images/GCI__Onboarding_Overview_drawio.svg"><img src="../../images/GCI__Onboarding_Overview_drawio.svg" alt="GCI Overview"></a></p>
|
||||
<hr>
|
||||
<h1 id="what-is-gci">What is GCI?</h1>
|
||||
<p>Genesis Cordonel Interface (GCI) is a software layer responsible for:</p>
|
||||
<ul>
|
||||
<li>meter communication</li>
|
||||
<li>firmware interaction</li>
|
||||
<li>runtime slot management</li>
|
||||
<li>register access</li>
|
||||
<li>workflow execution</li>
|
||||
<li>diagnostics</li>
|
||||
<li>API integration</li>
|
||||
</ul>
|
||||
<p>GCI acts as an integration bridge between external applications and internal meter infrastructure.</p>
|
||||
<p>The architecture evolved from legacy TBF-based implementations toward a more modular and reusable runtime-oriented platform.</p>
|
||||
<hr>
|
||||
<h1 id="high-level-architecture">High-Level Architecture</h1>
|
||||
<pre><code class="lang-text">External Applications
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
MeterBatch
|
||||
↓
|
||||
GenesisMeter
|
||||
↓
|
||||
Firmware
|
||||
</code></pre>
|
||||
<p>The architecture intentionally separates:</p>
|
||||
<ul>
|
||||
<li>external API exposure</li>
|
||||
<li>runtime orchestration</li>
|
||||
<li>worker execution</li>
|
||||
<li>communication handling</li>
|
||||
<li>firmware interaction</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="main-runtime-responsibilities">Main Runtime Responsibilities</h1>
|
||||
<p>GCI provides runtime infrastructure for:</p>
|
||||
<ul>
|
||||
<li>slot initialization</li>
|
||||
<li>meter login</li>
|
||||
<li>meter connection management</li>
|
||||
<li>register read/write access</li>
|
||||
<li>PCB identification</li>
|
||||
<li>communication port detection</li>
|
||||
<li>diagnostics and monitoring</li>
|
||||
<li>worker execution isolation</li>
|
||||
<li>preadjustment workflow execution</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="main-areas">Main Areas</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Area</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="page_gci__app_environment.html">Application Environment</a></td>
|
||||
<td>External systems, runtime environment and hosting architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__interfaces.html">Interfaces</a></td>
|
||||
<td>Public and internal GCI interfaces.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__internal_architecture.html">Internal Architecture</a></td>
|
||||
<td>Internal runtime layers and orchestration model.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__runtime.html">Runtime</a></td>
|
||||
<td>Slot lifecycle and runtime behavior.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__workers.html">Workers</a></td>
|
||||
<td>Worker execution and threading model.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__datastorage.html">DataStorage</a></td>
|
||||
<td>Data source abstraction and reader architecture.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h1 id="runtime-philosophy">Runtime Philosophy</h1>
|
||||
<p>GCI is designed as a runtime-oriented system rather than a static communication library.</p>
|
||||
<p>Key architectural principles:</p>
|
||||
<ul>
|
||||
<li>isolated slot execution</li>
|
||||
<li>worker-based runtime model</li>
|
||||
<li>interface separation</li>
|
||||
<li>centralized orchestration</li>
|
||||
<li>runtime diagnostics</li>
|
||||
<li>reusable integration interfaces</li>
|
||||
<li>modular communication layers</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="typical-runtime-flow">Typical Runtime Flow</h1>
|
||||
<pre><code class="lang-text">Initialize Slot
|
||||
↓
|
||||
Login
|
||||
↓
|
||||
Connect
|
||||
↓
|
||||
Read/Write Registers
|
||||
↓
|
||||
Execute Workflows
|
||||
↓
|
||||
Disconnect
|
||||
↓
|
||||
Clean Slot
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="worker-based-execution-model">Worker-Based Execution Model</h1>
|
||||
<p>All slot operations are executed through dedicated workers.</p>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>prevent concurrent meter access</li>
|
||||
<li>isolate slot execution</li>
|
||||
<li>serialize operations</li>
|
||||
<li>provide cancellation support</li>
|
||||
<li>improve runtime stability</li>
|
||||
</ul>
|
||||
<p>Typical execution flow:</p>
|
||||
<pre><code class="lang-text">API Request
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
MeterBatch
|
||||
↓
|
||||
GenesisMeter
|
||||
↓
|
||||
Firmware
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="communication-model">Communication Model</h1>
|
||||
<p>GCI supports multiple communication channels.</p>
|
||||
<h2 id="request-port">Request Port</h2>
|
||||
<p>Used for:</p>
|
||||
<ul>
|
||||
<li>login</li>
|
||||
<li>register access</li>
|
||||
<li>PCB identification</li>
|
||||
<li>runtime commands</li>
|
||||
</ul>
|
||||
<h2 id="streaming-port">Streaming Port</h2>
|
||||
<p>Used for:</p>
|
||||
<ul>
|
||||
<li>measurement streaming</li>
|
||||
<li>online data acquisition</li>
|
||||
<li>continuous runtime monitoring</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="integration-philosophy">Integration Philosophy</h1>
|
||||
<p>GCI is intended to provide a stable and reusable integration layer for external systems.</p>
|
||||
<p>Typical integrations:</p>
|
||||
<ul>
|
||||
<li>TBF-based systems</li>
|
||||
<li>Lautzen solutions</li>
|
||||
<li>standalone UI applications</li>
|
||||
<li>diagnostic utilities</li>
|
||||
<li>calibration workflows</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="documentation-structure">Documentation Structure</h1>
|
||||
<p>This documentation is divided into two major areas.</p>
|
||||
<h2 id="gci-documentation-1">GCI Documentation</h2>
|
||||
<p>Describes:</p>
|
||||
<ul>
|
||||
<li>runtime architecture</li>
|
||||
<li>interfaces</li>
|
||||
<li>execution model</li>
|
||||
<li>workers</li>
|
||||
<li>communication</li>
|
||||
<li>workflows</li>
|
||||
</ul>
|
||||
<h2 id="continuous-development">Continuous Development</h2>
|
||||
<p>Describes:</p>
|
||||
<ul>
|
||||
<li>current development state</li>
|
||||
<li>architecture evolution</li>
|
||||
<li>migration goals</li>
|
||||
<li>refactoring activities</li>
|
||||
<li>future concepts</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="related-pages">Related Pages</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="page_gci__interfaces.html">Interfaces</a></td>
|
||||
<td>Public and internal API layers.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__internal_architecture.html">Internal Architecture</a></td>
|
||||
<td>Detailed runtime architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__workers.html">Workers</a></td>
|
||||
<td>Worker execution and isolation model.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__runtime.html">Runtime</a></td>
|
||||
<td>Runtime lifecycle and slot management.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__datastorage.html">DataStorage</a></td>
|
||||
<td>Data source abstraction architecture.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,138 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>GCI Implementation into TBF | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="GCI Implementation into TBF | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="gci-implementation-into-tbf">GCI Implementation into TBF</h1>
|
||||
|
||||
<h2 id="purpose">Purpose</h2>
|
||||
<p>This page describes how Genesis Cordonel Interface (GCI) is implemented into the TBF environment.</p>
|
||||
<p>TBF is based on a runtime environment of cooperating components. These components can represent interfaces, technologies, peripherals, cooperating devices, test methods, tools for reading or writing measured data, and other runtime services.</p>
|
||||
<p>GCI is integrated into this environment through the <code>GciBridge</code> component.</p>
|
||||
<p>The purpose of <code>GciBridge</code> is to make GCI available as a TBF-compatible cooperating component while hiding internal GCI runtime complexity.</p>
|
||||
<hr>
|
||||
<h2 id="tbf-component-environment">TBF Component Environment</h2>
|
||||
<p>TBF does not operate as a single isolated application. It acts as an environment where multiple cooperating components work together.</p>
|
||||
<p>Typical component types include:</p>
|
||||
<ul>
|
||||
<li>interfaces</li>
|
||||
<li>technologies</li>
|
||||
<li>peripherals</li>
|
||||
<li>cooperating devices</li>
|
||||
<li>test methods</li>
|
||||
<li>measurement data readers</li>
|
||||
<li>measurement data writers</li>
|
||||
<li>diagnostic tools</li>
|
||||
<li>calibration tools</li>
|
||||
<li>runtime services</li>
|
||||
</ul>
|
||||
<p>Within this environment, GCI becomes one of the cooperating components responsible for meter communication and runtime meter access.</p>
|
||||
<hr>
|
||||
<h2 id="position-of-gci-in-tbf">Position of GCI in TBF</h2>
|
||||
<pre><code class="lang-text">TBF Runtime Environment
|
||||
│
|
||||
├── Interfaces
|
||||
├── Technologies
|
||||
├── Peripherals
|
||||
├── Cooperating Devices
|
||||
├── Test Methods
|
||||
├── Data Readers / Writers
|
||||
├── Diagnostic Tools
|
||||
│
|
||||
└── GciBridge
|
||||
↓
|
||||
GCI
|
||||
↓
|
||||
Meter Runtime
|
||||
↓
|
||||
Meter Firmware
|
||||
</code></pre>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content=" | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content=" | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content=" | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content=" | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,353 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Workers | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Workers | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="workers">Workers</h1>
|
||||
|
||||
<h2 id="purpose">Purpose</h2>
|
||||
<p>This page describes the worker-based execution model used inside Genesis Cordonel Interface (GCI).</p>
|
||||
<p>Workers are one of the core runtime concepts of GCI.</p>
|
||||
<p>Their purpose is to isolate runtime execution, serialize meter operations and provide stable communication behavior.</p>
|
||||
<hr>
|
||||
<h1 id="why-workers-exist">Why Workers Exist</h1>
|
||||
<p>Meter communication requires controlled execution.</p>
|
||||
<p>Direct concurrent access to meters can cause:</p>
|
||||
<ul>
|
||||
<li>communication corruption</li>
|
||||
<li>unstable runtime behavior</li>
|
||||
<li>protocol conflicts</li>
|
||||
<li>invalid firmware state</li>
|
||||
<li>race conditions</li>
|
||||
<li>unpredictable execution timing</li>
|
||||
</ul>
|
||||
<p>The worker model exists to prevent these problems.</p>
|
||||
<hr>
|
||||
<h1 id="worker-philosophy">Worker Philosophy</h1>
|
||||
<p>The runtime model follows these principles:</p>
|
||||
<ul>
|
||||
<li>one worker controls one execution context</li>
|
||||
<li>operations are serialized</li>
|
||||
<li>slot execution is isolated</li>
|
||||
<li>communication ownership is controlled</li>
|
||||
<li>asynchronous execution is supported</li>
|
||||
<li>cancellation is supported</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="high-level-worker-architecture">High-Level Worker Architecture</h1>
|
||||
<pre><code class="lang-text">External Request
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
MeterBatch
|
||||
↓
|
||||
GenesisMeter
|
||||
↓
|
||||
Firmware
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="runtime-isolation-model">Runtime Isolation Model</h1>
|
||||
<p>Each worker owns its execution context.</p>
|
||||
<p>Target runtime model:</p>
|
||||
<pre><code class="lang-text">Slot
|
||||
↓
|
||||
Dedicated Worker
|
||||
↓
|
||||
Dedicated Runtime State
|
||||
↓
|
||||
Dedicated Meter Access
|
||||
</code></pre>
|
||||
<p>This prevents overlapping access to the same meter runtime.</p>
|
||||
<hr>
|
||||
<h1 id="main-worker-responsibilities">Main Worker Responsibilities</h1>
|
||||
<p>Workers are responsible for:</p>
|
||||
<ul>
|
||||
<li>runtime execution isolation</li>
|
||||
<li>operation serialization</li>
|
||||
<li>asynchronous task execution</li>
|
||||
<li>cancellation handling</li>
|
||||
<li>execution queue processing</li>
|
||||
<li>communication ownership</li>
|
||||
<li>runtime stability</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="execution-flow">Execution Flow</h1>
|
||||
<p>Typical execution flow:</p>
|
||||
<pre><code class="lang-text">API Request
|
||||
↓
|
||||
Validation
|
||||
↓
|
||||
Worker Queue
|
||||
↓
|
||||
Serialized Execution
|
||||
↓
|
||||
Meter Operation
|
||||
↓
|
||||
Result Propagation
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="serialized-execution">Serialized Execution</h1>
|
||||
<p>Only one operation should actively access a meter runtime context at a time.</p>
|
||||
<p>Workers ensure:</p>
|
||||
<ul>
|
||||
<li>predictable communication</li>
|
||||
<li>stable protocol execution</li>
|
||||
<li>safe firmware interaction</li>
|
||||
<li>controlled runtime state</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="asynchronous-runtime-model">Asynchronous Runtime Model</h1>
|
||||
<p>Worker operations are asynchronous.</p>
|
||||
<p>Typical execution pattern:</p>
|
||||
<pre><code class="lang-csharp">await api.ConnectOneSlotAsync(slot);
|
||||
</code></pre>
|
||||
<p>Internally:</p>
|
||||
<pre><code class="lang-text">API Layer
|
||||
↓
|
||||
Worker Queue
|
||||
↓
|
||||
Serialized Execution
|
||||
↓
|
||||
Communication Layer
|
||||
</code></pre>
|
||||
<p>This prevents UI blocking and improves runtime responsiveness.</p>
|
||||
<hr>
|
||||
<h1 id="worker-queue-model">Worker Queue Model</h1>
|
||||
<p>Workers internally process queued operations.</p>
|
||||
<p>Typical queue responsibilities:</p>
|
||||
<ul>
|
||||
<li>pending requests</li>
|
||||
<li>execution ordering</li>
|
||||
<li>cancellation tracking</li>
|
||||
<li>runtime synchronization</li>
|
||||
</ul>
|
||||
<p>Conceptually:</p>
|
||||
<pre><code class="lang-text">Request Queue
|
||||
↓
|
||||
Worker Execution Loop
|
||||
↓
|
||||
Meter Operation
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="cancellation-support">Cancellation Support</h1>
|
||||
<p>Workers support cancellation-aware execution.</p>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>stop long-running operations</li>
|
||||
<li>interrupt workflows</li>
|
||||
<li>improve runtime control</li>
|
||||
<li>avoid deadlocks</li>
|
||||
</ul>
|
||||
<p>Typical sources:</p>
|
||||
<ul>
|
||||
<li>UI cancellation</li>
|
||||
<li>runtime shutdown</li>
|
||||
<li>communication timeout handling</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="communication-ownership">Communication Ownership</h1>
|
||||
<p>Workers own communication access during execution.</p>
|
||||
<p>This prevents:</p>
|
||||
<ul>
|
||||
<li>simultaneous serial port access</li>
|
||||
<li>overlapping register operations</li>
|
||||
<li>protocol corruption</li>
|
||||
<li>inconsistent runtime state</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="worker-isolation-benefits">Worker Isolation Benefits</h1>
|
||||
<p>The worker model provides:</p>
|
||||
<ul>
|
||||
<li>stable runtime behavior</li>
|
||||
<li>predictable execution order</li>
|
||||
<li>safer firmware access</li>
|
||||
<li>easier diagnostics</li>
|
||||
<li>asynchronous scalability</li>
|
||||
<li>controlled communication access</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="worker-diagnostics">Worker Diagnostics</h1>
|
||||
<p>Workers expose runtime diagnostic information.</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>active operation</li>
|
||||
<li>queue state</li>
|
||||
<li>execution status</li>
|
||||
<li>activity timestamps</li>
|
||||
<li>cancellation state</li>
|
||||
</ul>
|
||||
<p>Diagnostic access is exposed through:</p>
|
||||
<ul>
|
||||
<li>GetWorkerDebugStatuses()</li>
|
||||
<li>runtime diagnostics APIs</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="worker-and-slot-relationship">Worker and Slot Relationship</h1>
|
||||
<p>Workers are closely connected to slot runtime management.</p>
|
||||
<p>Typical relationship:</p>
|
||||
<pre><code class="lang-text">Slot
|
||||
↓
|
||||
Worker
|
||||
↓
|
||||
Meter Runtime
|
||||
↓
|
||||
Firmware Access
|
||||
</code></pre>
|
||||
<p>Each slot runtime should have controlled worker ownership.</p>
|
||||
<hr>
|
||||
<h1 id="runtime-stability-goals">Runtime Stability Goals</h1>
|
||||
<p>The worker architecture is designed to improve:</p>
|
||||
<ul>
|
||||
<li>runtime stability</li>
|
||||
<li>communication reliability</li>
|
||||
<li>execution predictability</li>
|
||||
<li>firmware safety</li>
|
||||
<li>maintainability</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="long-term-direction">Long-Term Direction</h1>
|
||||
<p>Future worker architecture goals:</p>
|
||||
<ul>
|
||||
<li>cleaner worker isolation</li>
|
||||
<li>dedicated runtime contexts</li>
|
||||
<li>improved diagnostics</li>
|
||||
<li>better execution tracing</li>
|
||||
<li>runtime telemetry</li>
|
||||
<li>improved cancellation handling</li>
|
||||
<li>scalable execution infrastructure</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="important-principle">Important Principle</h1>
|
||||
<p>Workers should execute runtime logic.</p>
|
||||
<p>Workers should NOT:</p>
|
||||
<ul>
|
||||
<li>contain UI logic</li>
|
||||
<li>contain visualization logic</li>
|
||||
<li>contain application-specific workflows</li>
|
||||
</ul>
|
||||
<p>Their responsibility is controlled runtime execution.</p>
|
||||
<hr>
|
||||
<h1 id="related-pages">Related Pages</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="page_gci__runtime.html">Runtime</a></td>
|
||||
<td>Slot lifecycle and runtime behavior.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__internal_architecture.html">Internal Architecture</a></td>
|
||||
<td>Runtime orchestration and layering.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__interfaces.html">Interfaces</a></td>
|
||||
<td>Public and internal GCI interfaces.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_gci__implementation_to_tbf.html">Implementation into TBF</a></td>
|
||||
<td>Integration of GCI into the TBF environment.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="../development/page_dev__target_architecture.html">Target Architecture</a></td>
|
||||
<td>Long-term architecture goals.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,214 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Configuration Domain | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Configuration Domain | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="configuration-domain">Configuration Domain</h1>
|
||||
|
||||
<p><img src="../../images/GCI__Onboarding_Overview_drawio__Meter_management_domain.svg" alt="Current State"></p>
|
||||
<h2 id="overview">Overview</h2>
|
||||
<p>The Configuration Domain is responsible for active meter management through request-response communication.</p>
|
||||
<p>Its purpose is to provide controlled access to meter functionality exposed by the firmware. Operations are initiated by the application and executed through explicit requests sent to the meter.</p>
|
||||
<p>This domain is used whenever meter configuration, diagnostics, calibration, or maintenance operations are required.</p>
|
||||
<hr>
|
||||
<h2 id="communication-model">Communication Model</h2>
|
||||
<p>The Configuration Domain operates through a request-response communication channel.</p>
|
||||
<pre><code class="lang-text">Application
|
||||
│
|
||||
▼
|
||||
Configuration Domain
|
||||
│
|
||||
▼
|
||||
RequestPort
|
||||
│
|
||||
▼
|
||||
Meter
|
||||
</code></pre>
|
||||
<p>Each operation is initiated by the application, transmitted to the meter, and completed after a corresponding response is received.</p>
|
||||
<p>Typical examples include:</p>
|
||||
<ul>
|
||||
<li>reading firmware registers</li>
|
||||
<li>writing configuration parameters</li>
|
||||
<li>executing calibration procedures</li>
|
||||
<li>meter diagnostics</li>
|
||||
<li>maintenance operations</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="current-implementation">Current Implementation</h2>
|
||||
<p>The current implementation is based on the Genesis Cordonel Interface (GCI).</p>
|
||||
<p>GCI provides a runtime environment that exposes meter functionality through a consistent programming interface while separating application logic from low-level communication details.</p>
|
||||
<p>The implementation consists of several logical layers.</p>
|
||||
<pre><code class="lang-text">Application
|
||||
│
|
||||
▼
|
||||
GciBridge
|
||||
│
|
||||
▼
|
||||
GCI
|
||||
│
|
||||
▼
|
||||
Laatzen Libraries
|
||||
│
|
||||
▼
|
||||
RequestPort
|
||||
│
|
||||
▼
|
||||
Meter
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="gci">GCI</h2>
|
||||
<p>Genesis Cordonel Interface (GCI) serves as the central runtime component of the Configuration Domain.</p>
|
||||
<p>Its primary responsibility is orchestration of meter-related operations.</p>
|
||||
<p>GCI provides:</p>
|
||||
<ul>
|
||||
<li>runtime management</li>
|
||||
<li>worker execution</li>
|
||||
<li>interface abstraction</li>
|
||||
<li>configuration handling</li>
|
||||
<li>execution flow control</li>
|
||||
<li>communication coordination</li>
|
||||
</ul>
|
||||
<p>GCI does not directly implement all meter-specific functionality. Instead, it provides the infrastructure through which such functionality can be executed.</p>
|
||||
<hr>
|
||||
<h2 id="laatzen-libraries">Laatzen Libraries</h2>
|
||||
<p>Meter-specific functionality is implemented within dedicated action and type libraries originating from the Laatzen project.</p>
|
||||
<p>These libraries contain:</p>
|
||||
<ul>
|
||||
<li>meter commands</li>
|
||||
<li>protocol implementations</li>
|
||||
<li>calibration procedures</li>
|
||||
<li>firmware interactions</li>
|
||||
<li>meter-specific data structures</li>
|
||||
</ul>
|
||||
<p>GCI exposes these capabilities through a unified runtime architecture.</p>
|
||||
<p>This separation allows meter functionality to remain independent from application-specific implementations.</p>
|
||||
<hr>
|
||||
<h2 id="gcibridge">GciBridge</h2>
|
||||
<p>Within the current TestBenchFramework environment, GCI is accessed through the GciBridge component.</p>
|
||||
<p>GciBridge acts as an integration layer between the application and the GCI runtime.</p>
|
||||
<p>Responsibilities include:</p>
|
||||
<ul>
|
||||
<li>exposing GCI functionality to TBF</li>
|
||||
<li>adapting application workflows</li>
|
||||
<li>simplifying integration</li>
|
||||
<li>isolating TBF from internal GCI implementation details</li>
|
||||
</ul>
|
||||
<p>As a result, the application interacts with GciBridge while GCI remains responsible for runtime orchestration and communication management.</p>
|
||||
<hr>
|
||||
<h2 id="architectural-characteristics">Architectural Characteristics</h2>
|
||||
<p>The current Configuration Domain architecture provides:</p>
|
||||
<ul>
|
||||
<li>clear separation between application logic and communication logic</li>
|
||||
<li>reusable runtime infrastructure</li>
|
||||
<li>modular meter functionality</li>
|
||||
<li>application-independent communication services</li>
|
||||
<li>simplified integration into external environments</li>
|
||||
</ul>
|
||||
<p>This architecture allows meter functionality to evolve independently from the applications that consume it.</p>
|
||||
<hr>
|
||||
<h2 id="summary">Summary</h2>
|
||||
<p>The Configuration Domain currently consists of the following major components:</p>
|
||||
<pre><code class="lang-text">Configuration Domain
|
||||
|
||||
├── GciBridge
|
||||
│
|
||||
├── GCI
|
||||
│ ├── Runtime
|
||||
│ ├── Workers
|
||||
│ ├── Interfaces
|
||||
│ └── DataStorage
|
||||
│
|
||||
└── Laatzen Libraries
|
||||
├── Actions
|
||||
└── Types
|
||||
</code></pre>
|
||||
<p>Together, these components provide the complete request-response infrastructure required for Genesis meter configuration, diagnostics, calibration, and maintenance operations.</p>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,272 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>GCI Documentation | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="GCI Documentation | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="gci-documentation">GCI Documentation</h1>
|
||||
|
||||
<h2 id="gci-onboarding-overview">GCI Onboarding Overview</h2>
|
||||
<p><a href="../../images/GCI__Onboarding_Overview_drawio.svg"><img src="../../images/GCI__Onboarding_Overview_drawio.svg" alt="GCI Overview"></a></p>
|
||||
<hr>
|
||||
<h1 id="what-is-gci">What is GCI?</h1>
|
||||
<p>Genesis Cordonel Interface (GCI) is a software layer responsible for:</p>
|
||||
<ul>
|
||||
<li>meter communication</li>
|
||||
<li>firmware interaction</li>
|
||||
<li>runtime slot management</li>
|
||||
<li>register access</li>
|
||||
<li>workflow execution</li>
|
||||
<li>diagnostics</li>
|
||||
<li>API integration</li>
|
||||
</ul>
|
||||
<p>GCI acts as an integration bridge between external applications and internal meter infrastructure.</p>
|
||||
<p>The architecture evolved from legacy TBF-based implementations toward a more modular and reusable runtime-oriented platform.</p>
|
||||
<hr>
|
||||
<h1 id="high-level-architecture">High-Level Architecture</h1>
|
||||
<pre><code class="lang-text">External Applications
|
||||
↓
|
||||
InterfaceOutsideToGCI
|
||||
↓
|
||||
InterfaceGCIToLaatzen
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
MeterBatch
|
||||
↓
|
||||
GenesisMeter
|
||||
↓
|
||||
Firmware
|
||||
</code></pre>
|
||||
<p>The architecture intentionally separates:</p>
|
||||
<ul>
|
||||
<li>external API exposure</li>
|
||||
<li>runtime orchestration</li>
|
||||
<li>worker execution</li>
|
||||
<li>communication handling</li>
|
||||
<li>firmware interaction</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="main-runtime-responsibilities">Main Runtime Responsibilities</h1>
|
||||
<p>GCI provides runtime infrastructure for:</p>
|
||||
<ul>
|
||||
<li>slot initialization</li>
|
||||
<li>meter login</li>
|
||||
<li>meter connection management</li>
|
||||
<li>register read/write access</li>
|
||||
<li>PCB identification</li>
|
||||
<li>communication port detection</li>
|
||||
<li>diagnostics and monitoring</li>
|
||||
<li>worker execution isolation</li>
|
||||
<li>preadjustment workflow execution</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="main-areas">Main Areas</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Area</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<hr>
|
||||
<h1 id="runtime-philosophy">Runtime Philosophy</h1>
|
||||
<p>GCI is designed as a runtime-oriented system rather than a static communication library.</p>
|
||||
<p>Key architectural principles:</p>
|
||||
<ul>
|
||||
<li>isolated slot execution</li>
|
||||
<li>worker-based runtime model</li>
|
||||
<li>interface separation</li>
|
||||
<li>centralized orchestration</li>
|
||||
<li>runtime diagnostics</li>
|
||||
<li>reusable integration interfaces</li>
|
||||
<li>modular communication layers</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="typical-runtime-flow">Typical Runtime Flow</h1>
|
||||
<pre><code class="lang-text">Initialize Slot
|
||||
↓
|
||||
Login
|
||||
↓
|
||||
Connect
|
||||
↓
|
||||
Read/Write Registers
|
||||
↓
|
||||
Execute Workflows
|
||||
↓
|
||||
Disconnect
|
||||
↓
|
||||
Clean Slot
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="worker-based-execution-model">Worker-Based Execution Model</h1>
|
||||
<p>All slot operations are executed through dedicated workers.</p>
|
||||
<p>Purpose:</p>
|
||||
<ul>
|
||||
<li>prevent concurrent meter access</li>
|
||||
<li>isolate slot execution</li>
|
||||
<li>serialize operations</li>
|
||||
<li>provide cancellation support</li>
|
||||
<li>improve runtime stability</li>
|
||||
</ul>
|
||||
<p>Typical execution flow:</p>
|
||||
<pre><code class="lang-text">API Request
|
||||
↓
|
||||
ApiWorker
|
||||
↓
|
||||
MeterBatch
|
||||
↓
|
||||
GenesisMeter
|
||||
↓
|
||||
Firmware
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h1 id="communication-model">Communication Model</h1>
|
||||
<p>GCI supports multiple communication channels.</p>
|
||||
<h2 id="request-port">Request Port</h2>
|
||||
<p>Used for:</p>
|
||||
<ul>
|
||||
<li>login</li>
|
||||
<li>register access</li>
|
||||
<li>PCB identification</li>
|
||||
<li>runtime commands</li>
|
||||
</ul>
|
||||
<h2 id="streaming-port">Streaming Port</h2>
|
||||
<p>Used for:</p>
|
||||
<ul>
|
||||
<li>measurement streaming</li>
|
||||
<li>online data acquisition</li>
|
||||
<li>continuous runtime monitoring</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="integration-philosophy">Integration Philosophy</h1>
|
||||
<p>GCI is intended to provide a stable and reusable integration layer for external systems.</p>
|
||||
<p>Typical integrations:</p>
|
||||
<ul>
|
||||
<li>TBF-based systems</li>
|
||||
<li>Lautzen solutions</li>
|
||||
<li>standalone UI applications</li>
|
||||
<li>diagnostic utilities</li>
|
||||
<li>calibration workflows</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="documentation-structure">Documentation Structure</h1>
|
||||
<p>This documentation is divided into two major areas.</p>
|
||||
<h2 id="gci-documentation-1">GCI Documentation</h2>
|
||||
<p>Describes:</p>
|
||||
<ul>
|
||||
<li>runtime architecture</li>
|
||||
<li>interfaces</li>
|
||||
<li>execution model</li>
|
||||
<li>workers</li>
|
||||
<li>communication</li>
|
||||
<li>workflows</li>
|
||||
</ul>
|
||||
<h2 id="continuous-development">Continuous Development</h2>
|
||||
<p>Describes:</p>
|
||||
<ul>
|
||||
<li>current development state</li>
|
||||
<li>architecture evolution</li>
|
||||
<li>migration goals</li>
|
||||
<li>refactoring activities</li>
|
||||
<li>future concepts</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h1 id="related-pages">Related Pages</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Data Collection Domain | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Data Collection Domain | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="data-collection-domain">Data Collection Domain</h1>
|
||||
|
||||
<p><img src="../../images/GCI__Onboarding_Overview_drawio__Data_collection_domain.svg" alt="Current State"></p>
|
||||
<h2 id="overview">Overview</h2>
|
||||
<p>The Data Collection Domain is responsible for acquiring and processing measurement data generated by Genesis meters.</p>
|
||||
<p>Unlike the Configuration Domain, which actively communicates with the meter through request-response operations, the Data Collection Domain continuously receives measurement data transmitted by the meter.</p>
|
||||
<h2 id="current-implementation">Current Implementation</h2>
|
||||
<p>The current implementation of the Data Collection Domain is based on the SmartRegisterReader component.</p>
|
||||
<p>SmartRegisterReader is responsible for acquiring and evaluating measurement data after the meter has been configured and a measurement process has been started.</p>
|
||||
<p>Each initialized meter is associated with its own dedicated SmartRegisterReader instance. This allows multiple meters to be monitored simultaneously while keeping measurement acquisition independent for each device.</p>
|
||||
<p>Every SmartRegisterReader instance maintains direct access to the communication channel associated with the corresponding meter. Measurement data is received through the meter streaming serial interface, which is exposed to the operating system as a dedicated COM port.</p>
|
||||
<p>In the current TestBenchFramework environment, the physical serial communication is provided through MOXA serial device servers, which convert the meter's streaming serial connection into a network-accessible COM port interface.</p>
|
||||
<p>At the current stage of development, SmartRegisterReader operates independently from the Configuration Domain.</p>
|
||||
<h2 id="relationship-to-the-configuration-domain">Relationship to the Configuration Domain</h2>
|
||||
<p>The Genesis Meter Platform currently consists of two independent functional domains.</p>
|
||||
<p>The Configuration Domain is responsible for meter management, configuration, calibration, and diagnostics through request-response communication.</p>
|
||||
<p>The Data Collection Domain is responsible for measurement acquisition and monitoring through the streaming communication interface.</p>
|
||||
<p>Together, these domains provide complete access to Genesis meter functionality.</p>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,172 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Genesis Meter Platform | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Genesis Meter Platform | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="genesis-meter-platform">Genesis Meter Platform</h1>
|
||||
|
||||
<p>The Genesis Meter Platform represents the complete software ecosystem used for communication, configuration, monitoring, and data acquisition from Genesis meters.</p>
|
||||
<p>The platform currently consists of two primary functional domains:</p>
|
||||
<ul>
|
||||
<li>Configuration Domain</li>
|
||||
<li>Data Collection Domain</li>
|
||||
</ul>
|
||||
<p>Together, these domains provide the complete functionality required for meter interaction throughout development, testing, calibration, diagnostics, and measurement acquisition workflows.</p>
|
||||
<hr>
|
||||
<h2 id="platform-overview">Platform Overview</h2>
|
||||
<pre><code class="lang-text">Genesis Meter Platform
|
||||
|
||||
├── Configuration Domain
|
||||
│ ├── GciBridge
|
||||
│ ├── GCI
|
||||
│ └── Laatzen Libraries
|
||||
│
|
||||
└── Data Collection Domain
|
||||
└── SmartRegisterReader
|
||||
</code></pre>
|
||||
<h3 id="configuration-domain">Configuration Domain</h3>
|
||||
<p>The Configuration Domain provides active meter management through request-response communication.</p>
|
||||
<p>Its primary purpose is to execute operations that require direct interaction with meter firmware, including:</p>
|
||||
<ul>
|
||||
<li>meter initialization</li>
|
||||
<li>login procedures</li>
|
||||
<li>firmware register access</li>
|
||||
<li>parameter configuration</li>
|
||||
<li>calibration workflows</li>
|
||||
<li>diagnostics</li>
|
||||
</ul>
|
||||
<p>The central runtime component of this domain is the Genesis Cordonel Interface (GCI), which provides a unified programming interface and runtime environment for meter-related operations.</p>
|
||||
<p>Within the current production environment, GCI is integrated into TestBenchFramework through the GciBridge component.</p>
|
||||
<h3 id="data-collection-domain">Data Collection Domain</h3>
|
||||
<p>The Data Collection Domain provides continuous acquisition and processing of measurement data transmitted by the meter.</p>
|
||||
<p>Its primary responsibilities include:</p>
|
||||
<ul>
|
||||
<li>stream acquisition</li>
|
||||
<li>measurement extraction</li>
|
||||
<li>data evaluation</li>
|
||||
<li>runtime monitoring</li>
|
||||
<li>diagnostics</li>
|
||||
</ul>
|
||||
<p>The central component of this domain is SmartRegisterReader, which independently handles streaming communication and measurement processing.</p>
|
||||
<hr>
|
||||
<h2 id="architectural-principle">Architectural Principle</h2>
|
||||
<p>The platform is intentionally divided into two independent domains.</p>
|
||||
<p>This separation allows configuration-related functionality and measurement-data processing to evolve independently while maintaining clear responsibility boundaries and reducing runtime complexity.</p>
|
||||
<p>Although implemented as separate solutions, both domains serve a common purpose: providing complete access to Genesis meter functionality.</p>
|
||||
<hr>
|
||||
<h2 id="related-pages">Related Pages</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="page_platform__current_state.html">Current State</a></td>
|
||||
<td>Current platform architecture and implementation.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_platform__configuration_domain.html">Configuration Domain</a></td>
|
||||
<td>Meter configuration and management architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_platform__data_collection_domain.html">Data Collection Domain</a></td>
|
||||
<td>Measurement acquisition and processing architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_platform__tbf_implementation.html">TBF Implementation</a></td>
|
||||
<td>Current integration of the platform into TestBenchFramework.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,193 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>TBF Implementation | Genesis Cordonel Interface </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="TBF Implementation | Genesis Cordonel Interface ">
|
||||
|
||||
|
||||
<link rel="icon" href="../../favicon.ico">
|
||||
<link rel="stylesheet" href="../../public/docfx.min.css">
|
||||
<link rel="stylesheet" href="../../public/main.css">
|
||||
<meta name="docfx:navrel" content="../../toc.html">
|
||||
<meta name="docfx:tocrel" content="../../toc.html">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta name="loc:inThisArticle" content="In this article">
|
||||
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
||||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||||
<meta name="loc:tocFilter" content="Filter by title">
|
||||
<meta name="loc:nextArticle" content="Next">
|
||||
<meta name="loc:prevArticle" content="Previous">
|
||||
<meta name="loc:themeLight" content="Light">
|
||||
<meta name="loc:themeDark" content="Dark">
|
||||
<meta name="loc:themeAuto" content="Auto">
|
||||
<meta name="loc:changeTheme" content="Change theme">
|
||||
<meta name="loc:copy" content="Copy">
|
||||
<meta name="loc:downloadPdf" content="Download PDF">
|
||||
|
||||
<script type="module" src="./../../public/docfx.min.js"></script>
|
||||
|
||||
<script>
|
||||
const theme = localStorage.getItem('theme') || 'auto'
|
||||
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
||||
<header class="bg-body border-bottom">
|
||||
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
||||
<div class="container-xxl flex-nowrap">
|
||||
<a class="navbar-brand" href="../../index.html">
|
||||
<img id="logo" class="svg" src="../../images/logo/logo.svg" alt="">
|
||||
|
||||
</a>
|
||||
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="bi bi-three-dots"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navpanel">
|
||||
<div id="navbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-xxl">
|
||||
|
||||
<div class="content">
|
||||
<div class="actionbar">
|
||||
|
||||
<nav id="breadcrumb"></nav>
|
||||
</div>
|
||||
|
||||
<article data-uid="">
|
||||
<h1 id="tbf-implementation">TBF Implementation</h1>
|
||||
|
||||
<h2 id="overview">Overview</h2>
|
||||
<p>The current production implementation of the Genesis Meter Platform is integrated into the TestBenchFramework (TBF) environment.</p>
|
||||
<p>This implementation combines the Configuration Domain and the Data Collection Domain into a single application environment while keeping their responsibilities separated.</p>
|
||||
<p>The objective is to provide a complete set of tools required for meter configuration, calibration, diagnostics, testing, and measurement acquisition.</p>
|
||||
<hr>
|
||||
<h2 id="high-level-architecture">High-Level Architecture</h2>
|
||||
<pre><code class="lang-text">TestBenchFramework
|
||||
|
||||
├── Configuration Domain
|
||||
│ └── GciBridge (TBF Component)
|
||||
│ └── GCI (external library)
|
||||
│ └── Laatzen Libraries (external libraries)
|
||||
│ └── requestingPort
|
||||
└── Data Collection Domain
|
||||
└── SmartRegisterReader (TBF Component)
|
||||
└── streamingPort
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h2 id="configuration-domain-integration">Configuration Domain Integration</h2>
|
||||
<p>The Configuration Domain is integrated into TestBenchFramework through the GciBridge component.</p>
|
||||
<p>GciBridge acts as an adapter layer between the TestBenchFramework application and the Genesis Cordonel Interface (GCI).</p>
|
||||
<p>Its primary responsibilities include:</p>
|
||||
<ul>
|
||||
<li>exposing meter operations to TBF</li>
|
||||
<li>translating application workflows into GCI requests</li>
|
||||
<li>managing runtime interaction with GCI</li>
|
||||
<li>isolating TBF from internal GCI implementation details</li>
|
||||
</ul>
|
||||
<p>The actual meter functionality is provided by GCI and the underlying Laatzen libraries.</p>
|
||||
<p>This architecture allows TestBenchFramework to access meter configuration capabilities through a stable and application-specific interface while preserving the independence of the GCI runtime.</p>
|
||||
<hr>
|
||||
<h2 id="data-collection-domain-integration">Data Collection Domain Integration</h2>
|
||||
<p>The Data Collection Domain is implemented through SmartRegisterReader.</p>
|
||||
<p>SmartRegisterReader is responsible for acquiring, decoding, and evaluating measurement data received through the meter streaming interface.</p>
|
||||
<p>Its responsibilities include:</p>
|
||||
<ul>
|
||||
<li>stream reception</li>
|
||||
<li>protocol decoding</li>
|
||||
<li>register extraction</li>
|
||||
<li>measurement evaluation</li>
|
||||
<li>diagnostic data processing</li>
|
||||
<li>runtime monitoring</li>
|
||||
</ul>
|
||||
<p>Unlike the Configuration Domain, SmartRegisterReader operates independently from GCI and communicates directly with the streaming interface.</p>
|
||||
<hr>
|
||||
<h2 id="architectural-benefits">Architectural Benefits</h2>
|
||||
<p>The current implementation provides several advantages:</p>
|
||||
<ul>
|
||||
<li>clear separation between configuration and measurement acquisition</li>
|
||||
<li>independent development of both domains</li>
|
||||
<li>simplified maintenance</li>
|
||||
<li>reusable platform components</li>
|
||||
<li>straightforward integration into TestBenchFramework</li>
|
||||
</ul>
|
||||
<p>The separation also allows each domain to evolve independently while remaining part of the same overall platform architecture.</p>
|
||||
<hr>
|
||||
<h2 id="current-usage">Current Usage</h2>
|
||||
<p>Within TestBenchFramework:</p>
|
||||
<ul>
|
||||
<li>GciBridge provides access to meter configuration functionality</li>
|
||||
<li>GCI provides runtime orchestration and communication services</li>
|
||||
<li>Laatzen libraries provide meter-specific functionality</li>
|
||||
<li>SmartRegisterReader provides streaming data acquisition and evaluation</li>
|
||||
</ul>
|
||||
<p>Together, these components form the current implementation of the Genesis Meter Platform.</p>
|
||||
<hr>
|
||||
<h2 id="related-pages">Related Pages</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="page_platform__home.html">Platform Home</a></td>
|
||||
<td>Platform overview and architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_platform__current_state.html">Current State</a></td>
|
||||
<td>Current platform architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_platform__configuration_domain.html">Configuration Domain</a></td>
|
||||
<td>Request-response communication architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="page_platform__data_collection_domain.html">Data Collection Domain</a></td>
|
||||
<td>Streaming communication architecture.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="../gci/page_gci__home.html">GCI Documentation</a></td>
|
||||
<td>Detailed GCI implementation documentation.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</article>
|
||||
|
||||
<div class="contribution d-print-none">
|
||||
</div>
|
||||
|
||||
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="affix">
|
||||
<nav id="affix"></nav>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
<footer class="border-top text-secondary">
|
||||
<div class="container-xxl">
|
||||
<div class="flex-fill">
|
||||
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
2
GenesisCordonelInterface/docs/_site/public/architecture-7HQA4BMR-HSKY6TUH.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import{a as e,b as r}from"./chunk-HC7FQI6W.min.js";import"./chunk-E24YF7OQ.min.js";import"./chunk-R5JLOOQ4.min.js";import"./chunk-PTL4EUOE.min.js";import"./chunk-E5F23VE2.min.js";import"./chunk-VBFLGJ4I.min.js";export{e as ArchitectureModule,r as createArchitectureServices};
|
||||
//# sourceMappingURL=architecture-7HQA4BMR-HSKY6TUH.min.js.map
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
37
GenesisCordonelInterface/docs/_site/public/architectureDiagram-VXUJARFQ-KGMRTIN6.min.js
vendored
Normal file
123
GenesisCordonelInterface/docs/_site/public/blockDiagram-VD42YOAC-ZDZZSUGS.min.js
vendored
Normal file
11
GenesisCordonelInterface/docs/_site/public/c4Diagram-YG6GDRKO-DXUAXJQ4.min.js
vendored
Normal file
2
GenesisCordonelInterface/docs/_site/public/chunk-2SNPQT3V.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import{b as t}from"./chunk-4TS2OR5T.min.js";var s=class{constructor(i){this.init=i,this.records=this.init()}static{t(this,"ImperativeState")}reset(){this.records=this.init()}};export{s as a};
|
||||
//# sourceMappingURL=chunk-2SNPQT3V.min.js.map
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../node_modules/mermaid/dist/chunks/mermaid.core/chunk-QZHKN3VN.mjs"],
|
||||
"sourcesContent": ["import {\n __name\n} from \"./chunk-AGHRB4JF.mjs\";\n\n// src/utils/imperativeState.ts\nvar ImperativeState = class {\n /**\n * @param init - Function that creates the default state.\n */\n constructor(init) {\n this.init = init;\n this.records = this.init();\n }\n static {\n __name(this, \"ImperativeState\");\n }\n reset() {\n this.records = this.init();\n }\n};\n\nexport {\n ImperativeState\n};\n"],
|
||||
"mappings": "4CAKA,IAAIA,EAAkB,KAAM,CAI1B,YAAYC,EAAM,CAChB,KAAK,KAAOA,EACZ,KAAK,QAAU,KAAK,KAAK,CAC3B,CACA,MAAO,CACLC,EAAO,KAAM,iBAAiB,CAChC,CACA,OAAQ,CACN,KAAK,QAAU,KAAK,KAAK,CAC3B,CACF",
|
||||
"names": ["ImperativeState", "init", "__name"]
|
||||
}
|
||||