tbf/GenesisCordonelTester/API/InterfaceToLaatzen.cs

447 lines
15 KiB
C#

using Logic.ProductionToProductMapper.Cordonel;
using Newtonsoft.Json;
using NLog;
using System;
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;
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;
using Access = Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Access;
using Register = Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Register;
using System.Collections.Concurrent;
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>
internal class InterfaceToLaatzen
{
#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;
}
#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())
{
meter.SetupFromConfigFile(slot, false);
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())
{
meter.SetupFromConfigFile(slot, false);
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
#region API - Connect
/// <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; }
}
/// <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 Connect(int slotNo, bool useOfflinePasswords)
{
try
{
if (slotNo <= 0)
throw new ArgumentOutOfRangeException(nameof(slotNo), "Slot number must be greater than zero.");
_currentGenesis?.DisposeMeter();
_meterBatch.RemoveAllMeters();
_currentGenesis = null;
_currentGenesis = new GenesisMeter();
_currentGenesis.UseOfflinePasswords = useOfflinePasswords;
_currentGenesis.SetupFromConfigFile(slotNo);
_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
};
}
}
#endregion
}
}