2026-04-14 10:32:51 +00:00
|
|
|
|
using Logic.ProductionToProductMapper.Cordonel;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using NLog;
|
|
|
|
|
|
using System;
|
2026-04-14 10:34:50 +00:00
|
|
|
|
using System.Collections.Concurrent;
|
2026-04-14 10:32:51 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
|
using System.Data;
|
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Net;
|
|
|
|
|
|
using System.Reflection;
|
2026-04-14 10:34:50 +00:00
|
|
|
|
using System.Security.Policy;
|
2026-04-14 10:32:51 +00:00
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
|
using Xylem.Common.CommonCore.Configuration;
|
|
|
|
|
|
using Xylem.Common.CommonCore.Consts;
|
|
|
|
|
|
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
|
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
|
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts;
|
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
|
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes;
|
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
|
|
|
|
|
using Xylem.Common.Logic.ProductionOrderCore.TestResults;
|
|
|
|
|
|
using Xylem.Common.Logic.SoftwareAccessHelper;
|
|
|
|
|
|
using Xylem.Common.Utils.Logging;
|
2026-04-14 10:34:50 +00:00
|
|
|
|
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
|
2026-04-14 10:32:51 +00:00
|
|
|
|
using Access = Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Access;
|
|
|
|
|
|
using Register = Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Register;
|
|
|
|
|
|
|
|
|
|
|
|
namespace GenesisCordonelInterface.API
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Provides a public API for Genesis meter operations.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This class exposes reusable functionality extracted from the original UI code
|
|
|
|
|
|
/// so it can be used from other projects within the solution.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// The API is intended to gradually consolidate meter-related operations such as:
|
|
|
|
|
|
/// - port detection
|
|
|
|
|
|
/// - PCB ID reading
|
|
|
|
|
|
/// - communication setup
|
|
|
|
|
|
/// - requests and commands
|
|
|
|
|
|
/// - additional service actions
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// This class should contain business logic only and should not depend on UI elements
|
|
|
|
|
|
/// such as forms, controls, MessageBox, or DataGridView.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// UI-specific code should remain outside this class and call this API instead.
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
/// <example>
|
|
|
|
|
|
/// <code>
|
|
|
|
|
|
/// var api = new Api2();
|
|
|
|
|
|
///
|
|
|
|
|
|
/// var request = api.DetectRequestPort(3);
|
|
|
|
|
|
/// if (request.Success)
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine($"PCB ID: {request.PcbId}");
|
|
|
|
|
|
/// }
|
|
|
|
|
|
///
|
|
|
|
|
|
/// var streaming = api.DetectStreamingPort(3);
|
|
|
|
|
|
/// if (streaming.Success)
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine($"Streaming port: {streaming.PortName}");
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// </code>
|
|
|
|
|
|
/// </example>
|
2026-04-14 10:34:50 +00:00
|
|
|
|
public class InterfaceGCIToLaatzen
|
2026-04-14 10:32:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
#region Declaration region
|
|
|
|
|
|
private static readonly Lazy<ILogger> Logger = new Lazy<ILogger>(() => NLogHelper.CreateOrGetLogger("GenesisCordonelInterface"));
|
|
|
|
|
|
|
|
|
|
|
|
public class regStore
|
|
|
|
|
|
{
|
|
|
|
|
|
public String PcbId;
|
|
|
|
|
|
public DateTimeOffset created;
|
|
|
|
|
|
public List<regDefValue> keyValues;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class regDefValue
|
|
|
|
|
|
{
|
|
|
|
|
|
public RegisterDefinition def;
|
|
|
|
|
|
public String value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private GenesisMeter _currentGenesis;
|
|
|
|
|
|
private MeterBatch _meterBatch = new MeterBatch();
|
|
|
|
|
|
private regStore _regsToStore;
|
|
|
|
|
|
private String _currentPcbId = "";
|
|
|
|
|
|
|
|
|
|
|
|
private Boolean IsBusy
|
|
|
|
|
|
{
|
|
|
|
|
|
get;
|
|
|
|
|
|
set;
|
|
|
|
|
|
}
|
2026-04-14 10:34:50 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets a value indicating whether the meter is connected and logged on.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsConnected
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return _currentGenesis != null && _currentGenesis.IsLoggedOn;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-14 10:32:51 +00:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region API - Port Detection region(extracted from FrmSetup:DgvConfig_CellContentClick)
|
|
|
|
|
|
|
|
|
|
|
|
public class PortDetectionResult
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Indicates whether the detection was successful.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool Success { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Slot number used for the detection.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int Slot { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Name of the detected communication port.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string PortName { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// PCB ID read from the device (available for request detection).
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string PcbId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Error message describing why detection failed (if not successful).
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string ErrorMessage { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="slot">Slot number.</param>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// Result containing success status and detected port name.
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <example>
|
|
|
|
|
|
/// <code>
|
|
|
|
|
|
/// var api = new Api2();
|
|
|
|
|
|
/// var result = api.DetectStreamingPort(3);
|
|
|
|
|
|
///
|
|
|
|
|
|
/// if (result.Success)
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine($"Port: {result.PortName}");
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// else
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine("Streaming detection failed");
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// </code>
|
|
|
|
|
|
/// </example>
|
|
|
|
|
|
public PortDetectionResult DetectStreamingPort(int slot)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (slot <= 0)
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(slot));
|
|
|
|
|
|
|
|
|
|
|
|
using (var mb = new MeterBatch())
|
|
|
|
|
|
using (var meter = new GenesisMeter())
|
|
|
|
|
|
{
|
2026-04-15 06:46:19 +00:00
|
|
|
|
//meter.SetupFromConfigFile(slot, false);
|
2026-04-14 10:32:51 +00:00
|
|
|
|
mb.AddMeter(meter);
|
|
|
|
|
|
|
|
|
|
|
|
var rawData = new ConcurrentBag<string>();
|
|
|
|
|
|
|
|
|
|
|
|
meter.StreamingPort.OnRawRecordReceived += (o, rawMsg) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var data = (string)rawMsg.GetData();
|
|
|
|
|
|
rawData.Add(data);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Thread.Sleep(500);
|
|
|
|
|
|
|
|
|
|
|
|
var success = rawData.Any();
|
|
|
|
|
|
var portName = meter.StreamingPort.GetPortName();
|
|
|
|
|
|
|
|
|
|
|
|
return new PortDetectionResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = success,
|
|
|
|
|
|
Slot = slot,
|
|
|
|
|
|
PortName = portName,
|
|
|
|
|
|
ErrorMessage = success ? null : "No streaming data received."
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Detects the request port by attempting to read the PCB ID.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="slot">Slot number.</param>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// Result containing success status, port name, and PCB ID if successful.
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <example>
|
|
|
|
|
|
/// <code>
|
|
|
|
|
|
/// var api = new Api2();
|
|
|
|
|
|
/// var result = api.DetectRequestPort(3);
|
|
|
|
|
|
///
|
|
|
|
|
|
/// if (result.Success)
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine($"PCB ID: {result.PcbId}");
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// else
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine("Detection failed");
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// </code>
|
|
|
|
|
|
/// </example>
|
|
|
|
|
|
public PortDetectionResult DetectRequestPort(int slot)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (slot <= 0)
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(slot));
|
|
|
|
|
|
|
|
|
|
|
|
using (var mb = new MeterBatch())
|
|
|
|
|
|
using (var meter = new GenesisMeter())
|
|
|
|
|
|
{
|
2026-04-15 06:46:19 +00:00
|
|
|
|
//meter.SetupFromConfigFile(slot, false);
|
2026-04-14 10:32:51 +00:00
|
|
|
|
mb.AddMeter(meter);
|
|
|
|
|
|
|
|
|
|
|
|
meter.Logout();
|
|
|
|
|
|
|
|
|
|
|
|
var pcbId = meter.GetPcbId();
|
|
|
|
|
|
var success = !string.IsNullOrEmpty(pcbId);
|
|
|
|
|
|
var portName = meter.RequestPort.GetPortName();
|
|
|
|
|
|
|
|
|
|
|
|
return new PortDetectionResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = success,
|
|
|
|
|
|
Slot = slot,
|
|
|
|
|
|
PortName = portName,
|
|
|
|
|
|
PcbId = pcbId,
|
|
|
|
|
|
ErrorMessage = success ? null : "PCB ID was empty."
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region API - PCB ID region
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Reads PCB ID for the specified slot.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="slot">Slot number.</param>
|
|
|
|
|
|
/// <returns>PCB ID read from the meter.</returns>
|
|
|
|
|
|
/// <example>
|
|
|
|
|
|
/// <code>
|
|
|
|
|
|
/// var api = new Api2();
|
|
|
|
|
|
/// string pcbId = api.GetPcbId(3);
|
|
|
|
|
|
/// Console.WriteLine(pcbId);
|
|
|
|
|
|
/// </code>
|
|
|
|
|
|
/// </example>
|
|
|
|
|
|
public string GetPcbId(int slot)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (slot <= 0)
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(slot), "Slot number must be greater than zero.");
|
|
|
|
|
|
|
|
|
|
|
|
using (var mb = new MeterBatch())
|
|
|
|
|
|
using (var meter = new GenesisMeter())
|
|
|
|
|
|
{
|
|
|
|
|
|
meter.SetupFromConfigFile(slot, false);
|
|
|
|
|
|
mb.AddMeter(meter);
|
|
|
|
|
|
|
|
|
|
|
|
meter.Logout();
|
|
|
|
|
|
|
|
|
|
|
|
return meter.GetPcbId();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-04-14 10:34:50 +00:00
|
|
|
|
#region Password and Login
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets meter password.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool SetMeterPassword(string password)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnsureConnected();
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(password))
|
|
|
|
|
|
throw new ArgumentException("Password cannot be null or empty.", nameof(password));
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// Replace this with the real Genesis API call if available.
|
|
|
|
|
|
// Example:
|
|
|
|
|
|
// return _currentGenesis.SetMeterPassword(password);
|
|
|
|
|
|
|
|
|
|
|
|
var result = WriteRegister("SECURITY_Password", password, true, true);
|
|
|
|
|
|
return result.Success;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Value.Error(ex, "SetMeterPassword failed.");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Performs login using provided password.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool Login(string password)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_currentGenesis == null)
|
|
|
|
|
|
throw new InvalidOperationException("Genesis meter is not initialized. Call Connect first.");
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(password))
|
|
|
|
|
|
throw new ArgumentException("Password cannot be null or empty.", nameof(password));
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// IMPORTANT:
|
|
|
|
|
|
// Replace with actual Genesis API method if available
|
|
|
|
|
|
|
|
|
|
|
|
// Variant A – direct login method (preferred)
|
|
|
|
|
|
// return _currentGenesis.Login(password);
|
|
|
|
|
|
|
|
|
|
|
|
// Variant B – if password must be set first
|
|
|
|
|
|
// _currentGenesis.Password = password;
|
|
|
|
|
|
// return _currentGenesis.Login();
|
|
|
|
|
|
|
|
|
|
|
|
// TEMP fallback (if no direct method known)
|
|
|
|
|
|
bool result = _currentGenesis.Login();
|
|
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
|
Logger.Value.Warn("Login failed.");
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Value.Error(ex, "Login failed.");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-04-14 10:32:51 +00:00
|
|
|
|
#region API - Connect
|
2026-04-15 06:46:19 +00:00
|
|
|
|
|
|
|
|
|
|
public class InitResult
|
|
|
|
|
|
{
|
|
|
|
|
|
public bool Success { get; set; }
|
|
|
|
|
|
public int Slot { get; set; }
|
|
|
|
|
|
public string ErrorMessage { get; set; }
|
|
|
|
|
|
public string InterfaceVersion { get; set; }
|
|
|
|
|
|
public bool InterfaceSupportsFwVersion { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-14 10:32:51 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Represents the result of a connect operation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class ConnectResult
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Indicates whether the connect operation was successful.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool Success { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Slot number used for connect.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int Slot { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Connected PCB ID.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string PcbId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Indicates whether the meter is logged on.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsLoggedOn { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Firmware version reported by the meter.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string FwVersion { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Interface version from configuration.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string InterfaceVersion { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Indicates whether the loaded configuration supports the detected firmware version.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool InterfaceSupportsFwVersion { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Registers available after successful connect.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<RegisterSnapshot> Registers { get; set; } = new List<RegisterSnapshot>();
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Error message if connect failed.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string ErrorMessage { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Represents one register returned after connect.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class RegisterSnapshot
|
|
|
|
|
|
{
|
|
|
|
|
|
public string Name { get; set; }
|
|
|
|
|
|
public string Type { get; set; }
|
|
|
|
|
|
public string RawValue { get; set; }
|
|
|
|
|
|
public string Min { get; set; }
|
|
|
|
|
|
public string Max { get; set; }
|
|
|
|
|
|
public string Description { get; set; }
|
|
|
|
|
|
public string Version { get; set; }
|
|
|
|
|
|
public string IsAvailable { get; set; }
|
|
|
|
|
|
public string Privilege { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 06:46:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public InitResult InitOneMeterFromExtern(Int32 slotNo, ConfigSource useConfigSource, PasswordSource usePasswordSource, PortConfig? requestPort, PortConfig? streamingPort)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (slotNo <= 0)
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(slotNo), "Slot number must be greater than zero.");
|
|
|
|
|
|
|
|
|
|
|
|
//_meterBatch.RemoveMeter(slotNo);
|
|
|
|
|
|
_currentGenesis?.DisposeMeter();
|
|
|
|
|
|
|
|
|
|
|
|
_currentGenesis = new GenesisMeter();
|
|
|
|
|
|
_currentGenesis.UseOfflinePasswords = usePasswordSource == PasswordSource.OfflineFile;
|
|
|
|
|
|
_currentGenesis.usePasswordSource = usePasswordSource;
|
|
|
|
|
|
_currentGenesis.useConfigSource = useConfigSource;
|
|
|
|
|
|
|
|
|
|
|
|
_currentGenesis.SetupFromExternConfig(
|
|
|
|
|
|
slotNo,
|
|
|
|
|
|
requestPort,
|
|
|
|
|
|
streamingPort,
|
|
|
|
|
|
true);
|
|
|
|
|
|
|
|
|
|
|
|
_meterBatch.AddMeter(_currentGenesis);
|
|
|
|
|
|
|
|
|
|
|
|
return new InitResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = true,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
InterfaceVersion = _currentGenesis.InterfaceInfo?.InterfaceVersion,
|
|
|
|
|
|
InterfaceSupportsFwVersion = _currentGenesis.InterfaceSupportsFwVersion
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
//_meterBatch.RemoveAllMeters();
|
|
|
|
|
|
_currentGenesis?.DisposeMeter();
|
|
|
|
|
|
_currentGenesis = null;
|
|
|
|
|
|
|
|
|
|
|
|
return new InitResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
ErrorMessage = ex.Message
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-14 10:32:51 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Connects to a Genesis meter for the specified slot.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="slotNo">Slot number.</param>
|
|
|
|
|
|
/// <param name="useOfflinePasswords">Specifies whether offline passwords should be used.</param>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// Connect operation result including PCB ID, firmware/configuration info, and register snapshots.
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <example>
|
|
|
|
|
|
/// <code>
|
|
|
|
|
|
/// var api = new GenesisAPI();
|
|
|
|
|
|
/// var result = api.Connect(3, true);
|
|
|
|
|
|
///
|
|
|
|
|
|
/// if (result.Success)
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine(result.PcbId);
|
|
|
|
|
|
/// Console.WriteLine(result.InterfaceVersion);
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// else
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine(result.ErrorMessage);
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// </code>
|
|
|
|
|
|
/// </example>
|
2026-04-15 06:46:19 +00:00
|
|
|
|
public ConnectResult ConnectOneMeter(int slotNo)
|
2026-04-14 10:32:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
try
|
2026-04-15 06:46:19 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Validate input
|
|
|
|
|
|
if (slotNo <= 0)
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(slotNo), "Slot number must be greater than zero.");
|
|
|
|
|
|
|
|
|
|
|
|
// Find meter in batch by slot
|
|
|
|
|
|
var meter = _meterBatch.ListOfMeters
|
|
|
|
|
|
.OfType<GenesisMeter>()
|
|
|
|
|
|
.FirstOrDefault(m => m.Slot == slotNo);
|
|
|
|
|
|
|
|
|
|
|
|
// Meter not initialized
|
|
|
|
|
|
if (meter == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ConnectResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
ErrorMessage = $"Meter for slot {slotNo} not found in batch."
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set current working meter
|
|
|
|
|
|
_currentGenesis = meter;
|
|
|
|
|
|
|
|
|
|
|
|
// Perform login for meters in batch
|
|
|
|
|
|
_meterBatch.MetersLogin();
|
|
|
|
|
|
|
|
|
|
|
|
// Validate connection result
|
|
|
|
|
|
if (!_currentGenesis.IsLoggedOn && string.IsNullOrEmpty(_currentGenesis.PcbId))
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ConnectResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
IsLoggedOn = false,
|
|
|
|
|
|
PcbId = _currentGenesis.PcbId,
|
|
|
|
|
|
ErrorMessage = "Cannot read out PcbId. Access to Cordonel denied."
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Build successful result
|
|
|
|
|
|
var result = new ConnectResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = _currentGenesis.IsLoggedOn,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
PcbId = _currentGenesis.PcbId,
|
|
|
|
|
|
IsLoggedOn = _currentGenesis.IsLoggedOn,
|
|
|
|
|
|
FwVersion = _currentGenesis.FwVersion,
|
|
|
|
|
|
InterfaceVersion = _currentGenesis.InterfaceInfo?.InterfaceVersion,
|
|
|
|
|
|
InterfaceSupportsFwVersion = _currentGenesis.InterfaceSupportsFwVersion
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Return failure result on exception
|
|
|
|
|
|
return new ConnectResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
ErrorMessage = ex.Message
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Connects to a Genesis meter for the specified slot.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="slotNo">Slot number.</param>
|
|
|
|
|
|
/// <param name="useOfflinePasswords">Specifies whether offline passwords should be used.</param>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// Connect operation result including PCB ID, firmware/configuration info, and register snapshots.
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <example>
|
|
|
|
|
|
/// <code>
|
|
|
|
|
|
/// var api = new GenesisAPI();
|
|
|
|
|
|
/// var result = api.Connect(3, true);
|
|
|
|
|
|
///
|
|
|
|
|
|
/// if (result.Success)
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine(result.PcbId);
|
|
|
|
|
|
/// Console.WriteLine(result.InterfaceVersion);
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// else
|
|
|
|
|
|
/// {
|
|
|
|
|
|
/// Console.WriteLine(result.ErrorMessage);
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// </code>
|
|
|
|
|
|
/// </example>
|
|
|
|
|
|
public ConnectResult ConnectAllMeters(int slotNo)
|
|
|
|
|
|
{
|
|
|
|
|
|
/*try
|
2026-04-14 10:32:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (slotNo <= 0)
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(slotNo), "Slot number must be greater than zero.");
|
|
|
|
|
|
|
|
|
|
|
|
_currentGenesis?.DisposeMeter();
|
|
|
|
|
|
_meterBatch.RemoveAllMeters();
|
|
|
|
|
|
_currentGenesis = null;
|
|
|
|
|
|
|
|
|
|
|
|
_currentGenesis = new GenesisMeter();
|
2026-04-14 10:34:50 +00:00
|
|
|
|
_currentGenesis.UseOfflinePasswords = usePasswordSource == PasswordSource.OfflineFile;
|
|
|
|
|
|
_currentGenesis.usePasswordSource = usePasswordSource;
|
2026-04-15 06:46:19 +00:00
|
|
|
|
_currentGenesis.useConfigSource = useConfigSource;
|
|
|
|
|
|
_currentGenesis.SetupFromConfigFile(slotNo);//...MF
|
|
|
|
|
|
_currentGenesis.SetupFromExternConfig(slotNo);
|
2026-04-14 10:32:51 +00:00
|
|
|
|
_meterBatch.AddMeter(_currentGenesis);
|
|
|
|
|
|
|
|
|
|
|
|
_meterBatch.MetersLogin();
|
|
|
|
|
|
|
|
|
|
|
|
if (!_currentGenesis.IsLoggedOn && string.IsNullOrEmpty(_currentGenesis.PcbId))
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ConnectResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
IsLoggedOn = false,
|
|
|
|
|
|
PcbId = _currentGenesis.PcbId,
|
|
|
|
|
|
ErrorMessage = "Cannot read out PcbId. Access to Cordonel denied."
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result = new ConnectResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = _currentGenesis.IsLoggedOn,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
PcbId = _currentGenesis.PcbId,
|
|
|
|
|
|
IsLoggedOn = _currentGenesis.IsLoggedOn,
|
|
|
|
|
|
FwVersion = _currentGenesis.FwVersion,
|
|
|
|
|
|
InterfaceVersion = _currentGenesis.InterfaceInfo?.InterfaceVersion,
|
|
|
|
|
|
InterfaceSupportsFwVersion = _currentGenesis.InterfaceSupportsFwVersion
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item in _currentGenesis.GetRegistersDic())
|
|
|
|
|
|
{
|
|
|
|
|
|
var from = item.Key.RegisterDetail.Version.First.HasValue
|
|
|
|
|
|
? item.Key.RegisterDetail.Version.First.Value.ToString()
|
|
|
|
|
|
: "-";
|
|
|
|
|
|
|
|
|
|
|
|
var to = item.Key.RegisterDetail.Version.Last.HasValue
|
|
|
|
|
|
? item.Key.RegisterDetail.Version.Last.Value.ToString()
|
|
|
|
|
|
: "-";
|
|
|
|
|
|
|
|
|
|
|
|
result.Registers.Add(new RegisterSnapshot
|
|
|
|
|
|
{
|
|
|
|
|
|
Name = item.Key.GetIdent(),
|
|
|
|
|
|
Type = item.Key.DataType.Name,
|
|
|
|
|
|
RawValue = BitConverter.ToString(item.Value).Replace("-", " "),
|
|
|
|
|
|
Min = item.Key.Minimum?.ToString(),
|
|
|
|
|
|
Max = item.Key.Maximum?.ToString(),
|
|
|
|
|
|
Description = item.Key.RegisterDetail.Description,
|
|
|
|
|
|
Version = $"from {from} to {to}",
|
|
|
|
|
|
IsAvailable = item.Key.IsAvailable.ToString(),
|
|
|
|
|
|
Privilege = item.Key.RegisterDetail.Privilege.Lvl8.ToString()
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_meterBatch.RemoveAllMeters();
|
|
|
|
|
|
_currentGenesis?.DisposeMeter();
|
|
|
|
|
|
|
|
|
|
|
|
return new ConnectResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
Slot = slotNo,
|
|
|
|
|
|
ErrorMessage = ex.Message
|
|
|
|
|
|
};
|
2026-04-15 06:46:19 +00:00
|
|
|
|
}*/
|
|
|
|
|
|
return null;
|
2026-04-14 10:32:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-04-14 10:34:50 +00:00
|
|
|
|
#region Meter Registers
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Result of a register read operation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class RegisterReadResult
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Indicates whether the read operation was successful.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool Success { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Name of the register.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string RegisterName { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Raw bytes returned from the device.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public byte[] RawBytes { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Raw value formatted as hexadecimal string.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string RawHex { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Converted value based on register data type (if possible).
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public object TypedValue { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// String representation of the converted value.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string TypedValueText { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Data type of the register.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string DataType { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Error message if operation failed.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string ErrorMessage { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Result of a register write operation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class RegisterWriteResult
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Indicates whether the write operation was successful.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool Success { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Name of the register.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string RegisterName { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Value that was written to the register.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public object WrittenValue { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Indicates whether configuration was stored to the device.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool StoreToDevice { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Indicates whether system state refresh was triggered.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool RefreshSystemState { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Error message if operation failed.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string ErrorMessage { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Helper methods
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Ensures that the meter is connected and logged on.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void EnsureConnected()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_currentGenesis == null)
|
|
|
|
|
|
throw new InvalidOperationException("Genesis meter is not initialized. Call Connect first.");
|
|
|
|
|
|
|
|
|
|
|
|
if (!_currentGenesis.IsLoggedOn)
|
|
|
|
|
|
throw new InvalidOperationException("Genesis meter is not logged on. Call Connect first.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Finds register definition by name.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private RegisterDefinition FindRegisterDefinition(string registerName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(registerName))
|
|
|
|
|
|
throw new ArgumentException("Register name cannot be null or empty.", nameof(registerName));
|
|
|
|
|
|
|
|
|
|
|
|
var match = _currentGenesis
|
|
|
|
|
|
.GetRegistersDic()
|
|
|
|
|
|
.Keys
|
|
|
|
|
|
.FirstOrDefault(r => string.Equals(r.GetIdent(), registerName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
|
|
if (match == null)
|
|
|
|
|
|
throw new KeyNotFoundException($"Register '{registerName}' was not found.");
|
|
|
|
|
|
|
|
|
|
|
|
return match;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Converts byte array to hex string.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private string ToHex(byte[] data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data == null || data.Length == 0)
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
return BitConverter.ToString(data).Replace("-", " ");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Read register
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Reads register value by register name.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public RegisterReadResult ReadRegister(string registerName)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
EnsureConnected();
|
|
|
|
|
|
|
|
|
|
|
|
var register = FindRegisterDefinition(registerName);
|
|
|
|
|
|
var raw = _currentGenesis.ReadRegister(registerName);
|
|
|
|
|
|
|
|
|
|
|
|
object typedValue = null;
|
|
|
|
|
|
string typedValueText = null;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
typedValue = ConvertRegisterValue(register, raw);
|
|
|
|
|
|
typedValueText = typedValue?.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// Ignore conversion errors, raw value is still valid
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new RegisterReadResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = true,
|
|
|
|
|
|
RegisterName = registerName,
|
|
|
|
|
|
RawBytes = raw,
|
|
|
|
|
|
RawHex = ToHex(raw),
|
|
|
|
|
|
TypedValue = typedValue,
|
|
|
|
|
|
TypedValueText = typedValueText,
|
|
|
|
|
|
DataType = register.DataType?.Name
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Value.Error(ex, $"ReadRegister failed for '{registerName}'.");
|
|
|
|
|
|
|
|
|
|
|
|
return new RegisterReadResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
RegisterName = registerName,
|
|
|
|
|
|
ErrorMessage = ex.Message
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Typed conversion
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Converts raw register value to a typed value based on register definition.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private object ConvertRegisterValue(RegisterDefinition register, byte[] raw)
|
|
|
|
|
|
{
|
|
|
|
|
|
var typeName = register.DataType?.Name;
|
|
|
|
|
|
|
|
|
|
|
|
switch (typeName)
|
|
|
|
|
|
{
|
|
|
|
|
|
case "Boolean":
|
|
|
|
|
|
return RegisterConverter.ByteArrayToValue<bool>(raw);
|
|
|
|
|
|
|
|
|
|
|
|
case "Byte":
|
|
|
|
|
|
return RegisterConverter.ByteArrayToValue<byte>(raw);
|
|
|
|
|
|
|
|
|
|
|
|
case "Int32":
|
|
|
|
|
|
return RegisterConverter.ByteArrayToValue<int>(raw);
|
|
|
|
|
|
|
|
|
|
|
|
case "UInt32":
|
|
|
|
|
|
return RegisterConverter.ByteArrayToValue<uint>(raw);
|
|
|
|
|
|
|
|
|
|
|
|
case "Double":
|
|
|
|
|
|
return RegisterConverter.ByteArrayToValue<double>(raw);
|
|
|
|
|
|
|
|
|
|
|
|
case "Single":
|
|
|
|
|
|
return RegisterConverter.ByteArrayToValue<float>(raw);
|
|
|
|
|
|
|
|
|
|
|
|
case "String":
|
|
|
|
|
|
return Encoding.ASCII.GetString(raw).TrimEnd('\0');
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
return ToHex(raw);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Generic login
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Reads register and converts it directly to specified type.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public T ReadRegisterValue<T>(string registerName)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnsureConnected();
|
|
|
|
|
|
|
|
|
|
|
|
var raw = _currentGenesis.ReadRegister(registerName);
|
|
|
|
|
|
return RegisterConverter.ByteArrayToValue<T>(raw);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Write register
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Writes value to register.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public RegisterWriteResult WriteRegister(
|
|
|
|
|
|
string registerName,
|
|
|
|
|
|
object value,
|
|
|
|
|
|
bool storeToDevice = false,
|
|
|
|
|
|
bool refreshSystemState = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
EnsureConnected();
|
|
|
|
|
|
|
|
|
|
|
|
bool writeOk = _currentGenesis.WriteRegister(registerName, value);
|
|
|
|
|
|
|
|
|
|
|
|
if (!writeOk)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new RegisterWriteResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
RegisterName = registerName,
|
|
|
|
|
|
WrittenValue = value,
|
|
|
|
|
|
ErrorMessage = "Write operation failed."
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (storeToDevice)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_currentGenesis.StoreAllConfigurations())
|
|
|
|
|
|
{
|
|
|
|
|
|
return new RegisterWriteResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
RegisterName = registerName,
|
|
|
|
|
|
WrittenValue = value,
|
|
|
|
|
|
ErrorMessage = "StoreAllConfigurations failed."
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (refreshSystemState)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_currentGenesis.WriteRegister("SENSUSRADIO_SYSTEMSTATE", 0xFF, true, false))
|
|
|
|
|
|
{
|
|
|
|
|
|
return new RegisterWriteResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
RegisterName = registerName,
|
|
|
|
|
|
WrittenValue = value,
|
|
|
|
|
|
ErrorMessage = "System state refresh failed."
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new RegisterWriteResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = true,
|
|
|
|
|
|
RegisterName = registerName,
|
|
|
|
|
|
WrittenValue = value,
|
|
|
|
|
|
StoreToDevice = storeToDevice,
|
|
|
|
|
|
RefreshSystemState = refreshSystemState
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Value.Error(ex, $"WriteRegister failed for '{registerName}'.");
|
|
|
|
|
|
|
|
|
|
|
|
return new RegisterWriteResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Success = false,
|
|
|
|
|
|
RegisterName = registerName,
|
|
|
|
|
|
WrittenValue = value,
|
|
|
|
|
|
ErrorMessage = ex.Message
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Bulk operations
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Reads multiple registers.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<RegisterReadResult> ReadRegisters(IEnumerable<string> registerNames)
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = new List<RegisterReadResult>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var name in registerNames)
|
|
|
|
|
|
{
|
|
|
|
|
|
result.Add(ReadRegister(name));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Writes multiple registers.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<RegisterWriteResult> WriteRegisters(
|
|
|
|
|
|
Dictionary<string, object> registerValues,
|
|
|
|
|
|
bool storeToDevice = false,
|
|
|
|
|
|
bool refreshSystemState = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
var results = new List<RegisterWriteResult>();
|
|
|
|
|
|
|
|
|
|
|
|
int index = 0;
|
|
|
|
|
|
int total = registerValues.Count;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var pair in registerValues)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool doStore = storeToDevice && index == total - 1;
|
|
|
|
|
|
bool doRefresh = refreshSystemState && index == total - 1;
|
|
|
|
|
|
|
|
|
|
|
|
results.Add(WriteRegister(pair.Key, pair.Value, doStore, doRefresh));
|
|
|
|
|
|
index++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Disconnect
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Disconnects from the meter and releases resources.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Disconnect()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_currentGenesis != null && _currentGenesis.IsLoggedOn)
|
|
|
|
|
|
{
|
|
|
|
|
|
_currentGenesis.Logout();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Value.Error(ex, "Disconnect failed.");
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_meterBatch.RemoveAllMeters();
|
|
|
|
|
|
_currentGenesis?.DisposeMeter();
|
|
|
|
|
|
_currentGenesis = null;
|
|
|
|
|
|
_currentPcbId = string.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-04-14 10:32:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|