447 lines
15 KiB
C#
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
|
|
|
|
}
|
|
}
|