Merge remote-tracking branch 'private-origin/develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode
# Conflicts: # TBF/Rig/TestMethods/GenesisCommunication/GenesisHead/GenesisHead.cs # TBF/TBF.csproj
This commit is contained in:
commit
5482d4ac96
312
GenesisCordonelInterface/API/GciPublicModels.cs
Normal file
312
GenesisCordonelInterface/API/GciPublicModels.cs
Normal file
@ -0,0 +1,312 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static GenesisCordonelInterface.API.InterfaceOutsideToGCI;
|
||||
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
|
||||
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace GenesisCordonelInterface.API
|
||||
{
|
||||
/// <summary>
|
||||
/// Public data contract layer for the Genesis Cordonel Interface (GCI).
|
||||
///
|
||||
/// This class defines all Data Transfer Objects (DTOs) that are exposed
|
||||
/// to external consumers (e.g. TBF, UI, or other integration layers).
|
||||
///
|
||||
/// Responsibilities:
|
||||
/// - Provide stable, dependency-free models for external usage
|
||||
/// - Decouple internal GCI implementation (GenesisMeter, Xylem libraries)
|
||||
/// from external systems
|
||||
/// - Define request/response contracts for all supported operations
|
||||
/// - Contain mapping methods between public DTOs and internal domain models
|
||||
///
|
||||
/// Architecture:
|
||||
/// External world (TBF / UI)
|
||||
/// ↓
|
||||
/// GciPublicModels (this layer)
|
||||
/// ↓
|
||||
/// Internal GCI API (InterfaceGCIToLaatzen, GenesisMeter, etc.)
|
||||
///
|
||||
/// Notes:
|
||||
/// - Public models must NOT expose internal types (e.g. GenesisMeter, IPort, etc.)
|
||||
/// - All mapping between internal and external representations must be done here
|
||||
/// - DTOs are designed to be simple, serializable, and stable over time
|
||||
/// - Any change in internal implementation should not affect these models
|
||||
///
|
||||
/// Pattern:
|
||||
/// Each operation follows a consistent structure:
|
||||
/// Request → Operation → Result
|
||||
///
|
||||
/// Example:
|
||||
/// GciInitSlotRequest → InitSlot → GciInitSlotResult
|
||||
/// GetSlot → GciSlotInfo
|
||||
/// GetPcbId → GciGetPcbIdResult
|
||||
///
|
||||
/// This layer acts as a boundary between domain logic and integration logic.
|
||||
/// </summary>
|
||||
public class GciPublicModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Public DTOs exposed to external systems.
|
||||
/// These models represent the contract of the GCI API.
|
||||
/// They must remain stable and independent of internal implementation.
|
||||
/// </summary>
|
||||
#region ================================== PUBLIC MODELS ===========================================
|
||||
|
||||
public class GciSlotInfo
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
|
||||
public bool Exists { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string PcbId { get; set; }
|
||||
|
||||
public GciConfigSource ConfigSource { get; set; }
|
||||
public GciPasswordSource PasswordSource { get; set; }
|
||||
|
||||
public GciPortConfig RequestPort { get; set; }
|
||||
public GciPortConfig StreamingPort { get; set; }
|
||||
}
|
||||
|
||||
public class Result
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"SlotId={0}, Success={1}, Message={2}",
|
||||
SlotId,
|
||||
Success,
|
||||
Message);
|
||||
}
|
||||
}
|
||||
|
||||
public enum GciPasswordSource
|
||||
{
|
||||
RestApi = 0,
|
||||
OfflineFile = 1,
|
||||
InterfaceInputPassword = 2,
|
||||
}
|
||||
public enum GciConfigSource
|
||||
{
|
||||
FileConfig = 0,
|
||||
InterfaceInputConfig = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collection of port settings
|
||||
/// </summary>
|
||||
public class GciPortConfig
|
||||
{
|
||||
public string PortName { get; set; }
|
||||
public string Type { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("PortName={0}, Type={1}", PortName, Type);
|
||||
}
|
||||
}
|
||||
|
||||
public class GciInitSlotRequest
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
public GciConfigSource ConfigSource { get; set; }
|
||||
public GciPasswordSource PasswordSource { get; set; }
|
||||
public GciPortConfig RequestPort { get; set; }
|
||||
public GciPortConfig StreamingPort { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"SlotId={0}, GciConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}",
|
||||
SlotId,
|
||||
ConfigSource,
|
||||
PasswordSource,
|
||||
RequestPort,
|
||||
StreamingPort);
|
||||
}
|
||||
}
|
||||
|
||||
public class GciInitSlotResult
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
|
||||
public bool Success { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public string PcbId { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"SlotId={0}, Success={1}, Message={2}, PcbId={3}",
|
||||
SlotId,
|
||||
Success,
|
||||
Message,
|
||||
PcbId);
|
||||
}
|
||||
}
|
||||
|
||||
public class GciCleanSlotsResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"Success={0}, Message={1}",
|
||||
Success,
|
||||
Message);
|
||||
}
|
||||
}
|
||||
|
||||
public class GciGetPcbIdResult
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
|
||||
public bool Success { get; set; }
|
||||
|
||||
public string PcbId { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"SlotId={0}, Success={1}, PcbId={2}, Message={3}",
|
||||
SlotId,
|
||||
Success,
|
||||
PcbId,
|
||||
Message);
|
||||
}
|
||||
}
|
||||
public class GciConnectResult
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string PcbId { get; set; }
|
||||
public bool IsLoggedOn { get; set; }
|
||||
public string FwVersion { get; set; }
|
||||
public string InterfaceVersion { get; set; }
|
||||
public bool InterfaceSupportsFwVersion { get; set; }
|
||||
public List<GciRegisterSnapshot> Registers { get; set; } = new List<GciRegisterSnapshot>();
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class GciRegisterSnapshot
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string RawValue { get; set; }
|
||||
public string Min { get; set; }
|
||||
public string Max { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string IsAvailable { get; set; }
|
||||
public string Privilege { get; set; }
|
||||
}
|
||||
|
||||
public class GciDisconnectResult
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"SlotId={0}, Success={1}, Message={2}",
|
||||
SlotId,
|
||||
Success,
|
||||
Message);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Mapping methods between public DTOs and internal GCI models.
|
||||
/// Ensures separation between external contracts and internal domain objects.
|
||||
/// </summary>
|
||||
#region ================================== OUTERN/INTERN and back models mapping ==================================
|
||||
public static PasswordSource MapPasswordSource(GciPasswordSource src)
|
||||
{
|
||||
return (PasswordSource)src;
|
||||
}
|
||||
|
||||
public static ConfigSource MapConfigSource(GciConfigSource src)
|
||||
{
|
||||
return (ConfigSource)src;
|
||||
}
|
||||
|
||||
public static Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig? MapPort(GciPortConfig port)
|
||||
{
|
||||
if (port == null)
|
||||
return null;
|
||||
|
||||
Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig result = new Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig();
|
||||
|
||||
result.PortName = port.PortName;
|
||||
result.Type = port.Type;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static GciInitSlotResult MapInitResult(GciInitSlotResult result)
|
||||
{
|
||||
if (result == null)
|
||||
return null;
|
||||
|
||||
return new GciInitSlotResult
|
||||
{
|
||||
SlotId = result.SlotId,
|
||||
Success = result.Success,
|
||||
Message = result.Message
|
||||
};
|
||||
}
|
||||
|
||||
public static GciPasswordSource MapPasswordSourceBack(PasswordSource src)
|
||||
{
|
||||
return (GciPasswordSource)src;
|
||||
}
|
||||
|
||||
public static GciConfigSource MapConfigSourceBack(ConfigSource src)
|
||||
{
|
||||
return (GciConfigSource)src;
|
||||
}
|
||||
|
||||
public static GciPortConfig MapPortBack(PortConfig? port)
|
||||
{
|
||||
if (!port.HasValue)
|
||||
return null;
|
||||
|
||||
PortConfig value = port.Value;
|
||||
|
||||
return new GciPortConfig
|
||||
{
|
||||
PortName = value.PortName,
|
||||
Type = value.Type
|
||||
};
|
||||
}
|
||||
|
||||
public static GciPortConfig MapPortBack(IPort port)
|
||||
{
|
||||
if (port == null)
|
||||
return null;
|
||||
|
||||
return new GciPortConfig
|
||||
{
|
||||
PortName = port.GetPortName(),
|
||||
Type = port.GetType().Name
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
||||
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
||||
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.API
|
||||
{
|
||||
@ -14,111 +14,346 @@ namespace GenesisCordonelInterface.API
|
||||
{
|
||||
private readonly InterfaceGCIToLaatzen _innerMeterAPI;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InterfaceOutsideToGCI"/> class.
|
||||
/// </summary>
|
||||
public event Action<List<InterfaceGCIToLaatzen.MeterBatchDebugStatus>> MeterBatchStatusChanged;
|
||||
|
||||
public InterfaceOutsideToGCI()
|
||||
{
|
||||
_innerMeterAPI = new InterfaceGCIToLaatzen();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the meter is currently connected and logged on.
|
||||
/// </summary>
|
||||
public bool IsConnected
|
||||
#region ================================== PORT DETECTION ==================================
|
||||
|
||||
public InterfaceGCIToLaatzen.PortDetectionResult DetectStreamingPort(int slot)
|
||||
{
|
||||
get
|
||||
{
|
||||
return _innerMeterAPI.IsConnected;
|
||||
}
|
||||
var result = _innerMeterAPI.DetectStreamingPort(slot);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the meter on the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slotNo">Slot number.</param>
|
||||
/// <param name="usePasswordsSource">Specifies whether offline passwords should be used.</param>
|
||||
/// <returns>Connect operation result.</returns>
|
||||
public InterfaceGCIToLaatzen.InitResult InitOneMeterFromExtern(Int32 slotNo, ConfigSource useConfigSource, PasswordSource usePasswordSource, PortConfig? requestPort, PortConfig? streamingPort)
|
||||
public async Task<InterfaceGCIToLaatzen.PortDetectionResult> DetectStreamingPortAsync(
|
||||
int slot,
|
||||
CancellationToken token = default(CancellationToken))
|
||||
{
|
||||
return _innerMeterAPI.InitOneMeterFromExtern(slotNo, useConfigSource, usePasswordSource, requestPort, streamingPort);
|
||||
var result = await _innerMeterAPI.DetectStreamingPortAsync(slot, token);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the meter on the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slotNo">Slot number.</param>
|
||||
/// <param name="usePasswordsSource">Specifies whether offline passwords should be used.</param>
|
||||
/// <returns>Connect operation result.</returns>
|
||||
public InterfaceGCIToLaatzen.ConnectResult ConnectOneMeter(int slotNo)
|
||||
public InterfaceGCIToLaatzen.PortDetectionResult DetectRequestPort(int slot)
|
||||
{
|
||||
return _innerMeterAPI.ConnectOneMeter(slotNo);
|
||||
var result = _innerMeterAPI.DetectRequestPort(slot);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the meter on the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slotNo">Slot number.</param>
|
||||
/// <param name="usePasswordsSource">Specifies whether offline passwords should be used.</param>
|
||||
/// <returns>Connect operation result.</returns>
|
||||
public InterfaceGCIToLaatzen.ConnectResult ConnectAllMeters(int slotNo)
|
||||
public async Task<InterfaceGCIToLaatzen.PortDetectionResult> DetectRequestPortAsync(
|
||||
int slot,
|
||||
CancellationToken token = default(CancellationToken))
|
||||
{
|
||||
return _innerMeterAPI.ConnectAllMeters(slotNo);
|
||||
var result = await _innerMeterAPI.DetectRequestPortAsync(slot, token);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects from the currently connected meter.
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
#endregion
|
||||
|
||||
#region ================================== INIT ==================================
|
||||
|
||||
public async Task<GciPublicModels.GciInitSlotResult> InitSlotAsync(GciPublicModels.GciInitSlotRequest request, CancellationToken token = default)
|
||||
{
|
||||
_innerMeterAPI.Disconnect();
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
|
||||
var result = await _innerMeterAPI.InitOneMeterFromExternAsync(
|
||||
request.SlotId,
|
||||
GciPublicModels.MapConfigSource(request.ConfigSource),
|
||||
GciPublicModels.MapPasswordSource(request.PasswordSource),
|
||||
GciPublicModels.MapPort(request.RequestPort),
|
||||
GciPublicModels.MapPort(request.StreamingPort),
|
||||
token);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads PCB ID from the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot number.</param>
|
||||
/// <returns>PCB ID string.</returns>
|
||||
public string GetPcbId(int slot)
|
||||
public async Task<GciPublicModels.GciSlotInfo> GetSlotAsync(
|
||||
int slotId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.GetPcbId(slot);
|
||||
if (slotId <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await _innerMeterAPI.GetOneMeterInfo(slotId, token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a register by name.
|
||||
/// </summary>
|
||||
/// <param name="registerName">Register name.</param>
|
||||
/// <returns>Register read result.</returns>
|
||||
public InterfaceGCIToLaatzen.RegisterReadResult ReadRegister(string registerName)
|
||||
public async Task<GciPublicModels.GciCleanSlotsResult> CleanSlotsAsync(
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.ReadRegister(registerName);
|
||||
var result = await _innerMeterAPI.CleanSlotsAsync(token);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ================================== CONNECTION ==================================
|
||||
|
||||
public async Task<GciPublicModels.GciConnectResult> ConnectOneSlotAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
GciPublicModels.GciConnectResult result = await _innerMeterAPI.ConnectOneSlotAsync(slot, token);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a value to a register.
|
||||
/// </summary>
|
||||
/// <param name="registerName">Register name.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
/// <param name="storeToDevice">Specifies whether configuration should be stored after write.</param>
|
||||
/// <param name="refreshSystemState">Specifies whether system state refresh should be triggered after write.</param>
|
||||
/// <returns>Register write result.</returns>
|
||||
public InterfaceGCIToLaatzen.RegisterWriteResult WriteRegister(
|
||||
public async Task<GciPublicModels.GciDisconnectResult> DisconnectAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await _innerMeterAPI.DisconnectAsync(slot, token);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ================================== PCB ==================================
|
||||
public async Task<GciPublicModels.GciGetPcbIdResult> GetPcbIdAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (slot <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await _innerMeterAPI.GetPcbIdAsync(slot, token);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ================================== READ ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
public InterfaceGCIToLaatzen.RegisterReadResult ReadRegister(
|
||||
int slot,
|
||||
string registerName)
|
||||
{
|
||||
return _innerMeterAPI.ReadRegister(slot, registerName);
|
||||
}
|
||||
|
||||
public Task<InterfaceGCIToLaatzen.RegisterReadResult> ReadRegisterAsync(
|
||||
int slot,
|
||||
string registerName,
|
||||
CancellationToken token = default(CancellationToken))
|
||||
{
|
||||
return _innerMeterAPI.ReadRegisterAsync(slot, registerName, token);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== WRITE ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
public async Task<InterfaceGCIToLaatzen.RegisterWriteResult> WriteRegisterAsync(
|
||||
int slot,
|
||||
string registerName,
|
||||
object value,
|
||||
bool storeToDevice = false,
|
||||
bool refreshSystemState = false)
|
||||
{
|
||||
return _innerMeterAPI.WriteRegister(registerName, value, storeToDevice, refreshSystemState);
|
||||
var result = await _innerMeterAPI.WriteRegisterAsync(
|
||||
slot,
|
||||
registerName,
|
||||
value,
|
||||
storeToDevice,
|
||||
refreshSystemState);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets meter password.
|
||||
/// </summary>
|
||||
/// <param name="password">Password value.</param>
|
||||
/// <returns>True if operation succeeded; otherwise false.</returns>
|
||||
public bool SetMeterPassword(string password)
|
||||
public InterfaceGCIToLaatzen.RegisterWriteResult WriteRegister(
|
||||
int slot,
|
||||
string registerName,
|
||||
object value,
|
||||
bool storeToDevice = false,
|
||||
bool refreshSystemState = false)
|
||||
{
|
||||
return _innerMeterAPI.SetMeterPassword(password);
|
||||
var result = _innerMeterAPI.WriteRegister(
|
||||
slot,
|
||||
registerName,
|
||||
value,
|
||||
storeToDevice,
|
||||
refreshSystemState);
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> SetMeterPasswordAsync(int slot, string password)
|
||||
{
|
||||
var result = await _innerMeterAPI.SetMeterPasswordAsync(slot, password);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool SetMeterPassword(int slot, string password)
|
||||
{
|
||||
var result = _innerMeterAPI.SetMeterPassword(slot, password);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== DEBUG STATUS ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
public List<InterfaceGCIToLaatzen.WorkerDebugStatus> GetWorkerDebugStatuses()
|
||||
{
|
||||
return _innerMeterAPI.GetWorkerDebugStatuses();
|
||||
}
|
||||
|
||||
public List<InterfaceGCIToLaatzen.MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
|
||||
{
|
||||
return _innerMeterAPI.GetMeterBatchDebugStatuses();
|
||||
}
|
||||
|
||||
public void RaiseMeterBatchStatusChanged()
|
||||
{
|
||||
var statuses = GetMeterBatchDebugStatuses();
|
||||
|
||||
var handler = MeterBatchStatusChanged;
|
||||
if (handler != null)
|
||||
handler(statuses);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== SLOT SELECTION ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
public void SetSlotSelected(int slot, bool selected)
|
||||
{
|
||||
_innerMeterAPI.SetSlotSelected(slot, selected);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
public bool IsSlotSelected(int slot)
|
||||
{
|
||||
return _innerMeterAPI.IsSlotSelected(slot);
|
||||
}
|
||||
|
||||
public List<int> 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<InterfaceGCIToLaatzen.MeterBatchDebugStatus> data)
|
||||
{
|
||||
_innerMeterAPI.SaveSlotSetup(data);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
314
GenesisCordonelInterface/Core/Threading/ApiWorker/ApiWorker.cs
Normal file
314
GenesisCordonelInterface/Core/Threading/ApiWorker/ApiWorker.cs
Normal file
@ -0,0 +1,314 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.Threading
|
||||
{
|
||||
/*
|
||||
ApiWorker – per-slot sequential execution worker
|
||||
|
||||
This class provides a lightweight background worker that executes actions
|
||||
sequentially on a dedicated thread.
|
||||
|
||||
PRIMARY PURPOSE
|
||||
---------------
|
||||
ApiWorker is designed to safely execute hardware-related operations
|
||||
(e.g. meter communication) without blocking the UI thread and without
|
||||
allowing concurrent access to the same device.
|
||||
|
||||
Each ApiWorker instance typically represents:
|
||||
1 worker = 1 slot = 1 meter = 1 communication channel
|
||||
|
||||
KEY PROPERTIES
|
||||
--------------
|
||||
- Single dedicated background thread
|
||||
- FIFO queue (first-in, first-out)
|
||||
- Sequential execution (NO parallelism inside one worker)
|
||||
- Thread-safe enqueueing
|
||||
- Task-based async interface for callers
|
||||
|
||||
WHY THIS IS IMPORTANT
|
||||
--------------------
|
||||
Hardware communication (serial ports, meters, etc.) is usually NOT thread-safe.
|
||||
If multiple commands are executed in parallel, communication may break or corrupt data.
|
||||
|
||||
ApiWorker guarantees:
|
||||
- operations are executed one-by-one
|
||||
- order is preserved
|
||||
- no race conditions on the device
|
||||
|
||||
HIGH-LEVEL FLOW
|
||||
---------------
|
||||
Caller (UI/API)
|
||||
|
|
||||
v
|
||||
RunAsync(...)
|
||||
|
|
||||
v
|
||||
TaskCompletionSource created
|
||||
|
|
||||
v
|
||||
Action wrapped into queue item
|
||||
|
|
||||
v
|
||||
Added to BlockingCollection queue
|
||||
|
|
||||
v
|
||||
Worker thread consumes queue
|
||||
|
|
||||
v
|
||||
Action executed (blocking HW call)
|
||||
|
|
||||
v
|
||||
Result propagated via TaskCompletionSource
|
||||
|
|
||||
v
|
||||
Caller receives result via await
|
||||
|
||||
GRAPH
|
||||
-----
|
||||
Caller thread (UI)
|
||||
|
|
||||
v
|
||||
RunAsync()
|
||||
|
|
||||
v
|
||||
Queue (BlockingCollection)
|
||||
|
|
||||
v
|
||||
-----------------------------
|
||||
| Worker Thread (background)|
|
||||
| while(queue) |
|
||||
| Execute Action |
|
||||
-----------------------------
|
||||
|
|
||||
v
|
||||
Task result (await)
|
||||
|
||||
THREADING MODEL
|
||||
---------------
|
||||
- Producer/Consumer pattern
|
||||
- Producer: any thread calling RunAsync
|
||||
- Consumer: single worker thread
|
||||
- Synchronization handled by BlockingCollection
|
||||
|
||||
MAIN COMPONENTS
|
||||
---------------
|
||||
1. BlockingCollection<Action> queue
|
||||
- thread-safe queue
|
||||
- stores work items
|
||||
- supports blocking consumption
|
||||
|
||||
2. Dedicated Thread
|
||||
- runs WorkerLoop()
|
||||
- continuously processes queue
|
||||
|
||||
3. TaskCompletionSource<T>
|
||||
- bridges sync execution → async API
|
||||
- allows caller to await result
|
||||
|
||||
METHODS
|
||||
-------
|
||||
|
||||
RunAsync<T>(Func<T>)
|
||||
--------------------
|
||||
- Enqueues a function returning a value
|
||||
- Wraps it into Action
|
||||
- Executes on worker thread
|
||||
- Returns Task<T> to caller
|
||||
|
||||
RunAsync(Action)
|
||||
----------------
|
||||
- Convenience overload for void methods
|
||||
- Internally wraps into Func<object>
|
||||
|
||||
WorkerLoop()
|
||||
------------
|
||||
- Infinite loop consuming queue
|
||||
- Executes actions one-by-one
|
||||
- Stops when queue is completed
|
||||
|
||||
Dispose()
|
||||
---------
|
||||
- Stops accepting new items
|
||||
- Cleans up queue
|
||||
- Does NOT forcibly stop running task
|
||||
|
||||
CANCELLATION
|
||||
------------
|
||||
- CancellationToken is checked BEFORE execution
|
||||
- If cancelled → Task is cancelled
|
||||
- Does NOT interrupt running operation
|
||||
|
||||
IMPORTANT LIMITATIONS
|
||||
--------------------
|
||||
- No parallel execution inside one worker (by design)
|
||||
- Long-running action blocks worker thread
|
||||
- No built-in timeout handling
|
||||
- Dispose does not abort running work
|
||||
|
||||
WHEN TO USE
|
||||
-----------
|
||||
✔ Per-device communication (serial, TCP, HW)
|
||||
✔ Ordered execution required
|
||||
✔ UI must stay responsive
|
||||
|
||||
WHEN NOT TO USE
|
||||
---------------
|
||||
✘ CPU parallel processing (use Task.Run / Parallel)
|
||||
✘ High-throughput parallel workloads
|
||||
✘ Fire-and-forget background tasks
|
||||
|
||||
SUMMARY
|
||||
-------
|
||||
ApiWorker is a simple, robust solution for:
|
||||
"Execute commands sequentially per resource, asynchronously from UI"
|
||||
|
||||
It is a perfect fit for:
|
||||
- hardware interfaces
|
||||
- device drivers
|
||||
- IO-bound serialized workflows
|
||||
*/
|
||||
|
||||
public sealed class ApiWorker : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Thread-safe FIFO queue holding work items.
|
||||
/// </summary>
|
||||
private readonly BlockingCollection<Action> queue = new BlockingCollection<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Dedicated worker thread processing the queue.
|
||||
/// </summary>
|
||||
private readonly Thread thread;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this worker has been disposed.
|
||||
/// </summary>
|
||||
private bool disposed;
|
||||
|
||||
public string Name { get; private set; }
|
||||
public int QueueLength { get { return queue.Count; } }
|
||||
public bool IsBusy { get; private set; }
|
||||
public string CurrentOperation { get; private set; }
|
||||
public string LastError { get; private set; }
|
||||
public DateTime LastActivity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ApiWorker with its own background thread.
|
||||
/// </summary>
|
||||
public ApiWorker(string name)
|
||||
{
|
||||
Name = name;
|
||||
LastActivity = DateTime.Now;
|
||||
|
||||
thread = new Thread(WorkerLoop)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = name
|
||||
};
|
||||
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
{
|
||||
if (action == null)
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException(nameof(ApiWorker));
|
||||
|
||||
var tcs = new TaskCompletionSource<T>();
|
||||
|
||||
queue.Add(() =>
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
tcs.TrySetCanceled();
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public Task RunAsync(
|
||||
Action action,
|
||||
CancellationToken token = default(CancellationToken),
|
||||
string operationName = null)
|
||||
{
|
||||
return RunAsync<object>(() =>
|
||||
{
|
||||
action();
|
||||
return null;
|
||||
}, token, operationName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues a void action for sequential execution.
|
||||
/// </summary>
|
||||
public Task RunAsync(Action action, CancellationToken token = default)
|
||||
{
|
||||
return RunAsync<object>(() =>
|
||||
{
|
||||
action();
|
||||
return null;
|
||||
}, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main worker loop processing queued actions.
|
||||
/// </summary>
|
||||
private void WorkerLoop()
|
||||
{
|
||||
foreach (var item in queue.GetConsumingEnumerable())
|
||||
{
|
||||
item();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the worker and releases resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
disposed = true;
|
||||
|
||||
queue.CompleteAdding();
|
||||
queue.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,8 +58,26 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="API\InterfaceOutsideToGCI.cs" />
|
||||
<Compile Include="API\InterfaceGCIToLaatzen.cs" />
|
||||
<Compile Include="API\GciPublicModels.cs" />
|
||||
<Compile Include="Core\Logging\UiLogBus.cs" />
|
||||
<Compile Include="Core\Logging\UiTarget.cs" />
|
||||
<Compile Include="Core\Threading\ApiWorker\ApiWorker.cs" />
|
||||
<Compile Include="UI\Debug\MeterBatchConfigPanel.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UI\Debug\MeterBatchConfigPanel.Designer.cs">
|
||||
<DependentUpon>MeterBatchConfigPanel.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI\Debug\WorkerDebugPanel.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UI\Debug\WorkerDebugPanel.Designer.cs">
|
||||
<DependentUpon>WorkerDebugPanel.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI\Grid\MeterGridColumnConfig.cs" />
|
||||
<Compile Include="UI\Grid\MeterGridConfigProvider.cs" />
|
||||
<Compile Include="UI\Grid\MeterGridManager.cs" />
|
||||
<Compile Include="UI\Grid\MeterRowDto.cs" />
|
||||
<Compile Include="UI\LaatzenAPI_GenesisToolBox\FrmConfigurations.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
@ -98,12 +116,31 @@
|
||||
<Compile Include="UI\LaatzenAPI_CordonelPreadjustmentUI\PreAdjustmentControl.Designer.cs">
|
||||
<DependentUpon>PreAdjustmentControl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI\MainView.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UI\MainView.Designer.cs">
|
||||
<DependentUpon>MainView.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.Designer.cs">
|
||||
<DependentUpon>FrmGCIAPI.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MeterInitView.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MeterInitView.Designer.cs">
|
||||
<DependentUpon>MeterInitView.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MetersActionView.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MetersActionView.Designer.cs">
|
||||
<DependentUpon>MetersActionView.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Content Include="RuntimePackage\Build\Copy.targets.xml" />
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
@ -145,8 +182,7 @@
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Core\AsTbfComponent\" />
|
||||
<Folder Include="UI\AsTbfComponent\" />
|
||||
<Folder Include="RuntimePackage\Package\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<WCFMetadata Include="Connected Services\" />
|
||||
@ -221,5 +257,7 @@
|
||||
<Name>Logging</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<!-- Import custom GCI runtime packaging logic -->
|
||||
<Import Project="RuntimePackage\Build\Copy.targets.xml" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@ -0,0 +1,37 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!--
|
||||
Creates a GCI runtime package after each successful build.
|
||||
The package contains the standalone executable and all runtime dependencies.
|
||||
-->
|
||||
<Target Name="CopyGciRuntimeToRuntimePackage" AfterTargets="Build">
|
||||
|
||||
<!-- Destination directory for the GCI runtime package -->
|
||||
<PropertyGroup>
|
||||
<GciRuntimePackageDir>$(ProjectDir)RuntimePackage\Package\</GciRuntimePackageDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Ensure destination directory exists -->
|
||||
<MakeDir Directories="$(GciRuntimePackageDir)" />
|
||||
|
||||
<!-- Collect runtime files from the project output directory -->
|
||||
<ItemGroup>
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.dll" />
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.exe" />
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.config" />
|
||||
<GciRuntimeFiles Include="$(TargetDir)*.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Build output messages -->
|
||||
<Message Text="Copying GCI runtime package to $(GciRuntimePackageDir)" Importance="High" />
|
||||
<Message Text="Files: @(GciRuntimeFiles)" Importance="Normal" />
|
||||
|
||||
<!-- Copy runtime files into the package directory -->
|
||||
<Copy
|
||||
SourceFiles="@(GciRuntimeFiles)"
|
||||
DestinationFolder="$(GciRuntimePackageDir)"
|
||||
SkipUnchangedFiles="false" />
|
||||
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
57
GenesisCordonelInterface/UI/Debug/MeterBatchConfigPanel.Designer.cs
generated
Normal file
57
GenesisCordonelInterface/UI/Debug/MeterBatchConfigPanel.Designer.cs
generated
Normal file
@ -0,0 +1,57 @@
|
||||
namespace GenesisCordonelInterface.UI.Debug
|
||||
{
|
||||
partial class MeterBatchConfigPanel
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
private System.Windows.Forms.DataGridView grid;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (components != null)
|
||||
components.Dispose();
|
||||
|
||||
if (api != null)
|
||||
api.MeterBatchStatusChanged -= Api_MeterBatchStatusChanged;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.grid = new System.Windows.Forms.DataGridView();
|
||||
|
||||
((System.ComponentModel.ISupportInitialize)(this.grid)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
|
||||
// grid
|
||||
this.grid.AllowUserToAddRows = false;
|
||||
this.grid.AllowUserToDeleteRows = false;
|
||||
this.grid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
|
||||
this.grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
this.grid.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.grid.Name = "grid";
|
||||
this.grid.RowHeadersWidth = 30;
|
||||
this.grid.TabIndex = 0;
|
||||
|
||||
// events
|
||||
this.grid.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.grid_CellValueChanged);
|
||||
this.grid.CurrentCellDirtyStateChanged += new System.EventHandler(this.grid_CurrentCellDirtyStateChanged);
|
||||
this.grid.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.grid_DataError);
|
||||
|
||||
// MeterBatchConfigPanel
|
||||
this.Controls.Add(this.grid);
|
||||
this.Name = "MeterBatchConfigPanel";
|
||||
this.Size = new System.Drawing.Size(600, 200);
|
||||
|
||||
((System.ComponentModel.ISupportInitialize)(this.grid)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
401
GenesisCordonelInterface/UI/Debug/MeterBatchConfigPanel.cs
Normal file
401
GenesisCordonelInterface/UI/Debug/MeterBatchConfigPanel.cs
Normal file
@ -0,0 +1,401 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using GenesisCordonelInterface.API;
|
||||
|
||||
namespace GenesisCordonelInterface.UI.Debug
|
||||
{
|
||||
public partial class MeterBatchConfigPanel : UserControl
|
||||
{
|
||||
private readonly InterfaceOutsideToGCI api;
|
||||
|
||||
private bool isRefreshing;
|
||||
private List<string> comPorts = new List<string>();
|
||||
|
||||
public MeterBatchConfigPanel(InterfaceOutsideToGCI api)
|
||||
{
|
||||
this.api = api;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
RefreshComPorts();
|
||||
InitializeGridColumns();
|
||||
|
||||
api.MeterBatchStatusChanged += Api_MeterBatchStatusChanged;
|
||||
|
||||
UpdateGrid(api.GetMeterBatchDebugStatuses());
|
||||
}
|
||||
|
||||
#region INIT
|
||||
// ----------------------------------------------------
|
||||
|
||||
private void InitializeGridColumns()
|
||||
{
|
||||
grid.Columns.Clear();
|
||||
|
||||
grid.Columns.Add("Slot", "Slot");
|
||||
grid.Columns.Add(new DataGridViewCheckBoxColumn { Name = "Selected", HeaderText = "Selected" });
|
||||
grid.Columns.Add("PcbId", "PcbId");
|
||||
grid.Columns.Add(new DataGridViewCheckBoxColumn { Name = "IsLoggedOn", HeaderText = "IsLoggedOn" });
|
||||
|
||||
grid.Columns.Add(CreateComPortColumn("RequestPort", "RequestPort"));
|
||||
grid.Columns.Add(CreateComPortColumn("StreamingPort", "StreamingPort"));
|
||||
|
||||
grid.Columns.Add(CreateButtonColumn("DetectRequest", "DetectRequest", "..."));
|
||||
grid.Columns.Add(CreateButtonColumn("DetectStreaming", "DetectStreaming", "..."));
|
||||
|
||||
grid.Columns.Add("FwVersion", "FwVersion");
|
||||
grid.Columns.Add("InterfaceVersion", "InterfaceVersion");
|
||||
|
||||
foreach (DataGridViewColumn col in grid.Columns)
|
||||
{
|
||||
col.ReadOnly =
|
||||
col.Name != "Selected" &&
|
||||
col.Name != "RequestPort" &&
|
||||
col.Name != "StreamingPort" &&
|
||||
col.Name != "DetectRequest" &&
|
||||
col.Name != "DetectStreaming";
|
||||
}
|
||||
|
||||
EnableDoubleBuffering(grid);
|
||||
}
|
||||
|
||||
private DataGridViewComboBoxColumn CreateComPortColumn(string name, string headerText)
|
||||
{
|
||||
return new DataGridViewComboBoxColumn
|
||||
{
|
||||
Name = name,
|
||||
HeaderText = headerText,
|
||||
DataSource = new List<string>(comPorts),
|
||||
FlatStyle = FlatStyle.Flat,
|
||||
DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton
|
||||
};
|
||||
}
|
||||
|
||||
private DataGridViewButtonColumn CreateButtonColumn(string name, string headerText, string text)
|
||||
{
|
||||
return new DataGridViewButtonColumn
|
||||
{
|
||||
Name = name,
|
||||
HeaderText = headerText,
|
||||
Text = text,
|
||||
UseColumnTextForButtonValue = true
|
||||
};
|
||||
}
|
||||
|
||||
private void RefreshComPorts()
|
||||
{
|
||||
comPorts = SerialPort.GetPortNames()
|
||||
.OrderBy(NaturalComPortOrder)
|
||||
.ToList();
|
||||
|
||||
if (!comPorts.Contains(""))
|
||||
comPorts.Insert(0, "");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region REFRESH EVENT DRIVEN
|
||||
// ----------------------------------------------------
|
||||
|
||||
private void Api_MeterBatchStatusChanged(
|
||||
List<InterfaceGCIToLaatzen.MeterBatchDebugStatus> data)
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action(() => UpdateGrid(data)));
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateGrid(data);
|
||||
}
|
||||
|
||||
private void UpdateGrid(List<InterfaceGCIToLaatzen.MeterBatchDebugStatus> data)
|
||||
{
|
||||
isRefreshing = true;
|
||||
|
||||
foreach (var meter in data)
|
||||
{
|
||||
EnsurePortValueExists(meter.RequestPort);
|
||||
EnsurePortValueExists(meter.StreamingPort);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
isRefreshing = false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region COM PORT HELPERS
|
||||
// ----------------------------------------------------
|
||||
|
||||
private void UpdateComPortColumnItems(string columnName)
|
||||
{
|
||||
var col = grid.Columns[columnName] as DataGridViewComboBoxColumn;
|
||||
if (col == null)
|
||||
return;
|
||||
|
||||
col.DataSource = null;
|
||||
col.DataSource = new List<string>(comPorts);
|
||||
}
|
||||
|
||||
private void EnsurePortValueExists(string port)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(port))
|
||||
return;
|
||||
|
||||
if (comPorts.Contains(port))
|
||||
return;
|
||||
|
||||
comPorts.Add(port);
|
||||
comPorts = comPorts.OrderBy(NaturalComPortOrder).ToList();
|
||||
|
||||
if (!comPorts.Contains(""))
|
||||
comPorts.Insert(0, "");
|
||||
|
||||
UpdateComPortColumnItems("RequestPort");
|
||||
UpdateComPortColumnItems("StreamingPort");
|
||||
}
|
||||
|
||||
private static int NaturalComPortOrder(string port)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(port))
|
||||
return 0;
|
||||
|
||||
string number = new string(port.Where(char.IsDigit).ToArray());
|
||||
|
||||
int parsed;
|
||||
if (int.TryParse(number, out parsed))
|
||||
return parsed;
|
||||
|
||||
return int.MaxValue;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region GRID HELPERS
|
||||
// ----------------------------------------------------
|
||||
|
||||
private DataGridViewRow FindOrCreateRow(int slot)
|
||||
{
|
||||
foreach (DataGridViewRow row in grid.Rows)
|
||||
{
|
||||
if (row.Cells["Slot"].Value != null &&
|
||||
Convert.ToInt32(row.Cells["Slot"].Value) == slot)
|
||||
{
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
int idx = grid.Rows.Add();
|
||||
var newRow = grid.Rows[idx];
|
||||
newRow.Cells["Slot"].Value = slot;
|
||||
return newRow;
|
||||
}
|
||||
|
||||
private void Set(DataGridViewRow row, string col, object value)
|
||||
{
|
||||
if (!grid.Columns.Contains(col))
|
||||
return;
|
||||
|
||||
if (value == null)
|
||||
value = "";
|
||||
|
||||
var cell = row.Cells[col];
|
||||
|
||||
if (!Equals(cell.Value, value))
|
||||
cell.Value = value;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region USER EDIT
|
||||
// ----------------------------------------------------
|
||||
|
||||
private void grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (grid.IsCurrentCellDirty)
|
||||
grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
}
|
||||
|
||||
private void grid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
if (isRefreshing)
|
||||
return;
|
||||
|
||||
if (e.RowIndex < 0)
|
||||
return;
|
||||
|
||||
string columnName = grid.Columns[e.ColumnIndex].Name;
|
||||
|
||||
if (columnName != "Selected" &&
|
||||
columnName != "RequestPort" &&
|
||||
columnName != "StreamingPort")
|
||||
return;
|
||||
|
||||
int slot = Convert.ToInt32(grid.Rows[e.RowIndex].Cells["Slot"].Value);
|
||||
|
||||
if (columnName == "Selected")
|
||||
{
|
||||
bool selected = Convert.ToBoolean(grid.Rows[e.RowIndex].Cells["Selected"].Value);
|
||||
api.SetSlotSelected(slot, selected);
|
||||
return;
|
||||
}
|
||||
|
||||
string port = Convert.ToString(grid.Rows[e.RowIndex].Cells[columnName].Value);
|
||||
|
||||
if (columnName == "RequestPort")
|
||||
{
|
||||
//api.SetSlotRequestPort(slot, port);
|
||||
return;
|
||||
}
|
||||
|
||||
if (columnName == "StreamingPort")
|
||||
{
|
||||
//api.SetSlotStreamingPort(slot, port);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private async void grid_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
if (e.RowIndex < 0)
|
||||
return;
|
||||
|
||||
string columnName = grid.Columns[e.ColumnIndex].Name;
|
||||
|
||||
if (columnName != "DetectRequest" &&
|
||||
columnName != "DetectStreaming")
|
||||
return;
|
||||
|
||||
int slot = Convert.ToInt32(grid.Rows[e.RowIndex].Cells["Slot"].Value);
|
||||
|
||||
if (columnName == "DetectRequest")
|
||||
{
|
||||
await DetectRequestPortAsync(slot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (columnName == "DetectStreaming")
|
||||
{
|
||||
await DetectStreamingPortAsync(slot);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DetectRequestPortAsync(int slot)
|
||||
{
|
||||
try
|
||||
{
|
||||
grid.Enabled = false;
|
||||
await api.DetectRequestPortAsync(slot);
|
||||
api.RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
finally
|
||||
{
|
||||
grid.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DetectStreamingPortAsync(int slot)
|
||||
{
|
||||
try
|
||||
{
|
||||
grid.Enabled = false;
|
||||
await api.DetectStreamingPortAsync(slot);
|
||||
api.RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
finally
|
||||
{
|
||||
grid.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void grid_DataError(object sender, DataGridViewDataErrorEventArgs e)
|
||||
{
|
||||
e.ThrowException = false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region PERFORMANCE
|
||||
// ----------------------------------------------------
|
||||
|
||||
private void EnableDoubleBuffering(DataGridView dgv)
|
||||
{
|
||||
typeof(DataGridView)
|
||||
.GetProperty(
|
||||
"DoubleBuffered",
|
||||
System.Reflection.BindingFlags.Instance |
|
||||
System.Reflection.BindingFlags.NonPublic)
|
||||
?.SetValue(dgv, true, null);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
public void AddEmptySlotRow()
|
||||
{
|
||||
int nextSlot = 1;
|
||||
|
||||
var existing = grid.Rows
|
||||
.Cast<DataGridViewRow>()
|
||||
.Where(r => r.Cells["Slot"].Value != null)
|
||||
.Select(r => Convert.ToInt32(r.Cells["Slot"].Value))
|
||||
.ToList();
|
||||
|
||||
if (existing.Count > 0)
|
||||
nextSlot = existing.Max() + 1;
|
||||
|
||||
int idx = grid.Rows.Add();
|
||||
var row = grid.Rows[idx];
|
||||
|
||||
row.Cells["Slot"].Value = nextSlot;
|
||||
row.Cells["Selected"].Value = true;
|
||||
row.Cells["RequestPort"].Value = "";
|
||||
row.Cells["StreamingPort"].Value = "";
|
||||
}
|
||||
|
||||
public List<InterfaceGCIToLaatzen.MeterBatchDebugStatus> GetGridData()
|
||||
{
|
||||
var list = new List<InterfaceGCIToLaatzen.MeterBatchDebugStatus>();
|
||||
|
||||
foreach (DataGridViewRow row in grid.Rows)
|
||||
{
|
||||
if (row.Cells["Slot"].Value == null)
|
||||
continue;
|
||||
|
||||
list.Add(new InterfaceGCIToLaatzen.MeterBatchDebugStatus
|
||||
{
|
||||
Slot = Convert.ToInt32(row.Cells["Slot"].Value),
|
||||
Selected = Convert.ToBoolean(row.Cells["Selected"].Value),
|
||||
RequestPort = Convert.ToString(row.Cells["RequestPort"].Value),
|
||||
StreamingPort = Convert.ToString(row.Cells["StreamingPort"].Value)
|
||||
});
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
GenesisCordonelInterface/UI/Debug/WorkerDebugPanel.Designer.cs
generated
Normal file
37
GenesisCordonelInterface/UI/Debug/WorkerDebugPanel.Designer.cs
generated
Normal file
@ -0,0 +1,37 @@
|
||||
namespace GenesisCordonelInterface.UI.Debug
|
||||
{
|
||||
partial class WorkerDebugPanel
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
components = new System.ComponentModel.Container();
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
51
GenesisCordonelInterface/UI/Debug/WorkerDebugPanel.cs
Normal file
51
GenesisCordonelInterface/UI/Debug/WorkerDebugPanel.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using GenesisCordonelInterface.API;
|
||||
|
||||
namespace GenesisCordonelInterface.UI.Debug
|
||||
{
|
||||
public partial class WorkerDebugPanel : UserControl
|
||||
{
|
||||
private readonly InterfaceOutsideToGCI api;
|
||||
private readonly Timer timer = new Timer();
|
||||
private readonly DataGridView grid = new DataGridView();
|
||||
|
||||
public WorkerDebugPanel(InterfaceOutsideToGCI api)
|
||||
{
|
||||
this.api = api;
|
||||
|
||||
grid.Dock = DockStyle.Fill;
|
||||
grid.ReadOnly = true;
|
||||
grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
|
||||
|
||||
Controls.Add(grid);
|
||||
|
||||
timer.Interval = 300;
|
||||
timer.Tick += (s, e) =>
|
||||
{
|
||||
grid.DataSource = null;
|
||||
grid.DataSource = api.GetWorkerDebugStatuses();
|
||||
};
|
||||
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void InitializeLayout()
|
||||
{
|
||||
grid.Dock = DockStyle.Fill;
|
||||
grid.ReadOnly = true;
|
||||
grid.AllowUserToAddRows = false;
|
||||
grid.AllowUserToDeleteRows = false;
|
||||
grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
|
||||
|
||||
Controls.Add(grid);
|
||||
Dock = DockStyle.Fill;
|
||||
}
|
||||
|
||||
private void Timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
grid.DataSource = null;
|
||||
grid.DataSource = api.GetWorkerDebugStatuses();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
GenesisCordonelInterface/UI/Grid/MeterGridColumnConfig.cs
Normal file
12
GenesisCordonelInterface/UI/Grid/MeterGridColumnConfig.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace GenesisCordonelInterface.UI.Grid
|
||||
{
|
||||
public class MeterGridColumnConfig
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string HeaderText { get; set; }
|
||||
public bool Visible { get; set; } = true;
|
||||
public int DisplayIndex { get; set; }
|
||||
public int Width { get; set; } = 80;
|
||||
public bool ReadOnly { get; set; } = false;
|
||||
}
|
||||
}
|
||||
22
GenesisCordonelInterface/UI/Grid/MeterGridConfigProvider.cs
Normal file
22
GenesisCordonelInterface/UI/Grid/MeterGridConfigProvider.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GenesisCordonelInterface.UI.Grid
|
||||
{
|
||||
public static class MeterGridConfigProvider
|
||||
{
|
||||
public static List<MeterGridColumnConfig> GetDefault()
|
||||
{
|
||||
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 = "PcbId", HeaderText = "PcbId", DisplayIndex = 2, Width = 80 },
|
||||
new MeterGridColumnConfig { Name = "IsLoggedOn", HeaderText = "IsLoggedOn", DisplayIndex = 3, Width = 80 },
|
||||
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 },
|
||||
new MeterGridColumnConfig { Name = "InterfaceVersion", HeaderText = "InterfaceVersion", DisplayIndex = 7, Width = 110 },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
107
GenesisCordonelInterface/UI/Grid/MeterGridManager.cs
Normal file
107
GenesisCordonelInterface/UI/Grid/MeterGridManager.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace GenesisCordonelInterface.UI.Grid
|
||||
{
|
||||
public class MeterGridManager
|
||||
{
|
||||
private readonly DataGridView grid;
|
||||
|
||||
public MeterGridManager(DataGridView grid)
|
||||
{
|
||||
this.grid = grid;
|
||||
EnableDoubleBuffering(grid);
|
||||
}
|
||||
|
||||
public void Init(List<MeterGridColumnConfig> columns)
|
||||
{
|
||||
grid.SuspendLayout();
|
||||
|
||||
grid.AutoGenerateColumns = false;
|
||||
grid.Columns.Clear();
|
||||
|
||||
grid.AllowUserToAddRows = false;
|
||||
grid.AllowUserToDeleteRows = false;
|
||||
grid.RowHeadersVisible = true;
|
||||
|
||||
foreach (var cfg in columns.OrderBy(c => c.DisplayIndex))
|
||||
{
|
||||
DataGridViewColumn col;
|
||||
|
||||
if (cfg.Name == "Selected" || cfg.Name == "IsLoggedOn")
|
||||
col = new DataGridViewCheckBoxColumn();
|
||||
else
|
||||
col = new DataGridViewTextBoxColumn();
|
||||
|
||||
col.Name = cfg.Name;
|
||||
col.HeaderText = cfg.HeaderText;
|
||||
col.Visible = cfg.Visible;
|
||||
col.Width = cfg.Width;
|
||||
col.ReadOnly = cfg.ReadOnly;
|
||||
|
||||
grid.Columns.Add(col);
|
||||
}
|
||||
|
||||
grid.ResumeLayout();
|
||||
}
|
||||
|
||||
public void Update(List<MeterRowDto> meters)
|
||||
{
|
||||
grid.SuspendLayout();
|
||||
|
||||
foreach (var meter in meters)
|
||||
{
|
||||
var row = FindOrCreateRow(meter.Slot);
|
||||
|
||||
SetCell(row, "Slot", meter.Slot);
|
||||
SetCell(row, "Selected", meter.Selected);
|
||||
SetCell(row, "PcbId", meter.PcbId);
|
||||
SetCell(row, "IsLoggedOn", meter.IsLoggedOn);
|
||||
SetCell(row, "RequestPort", meter.RequestPort);
|
||||
SetCell(row, "StreamingPort", meter.StreamingPort);
|
||||
SetCell(row, "FwVersion", meter.FwVersion);
|
||||
SetCell(row, "InterfaceVersion", meter.InterfaceVersion);
|
||||
}
|
||||
|
||||
grid.ResumeLayout();
|
||||
}
|
||||
|
||||
private DataGridViewRow FindOrCreateRow(int slot)
|
||||
{
|
||||
foreach (DataGridViewRow row in grid.Rows)
|
||||
{
|
||||
if (row.Cells["Slot"].Value != null &&
|
||||
Convert.ToInt32(row.Cells["Slot"].Value) == slot)
|
||||
{
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
int idx = grid.Rows.Add();
|
||||
var newRow = grid.Rows[idx];
|
||||
newRow.Cells["Slot"].Value = slot;
|
||||
return newRow;
|
||||
}
|
||||
|
||||
private void SetCell(DataGridViewRow row, string colName, object value)
|
||||
{
|
||||
if (!grid.Columns.Contains(colName))
|
||||
return;
|
||||
|
||||
var cell = row.Cells[colName];
|
||||
|
||||
if (!Equals(cell.Value, value))
|
||||
cell.Value = value;
|
||||
}
|
||||
|
||||
private void EnableDoubleBuffering(DataGridView dgv)
|
||||
{
|
||||
typeof(DataGridView)
|
||||
.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?.SetValue(dgv, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
GenesisCordonelInterface/UI/Grid/MeterRowDto.cs
Normal file
14
GenesisCordonelInterface/UI/Grid/MeterRowDto.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace GenesisCordonelInterface.UI.Grid
|
||||
{
|
||||
public class MeterRowDto
|
||||
{
|
||||
public int Slot { get; set; }
|
||||
public bool Selected { get; set; }
|
||||
public string PcbId { get; set; }
|
||||
public bool IsLoggedOn { get; set; }
|
||||
public string RequestPort { get; set; }
|
||||
public string StreamingPort { get; set; }
|
||||
public string FwVersion { get; set; }
|
||||
public string InterfaceVersion { get; set; }
|
||||
}
|
||||
}
|
||||
@ -492,7 +492,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
DisableAllButtons();
|
||||
_dataTable.Rows.Clear();
|
||||
|
||||
var result = await Task.Run(() => interfaceToLaatzen.ConnectOneMeter(slotNo));
|
||||
var result = await interfaceToLaatzen.ConnectOneSlotAsync(slotNo);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
@ -571,7 +571,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
lblState.Text = $@"Not Connected to PcbId:{result.PcbId}";
|
||||
registerGridView.Visible = false;
|
||||
|
||||
MessageBox.Show(result.ErrorMessage ?? "Connect failed.", @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
MessageBox.Show(result.Message ?? "Connect failed.", @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -857,7 +857,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
ForeColor = Color.Black
|
||||
};
|
||||
|
||||
SetProgress($"Read File {filename}");
|
||||
SetProgress($"Read FileConfig {filename}");
|
||||
var text = File.ReadAllText(filename);
|
||||
var loadedRegStore = JsonConvert.DeserializeObject<regStore>(text);
|
||||
|
||||
@ -941,7 +941,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
}
|
||||
else
|
||||
{
|
||||
((DataRow)rowItem)["RawValueFile"] = "Not in File";
|
||||
((DataRow)rowItem)["RawValueFile"] = "Not in FileConfig";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -949,7 +949,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
|
||||
private void ShowFile(String filename)
|
||||
{
|
||||
SetProgress($"Read File {filename}");
|
||||
SetProgress($"Read FileConfig {filename}");
|
||||
var text = File.ReadAllText(filename);
|
||||
var loadedRegStore = JsonConvert.DeserializeObject<regStore>(text);
|
||||
|
||||
@ -971,7 +971,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
}
|
||||
else
|
||||
{
|
||||
((DataRow)rowItem)["RawValueFile"] = "Not in File";
|
||||
((DataRow)rowItem)["RawValueFile"] = "Not in FileConfig";
|
||||
}
|
||||
|
||||
done += 1;
|
||||
@ -981,8 +981,8 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
|
||||
private void LoadFile(String filename)
|
||||
{
|
||||
//SetProgreess($"Read File {filename}");
|
||||
//var text = File.ReadAllText(filename);
|
||||
//SetProgreess($"Read FileConfig {filename}");
|
||||
//var text = FileConfig.ReadAllText(filename);
|
||||
//var loadedRegStore = Newtonsoft.Json.JsonConvert.DeserializeObject<regStore>(text);
|
||||
//if (loadedRegStore.PcbId != _currentPcbId)
|
||||
//{
|
||||
@ -1015,7 +1015,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
//{
|
||||
|
||||
// if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
||||
// File.WriteAllText(saveFileDialog.FileName, text);
|
||||
// FileConfig.WriteAllText(saveFileDialog.FileName, text);
|
||||
|
||||
//}));
|
||||
|
||||
@ -1172,11 +1172,16 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
|
||||
SetBusy(true, "GetPcbId");
|
||||
|
||||
// as new meter object will be generated the data grid shows outdated data
|
||||
_dataTable.Rows.Clear();
|
||||
DisableAllButtons();
|
||||
|
||||
_currentPcbId = await Task.Run(() => interfaceToLaatzen.GetPcbId(slotNr));
|
||||
GciPublicModels.GciGetPcbIdResult result =
|
||||
await interfaceToLaatzen.GetPcbIdAsync(slotNr);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception(result.Message);
|
||||
|
||||
_currentPcbId = result.PcbId;
|
||||
|
||||
btnConnect.Enabled = true;
|
||||
|
||||
@ -2026,7 +2031,7 @@ namespace GenesisCordonelInterface.UI.LaatzenAPI_GenesisToolBox
|
||||
|
||||
//_currentGenesis.WriteRegister("SENSUSRADIO_WakeupInterval", 0);
|
||||
//_currentGenesis.Logout();
|
||||
//File.AppendAllLines("RadioActivation.log", new[] { $"{freq};{_currentGenesis.PcbId};{RadioAdress};{DateTime.Now}" });
|
||||
//FileConfig.AppendAllLines("RadioActivation.log", new[] { $"{freq};{_currentGenesis.PcbId};{RadioAdress};{DateTime.Now}" });
|
||||
if (0x02 == RegisterConverter.ByteArrayToValue<Byte>(
|
||||
_currentGenesis.ReadRegister("SENSUSRADIO_SystemState")))
|
||||
{
|
||||
|
||||
243
GenesisCordonelInterface/UI/MainForm.Designer.cs
generated
243
GenesisCordonelInterface/UI/MainForm.Designer.cs
generated
@ -11,20 +11,14 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem miClearLog;
|
||||
private System.Windows.Forms.ToolStripMenuItem miHelp;
|
||||
private System.Windows.Forms.ToolStripMenuItem miHelpAbout;
|
||||
|
||||
private System.Windows.Forms.Panel pnlLeftMenu;
|
||||
private System.Windows.Forms.Panel pnlMain;
|
||||
private System.Windows.Forms.RichTextBox rtbMainLog;
|
||||
|
||||
private System.Windows.Forms.Panel mainHostPanel;
|
||||
private System.Windows.Forms.StatusStrip statusStrip1;
|
||||
private System.Windows.Forms.ToolStripStatusLabel tslStatus;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@ -38,54 +32,39 @@
|
||||
this.miClearLog = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.miHelp = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.miHelpAbout = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.pnlLeftMenu = new System.Windows.Forms.Panel();
|
||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||
this.tabPage1 = new System.Windows.Forms.TabPage();
|
||||
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||
this.preadjustmentButton = new System.Windows.Forms.Button();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.btnPulseSetup = new System.Windows.Forms.Button();
|
||||
this.btnSetup = new System.Windows.Forms.Button();
|
||||
this.btnRegisterStore = new System.Windows.Forms.Button();
|
||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||
this.pnlMain = new System.Windows.Forms.Panel();
|
||||
this.rtbMainLog = new System.Windows.Forms.RichTextBox();
|
||||
this.mainHostPanel = new System.Windows.Forms.Panel();
|
||||
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
||||
this.tslStatus = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.groupBox3 = new System.Windows.Forms.GroupBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.pnlLeftMenu.SuspendLayout();
|
||||
this.tabControl1.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.groupBox2.SuspendLayout();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
this.pnlMain.SuspendLayout();
|
||||
this.statusStrip1.SuspendLayout();
|
||||
this.groupBox3.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.miFile,
|
||||
this.miView,
|
||||
this.miHelp});
|
||||
this.miFile,
|
||||
this.miView,
|
||||
this.miHelp
|
||||
});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Padding = new System.Windows.Forms.Padding(5, 2, 0, 2);
|
||||
this.menuStrip1.Size = new System.Drawing.Size(1309, 24);
|
||||
this.menuStrip1.TabIndex = 0;
|
||||
this.menuStrip1.Text = "menuStrip1";
|
||||
|
||||
//
|
||||
// miFile
|
||||
//
|
||||
this.miFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.miExit});
|
||||
this.miExit
|
||||
});
|
||||
this.miFile.Name = "miFile";
|
||||
this.miFile.Size = new System.Drawing.Size(37, 20);
|
||||
this.miFile.Text = "File";
|
||||
this.miFile.Text = "FileConfig";
|
||||
|
||||
//
|
||||
// miExit
|
||||
//
|
||||
@ -93,14 +72,17 @@
|
||||
this.miExit.Size = new System.Drawing.Size(92, 22);
|
||||
this.miExit.Text = "Exit";
|
||||
this.miExit.Click += new System.EventHandler(this.miExit_Click);
|
||||
|
||||
//
|
||||
// miView
|
||||
//
|
||||
this.miView.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.miClearLog});
|
||||
this.miClearLog
|
||||
});
|
||||
this.miView.Name = "miView";
|
||||
this.miView.Size = new System.Drawing.Size(44, 20);
|
||||
this.miView.Text = "View";
|
||||
|
||||
//
|
||||
// miClearLog
|
||||
//
|
||||
@ -108,14 +90,17 @@
|
||||
this.miClearLog.Size = new System.Drawing.Size(124, 22);
|
||||
this.miClearLog.Text = "Clear Log";
|
||||
this.miClearLog.Click += new System.EventHandler(this.miClearLog_Click);
|
||||
|
||||
//
|
||||
// miHelp
|
||||
//
|
||||
this.miHelp.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.miHelpAbout});
|
||||
this.miHelpAbout
|
||||
});
|
||||
this.miHelp.Name = "miHelp";
|
||||
this.miHelp.Size = new System.Drawing.Size(44, 20);
|
||||
this.miHelp.Text = "Help";
|
||||
|
||||
//
|
||||
// miHelpAbout
|
||||
//
|
||||
@ -123,177 +108,44 @@
|
||||
this.miHelpAbout.Size = new System.Drawing.Size(107, 22);
|
||||
this.miHelpAbout.Text = "About";
|
||||
this.miHelpAbout.Click += new System.EventHandler(this.miHelpAbout_Click);
|
||||
|
||||
//
|
||||
// pnlLeftMenu
|
||||
// mainHostPanel
|
||||
//
|
||||
this.pnlLeftMenu.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.pnlLeftMenu.Controls.Add(this.tabControl1);
|
||||
this.pnlLeftMenu.Dock = System.Windows.Forms.DockStyle.Left;
|
||||
this.pnlLeftMenu.Location = new System.Drawing.Point(0, 24);
|
||||
this.pnlLeftMenu.Name = "pnlLeftMenu";
|
||||
this.pnlLeftMenu.Size = new System.Drawing.Size(190, 474);
|
||||
this.pnlLeftMenu.TabIndex = 1;
|
||||
//
|
||||
// tabControl1
|
||||
//
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
this.tabControl1.Controls.Add(this.tabPage2);
|
||||
this.tabControl1.Location = new System.Drawing.Point(3, 5);
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(186, 464);
|
||||
this.tabControl1.TabIndex = 4;
|
||||
//
|
||||
// tabPage1
|
||||
//
|
||||
this.tabPage1.Controls.Add(this.groupBox2);
|
||||
this.tabPage1.Controls.Add(this.groupBox1);
|
||||
this.tabPage1.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage1.Name = "tabPage1";
|
||||
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage1.Size = new System.Drawing.Size(178, 438);
|
||||
this.tabPage1.TabIndex = 0;
|
||||
this.tabPage1.Text = "Laatzen API";
|
||||
this.tabPage1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// groupBox2
|
||||
//
|
||||
this.groupBox2.Controls.Add(this.preadjustmentButton);
|
||||
this.groupBox2.Location = new System.Drawing.Point(6, 190);
|
||||
this.groupBox2.Name = "groupBox2";
|
||||
this.groupBox2.Size = new System.Drawing.Size(166, 66);
|
||||
this.groupBox2.TabIndex = 5;
|
||||
this.groupBox2.TabStop = false;
|
||||
this.groupBox2.Text = "CordonelPreadjustmentUI";
|
||||
//
|
||||
// preadjustmentButton
|
||||
//
|
||||
this.preadjustmentButton.Location = new System.Drawing.Point(6, 19);
|
||||
this.preadjustmentButton.Name = "preadjustmentButton";
|
||||
this.preadjustmentButton.Size = new System.Drawing.Size(153, 35);
|
||||
this.preadjustmentButton.TabIndex = 3;
|
||||
this.preadjustmentButton.Text = "Preadjustment";
|
||||
this.preadjustmentButton.UseVisualStyleBackColor = true;
|
||||
this.preadjustmentButton.Click += new System.EventHandler(this.preadjustmentButton_Click);
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Controls.Add(this.btnPulseSetup);
|
||||
this.groupBox1.Controls.Add(this.btnSetup);
|
||||
this.groupBox1.Controls.Add(this.btnRegisterStore);
|
||||
this.groupBox1.Location = new System.Drawing.Point(6, 17);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(166, 153);
|
||||
this.groupBox1.TabIndex = 4;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "GenesisToolBox";
|
||||
//
|
||||
// btnPulseSetup
|
||||
//
|
||||
this.btnPulseSetup.Location = new System.Drawing.Point(7, 101);
|
||||
this.btnPulseSetup.Name = "btnPulseSetup";
|
||||
this.btnPulseSetup.Size = new System.Drawing.Size(153, 35);
|
||||
this.btnPulseSetup.TabIndex = 2;
|
||||
this.btnPulseSetup.Text = "Pulse Setup";
|
||||
this.btnPulseSetup.UseVisualStyleBackColor = true;
|
||||
this.btnPulseSetup.Click += new System.EventHandler(this.btnPulseSetup_Click);
|
||||
//
|
||||
// btnSetup
|
||||
//
|
||||
this.btnSetup.Location = new System.Drawing.Point(7, 19);
|
||||
this.btnSetup.Name = "btnSetup";
|
||||
this.btnSetup.Size = new System.Drawing.Size(153, 35);
|
||||
this.btnSetup.TabIndex = 0;
|
||||
this.btnSetup.Text = "Setup";
|
||||
this.btnSetup.UseVisualStyleBackColor = true;
|
||||
this.btnSetup.Click += new System.EventHandler(this.btnSetup_Click);
|
||||
//
|
||||
// btnRegisterStore
|
||||
//
|
||||
this.btnRegisterStore.Location = new System.Drawing.Point(7, 60);
|
||||
this.btnRegisterStore.Name = "btnRegisterStore";
|
||||
this.btnRegisterStore.Size = new System.Drawing.Size(153, 35);
|
||||
this.btnRegisterStore.TabIndex = 1;
|
||||
this.btnRegisterStore.Text = "Register Store";
|
||||
this.btnRegisterStore.UseVisualStyleBackColor = true;
|
||||
this.btnRegisterStore.Click += new System.EventHandler(this.btnRegisterStore_Click);
|
||||
//
|
||||
// tabPage2
|
||||
//
|
||||
this.tabPage2.Controls.Add(this.groupBox3);
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage2.Name = "tabPage2";
|
||||
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage2.Size = new System.Drawing.Size(178, 438);
|
||||
this.tabPage2.TabIndex = 1;
|
||||
this.tabPage2.Text = "GCI API";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// pnlMain
|
||||
//
|
||||
this.pnlMain.Controls.Add(this.rtbMainLog);
|
||||
this.pnlMain.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pnlMain.Location = new System.Drawing.Point(190, 24);
|
||||
this.pnlMain.Name = "pnlMain";
|
||||
this.pnlMain.Padding = new System.Windows.Forms.Padding(9);
|
||||
this.pnlMain.Size = new System.Drawing.Size(1119, 474);
|
||||
this.pnlMain.TabIndex = 2;
|
||||
//
|
||||
// rtbMainLog
|
||||
//
|
||||
this.rtbMainLog.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.rtbMainLog.Font = new System.Drawing.Font("Consolas", 10F);
|
||||
this.rtbMainLog.Location = new System.Drawing.Point(9, 9);
|
||||
this.rtbMainLog.Name = "rtbMainLog";
|
||||
this.rtbMainLog.ReadOnly = true;
|
||||
this.rtbMainLog.Size = new System.Drawing.Size(1101, 456);
|
||||
this.rtbMainLog.TabIndex = 0;
|
||||
this.rtbMainLog.Text = "";
|
||||
this.mainHostPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.mainHostPanel.Location = new System.Drawing.Point(0, 24);
|
||||
this.mainHostPanel.Name = "mainHostPanel";
|
||||
this.mainHostPanel.Padding = new System.Windows.Forms.Padding(0);
|
||||
this.mainHostPanel.Size = new System.Drawing.Size(1309, 474);
|
||||
this.mainHostPanel.TabIndex = 1;
|
||||
|
||||
//
|
||||
// statusStrip1
|
||||
//
|
||||
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.tslStatus});
|
||||
this.tslStatus
|
||||
});
|
||||
this.statusStrip1.Location = new System.Drawing.Point(0, 498);
|
||||
this.statusStrip1.Name = "statusStrip1";
|
||||
this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 12, 0);
|
||||
this.statusStrip1.Size = new System.Drawing.Size(1309, 22);
|
||||
this.statusStrip1.TabIndex = 3;
|
||||
this.statusStrip1.TabIndex = 2;
|
||||
this.statusStrip1.Text = "statusStrip1";
|
||||
|
||||
//
|
||||
// tslStatus
|
||||
//
|
||||
this.tslStatus.Name = "tslStatus";
|
||||
this.tslStatus.Size = new System.Drawing.Size(39, 17);
|
||||
this.tslStatus.Text = "Ready";
|
||||
//
|
||||
// groupBox3
|
||||
//
|
||||
this.groupBox3.Controls.Add(this.button1);
|
||||
this.groupBox3.Location = new System.Drawing.Point(6, 15);
|
||||
this.groupBox3.Name = "groupBox3";
|
||||
this.groupBox3.Size = new System.Drawing.Size(166, 66);
|
||||
this.groupBox3.TabIndex = 6;
|
||||
this.groupBox3.TabStop = false;
|
||||
this.groupBox3.Text = "GenesisCordonelInterface";
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(6, 19);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(153, 35);
|
||||
this.button1.TabIndex = 3;
|
||||
this.button1.Text = "API";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(1309, 520);
|
||||
this.Controls.Add(this.pnlMain);
|
||||
this.Controls.Add(this.pnlLeftMenu);
|
||||
this.Controls.Add(this.mainHostPanel);
|
||||
this.Controls.Add(this.statusStrip1);
|
||||
this.Controls.Add(this.menuStrip1);
|
||||
this.MainMenuStrip = this.menuStrip1;
|
||||
@ -301,33 +153,14 @@
|
||||
this.Name = "MainForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Genesis Cordonel Interface";
|
||||
this.Load += new System.EventHandler(this.MainForm_Load);
|
||||
|
||||
this.menuStrip1.ResumeLayout(false);
|
||||
this.menuStrip1.PerformLayout();
|
||||
this.pnlLeftMenu.ResumeLayout(false);
|
||||
this.tabControl1.ResumeLayout(false);
|
||||
this.tabPage1.ResumeLayout(false);
|
||||
this.groupBox2.ResumeLayout(false);
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.tabPage2.ResumeLayout(false);
|
||||
this.pnlMain.ResumeLayout(false);
|
||||
this.statusStrip1.ResumeLayout(false);
|
||||
this.statusStrip1.PerformLayout();
|
||||
this.groupBox3.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
private System.Windows.Forms.TabControl tabControl1;
|
||||
private System.Windows.Forms.TabPage tabPage1;
|
||||
private System.Windows.Forms.Button btnSetup;
|
||||
private System.Windows.Forms.Button preadjustmentButton;
|
||||
private System.Windows.Forms.Button btnRegisterStore;
|
||||
private System.Windows.Forms.Button btnPulseSetup;
|
||||
private System.Windows.Forms.TabPage tabPage2;
|
||||
private System.Windows.Forms.GroupBox groupBox2;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.GroupBox groupBox3;
|
||||
private System.Windows.Forms.Button button1;
|
||||
}
|
||||
}
|
||||
@ -1,173 +1,42 @@
|
||||
using GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI;
|
||||
using GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface;
|
||||
using System;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Xylem.Common.Ui.GenesisToolBox;
|
||||
using Xylem.Common.Utils.Logging;
|
||||
|
||||
namespace GenesisCordonelInterface.UI
|
||||
{
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
private static readonly NLog.ILogger Logger = NLog.LogManager.GetLogger("GenesisCordonelInterface");
|
||||
private static readonly Regex LogLevelRegex = new Regex(@"\|\s*(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)\s*\|", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private MainView mainView;
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
/*_logger = logger;
|
||||
_logger.MessagePublished += OnLogMessagePublished;*/
|
||||
UiLogBus.MessageReceived += UiLogBus_MessageReceived;
|
||||
StartPosition = FormStartPosition.Manual;
|
||||
Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - Width, 0);
|
||||
|
||||
//TopRight position on screen
|
||||
this.StartPosition = FormStartPosition.Manual;
|
||||
this.Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - this.Width, 0);
|
||||
|
||||
//black background
|
||||
rtbMainLog.BackColor = Color.Black;
|
||||
rtbMainLog.ForeColor = Color.Gainsboro;
|
||||
rtbMainLog.Font = new Font("Consolas", 9f);
|
||||
rtbMainLog.ReadOnly = true;
|
||||
rtbMainLog.HideSelection = false;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
WindowState = FormWindowState.Maximized;
|
||||
}
|
||||
|
||||
private void UiLogBus_MessageReceived(string msg)
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<string>(UiLogBus_MessageReceived), msg);
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = msg.Replace("\r\n", "\n").Split('\n');
|
||||
|
||||
foreach (string originalLine in lines)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(originalLine))
|
||||
continue;
|
||||
|
||||
string line = originalLine;
|
||||
|
||||
// Find header (timestamp|LEVEL|)
|
||||
Match m = LogLevelRegex.Match(line);
|
||||
|
||||
string indent = "";
|
||||
if (m.Success)
|
||||
{
|
||||
int indentLength = m.Index + m.Length;
|
||||
indent = new string(' ', indentLength);
|
||||
}
|
||||
|
||||
// Split long message manually if needed (optional)
|
||||
string[] subLines = line.Split(new[] { " - " }, 2, StringSplitOptions.None);
|
||||
|
||||
string firstLine = line;
|
||||
string rest = null;
|
||||
|
||||
if (subLines.Length == 2 && subLines[1].Length > 120) // heuristic
|
||||
{
|
||||
firstLine = subLines[0] + " - " + subLines[1].Substring(0, 120);
|
||||
rest = subLines[1].Substring(120);
|
||||
}
|
||||
|
||||
AppendStyledLine(firstLine);
|
||||
|
||||
if (!string.IsNullOrEmpty(rest))
|
||||
{
|
||||
AppendStyledLine(indent + rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendStyledLine(string line)
|
||||
{
|
||||
int start = rtbMainLog.TextLength;
|
||||
|
||||
rtbMainLog.SelectionStart = start;
|
||||
rtbMainLog.SelectionLength = 0;
|
||||
rtbMainLog.SelectionColor = Color.Gainsboro;
|
||||
rtbMainLog.AppendText(line + Environment.NewLine);
|
||||
|
||||
Match m = LogLevelRegex.Match(line);
|
||||
if (m.Success)
|
||||
{
|
||||
rtbMainLog.SelectionStart = start + m.Index;
|
||||
rtbMainLog.SelectionLength = m.Length;
|
||||
rtbMainLog.SelectionColor = GetLogLevelColor(m.Groups[1].Value);
|
||||
}
|
||||
|
||||
HighlightKeywordsInLine(line, start);
|
||||
}
|
||||
|
||||
private int MeasureTextWidthPx(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return 0;
|
||||
|
||||
return TextRenderer.MeasureText(text, rtbMainLog.Font).Width;
|
||||
}
|
||||
|
||||
protected override void OnFormClosed(FormClosedEventArgs e)
|
||||
{
|
||||
//_logger.MessagePublished -= OnLogMessagePublished;
|
||||
UiLogBus.MessageReceived -= UiLogBus_MessageReceived;
|
||||
base.OnFormClosed(e);
|
||||
}
|
||||
|
||||
private void btnSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Setup open");
|
||||
|
||||
using (FrmSetup frm = new FrmSetup())
|
||||
{
|
||||
frm.ShowDialog(this);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: Setup closed.");
|
||||
}
|
||||
|
||||
private void btnRegisterStore_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Register Store open.");
|
||||
|
||||
|
||||
using (FrmRegisterStore frm = new FrmRegisterStore())
|
||||
{
|
||||
frm.ShowDialog(this);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: Register Store closed.");
|
||||
}
|
||||
|
||||
private void btnPulseSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Pulse Setup open.");
|
||||
|
||||
|
||||
using (FrmConfigurations frm = new FrmConfigurations())
|
||||
{
|
||||
frm.ShowDialog(this);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: Pulse Setup closed.");
|
||||
mainView = new MainView();
|
||||
mainView.Dock = DockStyle.Fill;
|
||||
Controls.Add(mainView);
|
||||
mainView.BringToFront();
|
||||
this.WindowState = FormWindowState.Maximized;
|
||||
}
|
||||
|
||||
private void miExit_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
|
||||
private void miClearLog_Click(object sender, EventArgs e)
|
||||
{
|
||||
rtbMainLog.Clear();
|
||||
Logger.Trace("Log cleared.");
|
||||
if (mainView != null)
|
||||
mainView.ClearLog();
|
||||
}
|
||||
|
||||
private void miHelpAbout_Click(object sender, EventArgs e)
|
||||
@ -178,113 +47,5 @@ namespace GenesisCordonelInterface.UI
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
private Color GetLogLevelColor(string level)
|
||||
{
|
||||
switch (level.Trim().ToUpperInvariant())
|
||||
{
|
||||
case "TRACE": return Color.Gray;
|
||||
case "DEBUG": return Color.DeepSkyBlue;
|
||||
case "INFO": return Color.LimeGreen;
|
||||
case "WARN": return Color.Orange;
|
||||
case "ERROR": return Color.Red;
|
||||
case "FATAL": return Color.Magenta;
|
||||
default: return Color.Gainsboro;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsSeparatorLine(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return false;
|
||||
|
||||
string trimmed = text.Trim();
|
||||
|
||||
// Remove spaces and tab-like spacing
|
||||
string compact = new string(trimmed.Where(c => !char.IsWhiteSpace(c)).ToArray());
|
||||
|
||||
if (compact.Length < 4)
|
||||
return false;
|
||||
|
||||
// Count non-letter/non-digit characters
|
||||
int nonAlnumCount = compact.Count(c => !char.IsLetterOrDigit(c));
|
||||
|
||||
// Consider it a separator if most characters are non-alphanumeric
|
||||
// Examples:
|
||||
// -----CommandToMeter()-----
|
||||
// ==========================
|
||||
// /////////
|
||||
double ratio = (double)nonAlnumCount / compact.Length;
|
||||
|
||||
return ratio >= 0.6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// special string highlighting
|
||||
/// "TX FINAL" have to go before "TX"
|
||||
/// else "TX" will highlighted in the middle of "TX FINAL"
|
||||
/// </summary>
|
||||
private static readonly (Color color, string[] keywords)[] KeywordGroups =
|
||||
{
|
||||
(Color.DeepSkyBlue, new[] { "REQUEST" }),
|
||||
(Color.Lime, new[] { "RESPONSE" }),
|
||||
//(Color.LightGreen, new[] { "START", "END" }),
|
||||
//(Color.Cyan, new[] { "TX", "TX FINAL" }),
|
||||
//(Color.DeepSkyBlue,new[] { "RX", "RX FINAL", "RX CHUNK" }),
|
||||
(Color.Gold, new[] { "READ-REGISTER-SESSION", "UI-CLICK"}),
|
||||
//(Color.Violet, new[] { "REGADDR" }),
|
||||
//(Color.Khaki, new[] { "DEFAULT", "ALIGNED", "STRING" })
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// special string highlighting
|
||||
/// </summary>
|
||||
/// <param name="line"></param>
|
||||
/// <param name="lineStartIndex"></param>
|
||||
private void HighlightKeywordsInLine(string line, int lineStartIndex)
|
||||
{
|
||||
foreach (var group in KeywordGroups)
|
||||
{
|
||||
foreach (var keyword in group.keywords)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
while ((index = line.IndexOf(keyword, index, StringComparison.Ordinal)) >= 0)
|
||||
{
|
||||
rtbMainLog.SelectionStart = lineStartIndex + index;
|
||||
rtbMainLog.SelectionLength = keyword.Length;
|
||||
rtbMainLog.SelectionColor = group.color;
|
||||
|
||||
index += keyword.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void preadjustmentButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Preadjustment open.");
|
||||
|
||||
using (FrmCordonelPreadjustmentUI frm = new FrmCordonelPreadjustmentUI())
|
||||
{
|
||||
frm.ShowDialog(this);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: Preadjustment closed.");
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: GCI GUI interface open.");
|
||||
|
||||
using (FrmGCIAPI frm = new FrmGCIAPI())
|
||||
{
|
||||
frm.ShowDialog(this);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: GCI GUI interface closed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
311
GenesisCordonelInterface/UI/MainView.Designer.cs
generated
Normal file
311
GenesisCordonelInterface/UI/MainView.Designer.cs
generated
Normal file
@ -0,0 +1,311 @@
|
||||
namespace GenesisCordonelInterface.UI
|
||||
{
|
||||
partial class MainView
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
private System.Windows.Forms.Panel pnlLeftMenu;
|
||||
private System.Windows.Forms.Panel pnlMain;
|
||||
|
||||
private System.Windows.Forms.SplitContainer splitMain;
|
||||
private System.Windows.Forms.SplitContainer splitBottom;
|
||||
|
||||
private System.Windows.Forms.RichTextBox rtbMainLog;
|
||||
private System.Windows.Forms.Panel pnlSlotConfig;
|
||||
private System.Windows.Forms.Panel pnlWorkerDebug;
|
||||
|
||||
private System.Windows.Forms.TabControl tabControl1;
|
||||
private System.Windows.Forms.TabPage tabPage1;
|
||||
private System.Windows.Forms.TabPage tabPage2;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.GroupBox groupBox2;
|
||||
private System.Windows.Forms.GroupBox groupBox3;
|
||||
|
||||
private System.Windows.Forms.Button btnSetup;
|
||||
private System.Windows.Forms.Button btnRegisterStore;
|
||||
private System.Windows.Forms.Button btnPulseSetup;
|
||||
private System.Windows.Forms.Button preadjustmentButton;
|
||||
private System.Windows.Forms.Button btnMeterInit;
|
||||
private System.Windows.Forms.Button btnMetersAction;
|
||||
private System.Windows.Forms.SplitContainer splitWorkArea;
|
||||
private System.Windows.Forms.Panel pnlGciViewHost;
|
||||
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.pnlLeftMenu = new System.Windows.Forms.Panel();
|
||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||
this.tabPage1 = new System.Windows.Forms.TabPage();
|
||||
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||
this.preadjustmentButton = new System.Windows.Forms.Button();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.btnPulseSetup = new System.Windows.Forms.Button();
|
||||
this.btnSetup = new System.Windows.Forms.Button();
|
||||
this.btnRegisterStore = new System.Windows.Forms.Button();
|
||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||
this.groupBox3 = new System.Windows.Forms.GroupBox();
|
||||
this.btnMeterInit = new System.Windows.Forms.Button();
|
||||
this.btnMetersAction = new System.Windows.Forms.Button();
|
||||
this.pnlMain = new System.Windows.Forms.Panel();
|
||||
this.splitMain = new System.Windows.Forms.SplitContainer();
|
||||
this.splitBottom = new System.Windows.Forms.SplitContainer();
|
||||
this.rtbMainLog = new System.Windows.Forms.RichTextBox();
|
||||
this.pnlSlotConfig = new System.Windows.Forms.Panel();
|
||||
this.pnlWorkerDebug = new System.Windows.Forms.Panel();
|
||||
this.splitWorkArea = new System.Windows.Forms.SplitContainer();
|
||||
this.pnlGciViewHost = new System.Windows.Forms.Panel();
|
||||
|
||||
this.pnlLeftMenu.SuspendLayout();
|
||||
this.tabControl1.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.groupBox2.SuspendLayout();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
this.groupBox3.SuspendLayout();
|
||||
this.pnlMain.SuspendLayout();
|
||||
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitMain)).BeginInit();
|
||||
this.splitMain.Panel1.SuspendLayout();
|
||||
this.splitMain.Panel2.SuspendLayout();
|
||||
this.splitMain.SuspendLayout();
|
||||
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitBottom)).BeginInit();
|
||||
this.splitBottom.Panel1.SuspendLayout();
|
||||
this.splitBottom.Panel2.SuspendLayout();
|
||||
this.splitBottom.SuspendLayout();
|
||||
|
||||
this.SuspendLayout();
|
||||
|
||||
// pnlLeftMenu
|
||||
this.pnlLeftMenu.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.pnlLeftMenu.Controls.Add(this.tabControl1);
|
||||
this.pnlLeftMenu.Dock = System.Windows.Forms.DockStyle.Left;
|
||||
this.pnlLeftMenu.Location = new System.Drawing.Point(0, 0);
|
||||
this.pnlLeftMenu.Name = "pnlLeftMenu";
|
||||
this.pnlLeftMenu.Size = new System.Drawing.Size(190, 500);
|
||||
this.pnlLeftMenu.TabIndex = 0;
|
||||
|
||||
// tabControl1
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
this.tabControl1.Controls.Add(this.tabPage2);
|
||||
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tabControl1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(188, 498);
|
||||
this.tabControl1.TabIndex = 0;
|
||||
|
||||
// tabPage1
|
||||
this.tabPage1.Controls.Add(this.groupBox2);
|
||||
this.tabPage1.Controls.Add(this.groupBox1);
|
||||
this.tabPage1.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage1.Name = "tabPage1";
|
||||
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage1.Size = new System.Drawing.Size(180, 472);
|
||||
this.tabPage1.TabIndex = 0;
|
||||
this.tabPage1.Text = "Laatzen API";
|
||||
this.tabPage1.UseVisualStyleBackColor = true;
|
||||
|
||||
// groupBox1
|
||||
this.groupBox1.Controls.Add(this.btnPulseSetup);
|
||||
this.groupBox1.Controls.Add(this.btnSetup);
|
||||
this.groupBox1.Controls.Add(this.btnRegisterStore);
|
||||
this.groupBox1.Location = new System.Drawing.Point(6, 10);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(166, 150);
|
||||
this.groupBox1.TabIndex = 0;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "GenesisToolBox";
|
||||
|
||||
// btnSetup
|
||||
this.btnSetup.Location = new System.Drawing.Point(7, 20);
|
||||
this.btnSetup.Name = "btnSetup";
|
||||
this.btnSetup.Size = new System.Drawing.Size(150, 30);
|
||||
this.btnSetup.TabIndex = 0;
|
||||
this.btnSetup.Text = "Setup";
|
||||
this.btnSetup.UseVisualStyleBackColor = true;
|
||||
this.btnSetup.Click += new System.EventHandler(this.btnSetup_Click);
|
||||
|
||||
// btnRegisterStore
|
||||
this.btnRegisterStore.Location = new System.Drawing.Point(7, 55);
|
||||
this.btnRegisterStore.Name = "btnRegisterStore";
|
||||
this.btnRegisterStore.Size = new System.Drawing.Size(150, 30);
|
||||
this.btnRegisterStore.TabIndex = 1;
|
||||
this.btnRegisterStore.Text = "Register Store";
|
||||
this.btnRegisterStore.UseVisualStyleBackColor = true;
|
||||
this.btnRegisterStore.Click += new System.EventHandler(this.btnRegisterStore_Click);
|
||||
|
||||
// btnPulseSetup
|
||||
this.btnPulseSetup.Location = new System.Drawing.Point(7, 90);
|
||||
this.btnPulseSetup.Name = "btnPulseSetup";
|
||||
this.btnPulseSetup.Size = new System.Drawing.Size(150, 30);
|
||||
this.btnPulseSetup.TabIndex = 2;
|
||||
this.btnPulseSetup.Text = "Pulse Setup";
|
||||
this.btnPulseSetup.UseVisualStyleBackColor = true;
|
||||
this.btnPulseSetup.Click += new System.EventHandler(this.btnPulseSetup_Click);
|
||||
|
||||
// groupBox2
|
||||
this.groupBox2.Controls.Add(this.preadjustmentButton);
|
||||
this.groupBox2.Location = new System.Drawing.Point(6, 170);
|
||||
this.groupBox2.Name = "groupBox2";
|
||||
this.groupBox2.Size = new System.Drawing.Size(166, 70);
|
||||
this.groupBox2.TabIndex = 1;
|
||||
this.groupBox2.TabStop = false;
|
||||
this.groupBox2.Text = "CordonelPreadjustmentUI";
|
||||
|
||||
// preadjustmentButton
|
||||
this.preadjustmentButton.Location = new System.Drawing.Point(6, 20);
|
||||
this.preadjustmentButton.Name = "preadjustmentButton";
|
||||
this.preadjustmentButton.Size = new System.Drawing.Size(150, 30);
|
||||
this.preadjustmentButton.TabIndex = 0;
|
||||
this.preadjustmentButton.Text = "Preadjustment";
|
||||
this.preadjustmentButton.UseVisualStyleBackColor = true;
|
||||
this.preadjustmentButton.Click += new System.EventHandler(this.preadjustmentButton_Click);
|
||||
|
||||
// tabPage2
|
||||
this.tabPage2.Controls.Add(this.groupBox3);
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage2.Name = "tabPage2";
|
||||
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage2.Size = new System.Drawing.Size(180, 472);
|
||||
this.tabPage2.TabIndex = 1;
|
||||
this.tabPage2.Text = "GCI API";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
|
||||
// groupBox3
|
||||
this.groupBox3.Controls.Add(this.btnMeterInit);
|
||||
this.groupBox3.Controls.Add(this.btnMetersAction);
|
||||
this.groupBox3.Size = new System.Drawing.Size(166, 105);
|
||||
this.groupBox3.Location = new System.Drawing.Point(6, 10);
|
||||
this.groupBox3.Name = "groupBox3";
|
||||
this.groupBox3.TabIndex = 0;
|
||||
this.groupBox3.TabStop = false;
|
||||
this.groupBox3.Text = "GenesisCordonelInterface";
|
||||
|
||||
// btnMeterInit
|
||||
this.btnMeterInit.Location = new System.Drawing.Point(6, 20);
|
||||
this.btnMeterInit.Name = "btnMeterInit";
|
||||
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);
|
||||
this.btnMetersAction.Name = "btnMetersAction";
|
||||
this.btnMetersAction.Size = new System.Drawing.Size(150, 30);
|
||||
this.btnMetersAction.Text = "Meters Action";
|
||||
this.btnMetersAction.UseVisualStyleBackColor = true;
|
||||
this.btnMetersAction.Click += new System.EventHandler(this.btnMetersAction_Click);
|
||||
|
||||
// pnlMain
|
||||
this.pnlMain.Controls.Add(this.splitMain);
|
||||
this.pnlMain.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pnlMain.Location = new System.Drawing.Point(190, 0);
|
||||
this.pnlMain.Name = "pnlMain";
|
||||
this.pnlMain.Padding = new System.Windows.Forms.Padding(5);
|
||||
this.pnlMain.Size = new System.Drawing.Size(710, 500);
|
||||
this.pnlMain.TabIndex = 1;
|
||||
|
||||
// splitMain
|
||||
this.splitMain.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitMain.Location = new System.Drawing.Point(5, 5);
|
||||
this.splitMain.Name = "splitMain";
|
||||
this.splitMain.Orientation = System.Windows.Forms.Orientation.Horizontal;
|
||||
this.splitMain.Panel1.Controls.Add(this.splitWorkArea);
|
||||
this.splitMain.Panel1MinSize = 100;
|
||||
this.splitMain.Panel2.Controls.Add(this.splitBottom);
|
||||
this.splitMain.Panel2MinSize = 100;
|
||||
this.splitMain.Size = new System.Drawing.Size(700, 490);
|
||||
this.splitMain.SplitterDistance = 320;
|
||||
this.splitMain.SplitterWidth = 6;
|
||||
this.splitMain.TabIndex = 0;
|
||||
|
||||
// rtbMainLog
|
||||
this.rtbMainLog.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.rtbMainLog.Font = new System.Drawing.Font("Consolas", 10F);
|
||||
this.rtbMainLog.Name = "rtbMainLog";
|
||||
this.rtbMainLog.ReadOnly = true;
|
||||
this.rtbMainLog.Size = new System.Drawing.Size(700, 320);
|
||||
this.rtbMainLog.TabIndex = 0;
|
||||
this.rtbMainLog.Text = "";
|
||||
|
||||
// splitBottom
|
||||
this.splitBottom.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitBottom.Location = new System.Drawing.Point(0, 0);
|
||||
this.splitBottom.Name = "splitBottom";
|
||||
this.splitBottom.Orientation = System.Windows.Forms.Orientation.Vertical;
|
||||
this.splitBottom.Panel1.Controls.Add(this.pnlSlotConfig);
|
||||
this.splitBottom.Panel1MinSize = 250;
|
||||
this.splitBottom.Panel2.Controls.Add(this.pnlWorkerDebug);
|
||||
this.splitBottom.Panel2MinSize = 250;
|
||||
this.splitBottom.Size = new System.Drawing.Size(700, 164);
|
||||
this.splitBottom.SplitterDistance = 320;
|
||||
this.splitBottom.SplitterWidth = 6;
|
||||
this.splitBottom.TabIndex = 0;
|
||||
|
||||
// pnlSlotConfig
|
||||
this.pnlSlotConfig.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.pnlSlotConfig.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pnlSlotConfig.Location = new System.Drawing.Point(0, 0);
|
||||
this.pnlSlotConfig.Name = "pnlSlotConfig";
|
||||
this.pnlSlotConfig.Size = new System.Drawing.Size(320, 164);
|
||||
this.pnlSlotConfig.TabIndex = 0;
|
||||
|
||||
// pnlWorkerDebug
|
||||
this.pnlWorkerDebug.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.pnlWorkerDebug.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pnlWorkerDebug.Location = new System.Drawing.Point(0, 0);
|
||||
this.pnlWorkerDebug.Name = "pnlWorkerDebug";
|
||||
this.pnlWorkerDebug.Size = new System.Drawing.Size(374, 164);
|
||||
this.pnlWorkerDebug.TabIndex = 0;
|
||||
|
||||
// splitWorkArea
|
||||
this.splitWorkArea.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitWorkArea.Orientation = System.Windows.Forms.Orientation.Vertical;
|
||||
this.splitWorkArea.Panel1MinSize = 25;
|
||||
this.splitWorkArea.Panel2MinSize = 25;
|
||||
this.splitWorkArea.SplitterWidth = 6;
|
||||
|
||||
// left = active GCI view
|
||||
this.splitWorkArea.Panel1.Controls.Add(this.pnlGciViewHost);
|
||||
|
||||
// right = black memo/log
|
||||
this.splitWorkArea.Panel2.Controls.Add(this.rtbMainLog);
|
||||
|
||||
// pnlGciViewHost
|
||||
this.pnlGciViewHost.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pnlGciViewHost.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
|
||||
// rtbMainLog
|
||||
this.rtbMainLog.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
|
||||
// MainView
|
||||
this.Controls.Add(this.pnlMain);
|
||||
this.Controls.Add(this.pnlLeftMenu);
|
||||
this.Name = "MainView";
|
||||
this.Size = new System.Drawing.Size(900, 500);
|
||||
|
||||
this.pnlLeftMenu.ResumeLayout(false);
|
||||
this.tabControl1.ResumeLayout(false);
|
||||
this.tabPage1.ResumeLayout(false);
|
||||
this.groupBox2.ResumeLayout(false);
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.tabPage2.ResumeLayout(false);
|
||||
this.groupBox3.ResumeLayout(false);
|
||||
this.pnlMain.ResumeLayout(false);
|
||||
|
||||
this.splitMain.Panel1.ResumeLayout(false);
|
||||
this.splitMain.Panel2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitMain)).EndInit();
|
||||
this.splitMain.ResumeLayout(false);
|
||||
|
||||
this.splitBottom.Panel1.ResumeLayout(false);
|
||||
this.splitBottom.Panel2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitBottom)).EndInit();
|
||||
this.splitBottom.ResumeLayout(false);
|
||||
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
333
GenesisCordonelInterface/UI/MainView.cs
Normal file
333
GenesisCordonelInterface/UI/MainView.cs
Normal file
@ -0,0 +1,333 @@
|
||||
using GenesisCordonelInterface.UI.LaatzenAPI_CordonelPreadjustmentUI;
|
||||
using GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Xylem.Common.Ui.GenesisToolBox;
|
||||
using Xylem.Common.Utils.Logging;
|
||||
using GenesisCordonelInterface.API;
|
||||
using GenesisCordonelInterface.UI.Debug;
|
||||
|
||||
namespace GenesisCordonelInterface.UI
|
||||
{
|
||||
public partial class MainView : UserControl
|
||||
{
|
||||
private static readonly NLog.ILogger Logger = NLog.LogManager.GetLogger("GenesisCordonelInterface");
|
||||
private static readonly Regex LogLevelRegex = new Regex(@"\|\s*(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)\s*\|", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private readonly InterfaceOutsideToGCI _api = new InterfaceOutsideToGCI();
|
||||
private MeterBatchConfigPanel _batchPanel;
|
||||
|
||||
public MainView()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitializeDebugPanels();
|
||||
//InitializeWorkerDebugPanel();
|
||||
|
||||
UiLogBus.MessageReceived += UiLogBus_MessageReceived;
|
||||
|
||||
rtbMainLog.BackColor = Color.Black;
|
||||
rtbMainLog.ForeColor = Color.Gainsboro;
|
||||
rtbMainLog.Font = new Font("Consolas", 9f);
|
||||
rtbMainLog.ReadOnly = true;
|
||||
rtbMainLog.HideSelection = false;
|
||||
}
|
||||
|
||||
private void InitializeWorkerDebugPanel()
|
||||
{
|
||||
pnlWorkerDebug.Controls.Clear();
|
||||
|
||||
var debugPanel = new WorkerDebugPanel(_api)
|
||||
{
|
||||
Dock = DockStyle.Fill
|
||||
};
|
||||
|
||||
pnlWorkerDebug.Controls.Add(debugPanel);
|
||||
}
|
||||
private void InitializeDebugPanels()
|
||||
{
|
||||
_batchPanel = new MeterBatchConfigPanel(_api)
|
||||
{
|
||||
Dock = DockStyle.Fill
|
||||
};
|
||||
|
||||
pnlSlotConfig.Controls.Add(_batchPanel);
|
||||
|
||||
pnlWorkerDebug.Controls.Add(new WorkerDebugPanel(_api)
|
||||
{
|
||||
Dock = DockStyle.Fill
|
||||
});
|
||||
}
|
||||
|
||||
public void AddSlotRow()
|
||||
{
|
||||
_batchPanel?.AddEmptySlotRow();
|
||||
}
|
||||
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
base.OnLoad(e);
|
||||
|
||||
BeginInvoke(new Action(() =>
|
||||
{
|
||||
SetSafeSplitterDistance(splitWorkArea, 420);
|
||||
}));
|
||||
}
|
||||
|
||||
private void SetSafeSplitterDistance(SplitContainer split, int desired)
|
||||
{
|
||||
int width = split.ClientSize.Width;
|
||||
|
||||
int min1 = split.Panel1MinSize;
|
||||
int min2 = split.Panel2MinSize;
|
||||
int splitter = split.SplitterWidth;
|
||||
|
||||
int max = width - min2 - splitter;
|
||||
|
||||
if (width <= min1 + min2 + splitter)
|
||||
return;
|
||||
|
||||
if (desired < min1)
|
||||
desired = min1;
|
||||
|
||||
if (desired > max)
|
||||
desired = max;
|
||||
|
||||
split.SplitterDistance = desired;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
UiLogBus.MessageReceived -= UiLogBus_MessageReceived;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private IWin32Window DialogOwner
|
||||
{
|
||||
get
|
||||
{
|
||||
Form owner = FindForm();
|
||||
return owner ?? (IWin32Window)this;
|
||||
}
|
||||
}
|
||||
|
||||
private void btnSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Setup open");
|
||||
|
||||
using (FrmSetup frm = new FrmSetup())
|
||||
{
|
||||
frm.ShowDialog(DialogOwner);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: Setup closed.");
|
||||
}
|
||||
|
||||
private void btnRegisterStore_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Register Store open.");
|
||||
|
||||
using (FrmRegisterStore frm = new FrmRegisterStore())
|
||||
{
|
||||
frm.ShowDialog(DialogOwner);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: Register Store closed.");
|
||||
}
|
||||
|
||||
private void btnPulseSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Pulse Setup open.");
|
||||
|
||||
using (FrmConfigurations frm = new FrmConfigurations())
|
||||
{
|
||||
frm.ShowDialog(DialogOwner);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: Pulse Setup closed.");
|
||||
}
|
||||
|
||||
private void preadjustmentButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Preadjustment open.");
|
||||
|
||||
using (FrmCordonelPreadjustmentUI frm = new FrmCordonelPreadjustmentUI())
|
||||
{
|
||||
frm.ShowDialog(DialogOwner);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: Preadjustment closed.");
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: GCI GUI interface open.");
|
||||
|
||||
using (var frm = new FrmGCIAPI(_api))
|
||||
{
|
||||
frm.ShowDialog(this);
|
||||
}
|
||||
|
||||
Logger.Trace("FORM: GCI GUI interface closed.");
|
||||
}
|
||||
|
||||
private void SwitchGciView(string name, Control view)
|
||||
{
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace($"FORM: GCI VIEW -> {name} OPEN");
|
||||
|
||||
pnlGciViewHost.Controls.Clear();
|
||||
|
||||
view.Dock = DockStyle.Fill;
|
||||
pnlGciViewHost.Controls.Add(view);
|
||||
view.BringToFront();
|
||||
|
||||
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(
|
||||
"MetersAction",
|
||||
new MetersActionView(_api));
|
||||
}
|
||||
|
||||
public void ClearLog()
|
||||
{
|
||||
rtbMainLog.Clear();
|
||||
Logger.Trace("Log cleared.");
|
||||
}
|
||||
|
||||
private void UiLogBus_MessageReceived(string msg)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<string>(UiLogBus_MessageReceived), msg);
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = msg.Replace("\r\n", "\n").Split('\n');
|
||||
|
||||
foreach (string originalLine in lines)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(originalLine))
|
||||
continue;
|
||||
|
||||
string line = originalLine;
|
||||
Match m = LogLevelRegex.Match(line);
|
||||
|
||||
string indent = "";
|
||||
if (m.Success)
|
||||
{
|
||||
int indentLength = m.Index + m.Length;
|
||||
indent = new string(' ', indentLength);
|
||||
}
|
||||
|
||||
string[] subLines = line.Split(new[] { " - " }, 2, StringSplitOptions.None);
|
||||
|
||||
string firstLine = line;
|
||||
string rest = null;
|
||||
|
||||
if (subLines.Length == 2 && subLines[1].Length > 120)
|
||||
{
|
||||
firstLine = subLines[0] + " - " + subLines[1].Substring(0, 120);
|
||||
rest = subLines[1].Substring(120);
|
||||
}
|
||||
|
||||
AppendStyledLine(firstLine);
|
||||
|
||||
if (!string.IsNullOrEmpty(rest))
|
||||
AppendStyledLine(indent + rest);
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendStyledLine(string line)
|
||||
{
|
||||
int start = rtbMainLog.TextLength;
|
||||
|
||||
rtbMainLog.SelectionStart = start;
|
||||
rtbMainLog.SelectionLength = 0;
|
||||
rtbMainLog.SelectionColor = Color.Gainsboro;
|
||||
rtbMainLog.AppendText(line + Environment.NewLine);
|
||||
|
||||
Match m = LogLevelRegex.Match(line);
|
||||
if (m.Success)
|
||||
{
|
||||
rtbMainLog.SelectionStart = start + m.Index;
|
||||
rtbMainLog.SelectionLength = m.Length;
|
||||
rtbMainLog.SelectionColor = GetLogLevelColor(m.Groups[1].Value);
|
||||
}
|
||||
|
||||
HighlightKeywordsInLine(line, start);
|
||||
}
|
||||
|
||||
private Color GetLogLevelColor(string level)
|
||||
{
|
||||
switch (level.Trim().ToUpperInvariant())
|
||||
{
|
||||
case "TRACE": return Color.Gray;
|
||||
case "DEBUG": return Color.DeepSkyBlue;
|
||||
case "INFO": return Color.LimeGreen;
|
||||
case "WARN": return Color.Orange;
|
||||
case "ERROR": return Color.Red;
|
||||
case "FATAL": return Color.Magenta;
|
||||
default: return Color.Gainsboro;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Tuple<Color, string[]>[] KeywordGroups =
|
||||
{
|
||||
Tuple.Create(Color.DeepSkyBlue, new[] { "REQUEST" }),
|
||||
Tuple.Create(Color.Lime, new[] { "RESPONSE" }),
|
||||
Tuple.Create(Color.Gold, new[] { "READ-REGISTER-SESSION", "UI-CLICK" })
|
||||
};
|
||||
|
||||
private void HighlightKeywordsInLine(string line, int lineStartIndex)
|
||||
{
|
||||
foreach (var group in KeywordGroups)
|
||||
{
|
||||
foreach (var keyword in group.Item2)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
while ((index = line.IndexOf(keyword, index, StringComparison.Ordinal)) >= 0)
|
||||
{
|
||||
rtbMainLog.SelectionStart = lineStartIndex + index;
|
||||
rtbMainLog.SelectionLength = keyword.Length;
|
||||
rtbMainLog.SelectionColor = group.Item1;
|
||||
|
||||
index += keyword.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowGciView(Control view)
|
||||
{
|
||||
pnlGciViewHost.Controls.Clear();
|
||||
|
||||
view.Dock = DockStyle.Fill;
|
||||
pnlGciViewHost.Controls.Add(view);
|
||||
}
|
||||
|
||||
public void SaveSlots()
|
||||
{
|
||||
var data = _batchPanel.GetGridData();
|
||||
_api.SaveSlotSetup(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,7 +82,7 @@
|
||||
this.lblConfigSource.Name = "lblConfigSource";
|
||||
this.lblConfigSource.Size = new System.Drawing.Size(75, 13);
|
||||
this.lblConfigSource.TabIndex = 2;
|
||||
this.lblConfigSource.Text = "ConfigSource:";
|
||||
this.lblConfigSource.Text = "GciConfigSource:";
|
||||
//
|
||||
// cmbConfigSource
|
||||
//
|
||||
@ -314,7 +314,7 @@
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(624, 455);
|
||||
this.ClientSize = new System.Drawing.Size(624, 650);
|
||||
this.Controls.Add(this.btnSetPassword);
|
||||
this.Controls.Add(this.btnWriteRegister);
|
||||
this.Controls.Add(this.btnReadRegister);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using GenesisCordonelInterface.API;
|
||||
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
||||
@ -8,36 +9,137 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
{
|
||||
public partial class FrmGCIAPI : Form
|
||||
{
|
||||
private readonly InterfaceOutsideToGCI _api = new InterfaceOutsideToGCI();
|
||||
/*
|
||||
GCI UI async execution model
|
||||
|
||||
public FrmGCIAPI()
|
||||
The UI thread must never execute long-running meter communication directly.
|
||||
When the user clicks a button, the UI only reads input values from controls
|
||||
(for example slot number) and then delegates the real work to the GCI API.
|
||||
|
||||
The API call is executed by a per-slot worker queue:
|
||||
- one slot / one meter / one communication port has its own worker
|
||||
- commands for the same meter are executed sequentially
|
||||
- commands for different meters can run in parallel
|
||||
- after await completes, execution returns back to the UI thread safely
|
||||
|
||||
This prevents the WinForms UI from freezing while keeping meter communication
|
||||
safe and ordered.
|
||||
|
||||
workflow:
|
||||
|
||||
User clicks button
|
||||
|
|
||||
v
|
||||
WinForms UI thread
|
||||
(read textbox values only)
|
||||
|
|
||||
v
|
||||
await _api.GetPcbIdAsync(slot)
|
||||
|
|
||||
v
|
||||
InterfaceOutsideToGCI
|
||||
(clean public facade)
|
||||
|
|
||||
v
|
||||
InterfaceGCIToLaatzen
|
||||
(API/business logic)
|
||||
|
|
||||
v
|
||||
Per-slot ApiWorker queue
|
||||
(slot 1 / slot 2 / slot 3 ...)
|
||||
|
|
||||
v
|
||||
GenesisMeter communication
|
||||
(blocking HW operation)
|
||||
|
|
||||
v
|
||||
Result returned
|
||||
|
|
||||
v
|
||||
UI thread continues after await
|
||||
(update labels / show MessageBox)
|
||||
|
||||
The UI does not freeze because hardware communication is not executed directly inside the event handler.
|
||||
The event handler only calls an async method and waits using await. While waiting, the UI thread remains free.
|
||||
The actual work is executed on a worker thread assigned to a specific slot/meter.
|
||||
For a single meter, requests are queued, so they are executed sequentially and do not run at the same time,
|
||||
which prevents communication conflicts.
|
||||
*/
|
||||
|
||||
private readonly InterfaceOutsideToGCI _api;
|
||||
|
||||
|
||||
public FrmGCIAPI() : this(new InterfaceOutsideToGCI())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the form and default UI values.
|
||||
/// </summary>
|
||||
public FrmGCIAPI(InterfaceOutsideToGCI api)
|
||||
{
|
||||
_api = api ?? throw new ArgumentNullException(nameof(api));
|
||||
|
||||
InitializeComponent();
|
||||
InitializeDefaults();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets default values for combo boxes.
|
||||
/// </summary>
|
||||
private void InitializeDefaults()
|
||||
{
|
||||
cmbConfigSource.DataSource = Enum.GetValues(typeof(ConfigSource));
|
||||
cmbPasswordSource.DataSource = Enum.GetValues(typeof(PasswordSource));
|
||||
|
||||
cmbConfigSource.SelectedItem = ConfigSource.ExternStorage;
|
||||
cmbConfigSource.SelectedItem = ConfigSource.InterfaceInputConfig;
|
||||
cmbPasswordSource.SelectedItem = PasswordSource.OfflineFile;
|
||||
}
|
||||
|
||||
private void ExecuteApiAction(Action action)
|
||||
#region UI Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Executes an async API action with UI busy state and centralized error handling.
|
||||
/// </summary>
|
||||
private async Task ExecuteApiActionAsync(Func<Task> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
SetBusy(true);
|
||||
await action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "API call failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetSlot()
|
||||
/// <summary>
|
||||
/// Enables/disables UI controls and updates cursor to indicate busy state.
|
||||
/// </summary>
|
||||
private void SetBusy(bool busy)
|
||||
{
|
||||
Cursor = busy ? Cursors.WaitCursor : Cursors.Default;
|
||||
|
||||
btnInit.Enabled = !busy;
|
||||
btnConnectOne.Enabled = !busy;
|
||||
btnConnectAll.Enabled = !busy;
|
||||
btnDisconnect.Enabled = !busy;
|
||||
btnGetPcbId.Enabled = !busy;
|
||||
btnReadRegister.Enabled = !busy;
|
||||
btnWriteRegister.Enabled = !busy;
|
||||
btnSetPassword.Enabled = !busy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely parses slot number from UI textbox.
|
||||
/// </summary>
|
||||
private int GetSlotSafe()
|
||||
{
|
||||
if (!int.TryParse(txtSlot.Text, out int slot))
|
||||
throw new Exception("Invalid slot number.");
|
||||
@ -45,21 +147,28 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
return slot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts string input to best matching primitive type.
|
||||
/// </summary>
|
||||
private object ParseValue(string input)
|
||||
{
|
||||
if (int.TryParse(input, out int intValue))
|
||||
return intValue;
|
||||
|
||||
if (uint.TryParse(input, out uint uintValue))
|
||||
return uintValue;
|
||||
|
||||
if (bool.TryParse(input, out bool boolValue))
|
||||
return boolValue;
|
||||
if (int.TryParse(input, out int i)) return i;
|
||||
if (uint.TryParse(input, out uint ui)) return ui;
|
||||
if (bool.TryParse(input, out bool b)) return b;
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
private PortConfig? CreatePortConfig(string portName, string baudRateText)
|
||||
/// <summary>
|
||||
/// Creates a public GCI port configuration object from UI input values.
|
||||
/// </summary>
|
||||
/// <param name="portName">Name of the port (e.g. COM3).</param>
|
||||
/// <param name="baudRateText">Baud rate entered in the UI.</param>
|
||||
/// <returns>
|
||||
/// Instance of <see cref="GciPublicModels.GciPortConfig"/> or null if port name is empty.
|
||||
/// </returns>
|
||||
/// <exception cref="Exception">Thrown when baud rate is invalid.</exception>
|
||||
private GciPublicModels.GciPortConfig CreateGciPortConfig(string portName, string baudRateText)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(portName))
|
||||
return null;
|
||||
@ -67,127 +176,267 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
if (!int.TryParse(baudRateText, out int baudRate))
|
||||
throw new Exception($"Invalid baud rate for port {portName}.");
|
||||
|
||||
var cfg = new PortConfig
|
||||
return new GciPublicModels.GciPortConfig
|
||||
{
|
||||
PortName = portName,
|
||||
Type = "Serial"
|
||||
Type = "Serial",
|
||||
};
|
||||
|
||||
var sp = cfg.GetSerialPort();
|
||||
if (sp == null)
|
||||
throw new Exception($"Serial port object was not created for {portName}.");
|
||||
|
||||
sp.BaudRate = baudRate;
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
private ConfigSource GetConfigSource()
|
||||
/// <summary>
|
||||
/// Gets selected configuration source from UI and maps it to public GCI model.
|
||||
/// </summary>
|
||||
/// <returns>Selected <see cref="GciPublicModels.GciConfigSource"/>.</returns>
|
||||
/// <exception cref="Exception">Thrown when no value is selected.</exception>
|
||||
private GciPublicModels.GciConfigSource GetConfigSource()
|
||||
{
|
||||
if (cmbConfigSource.SelectedItem == null)
|
||||
throw new Exception("ConfigSource is not selected.");
|
||||
throw new Exception("Config source is not selected.");
|
||||
|
||||
return (ConfigSource)cmbConfigSource.SelectedItem;
|
||||
return (GciPublicModels.GciConfigSource)cmbConfigSource.SelectedItem;
|
||||
}
|
||||
|
||||
private PasswordSource GetPasswordSource()
|
||||
/// <summary>
|
||||
/// Gets selected password source from UI and maps it to public GCI model.
|
||||
/// </summary>
|
||||
/// <returns>Selected <see cref="GciPublicModels.GciPasswordSource"/>.</returns>
|
||||
/// <exception cref="Exception">Thrown when no value is selected.</exception>
|
||||
private GciPublicModels.GciPasswordSource GetPasswordSource()
|
||||
{
|
||||
if (cmbPasswordSource.SelectedItem == null)
|
||||
throw new Exception("PasswordSource is not selected.");
|
||||
throw new Exception("Password source is not selected.");
|
||||
|
||||
return (PasswordSource)cmbPasswordSource.SelectedItem;
|
||||
return (GciPublicModels.GciPasswordSource)cmbPasswordSource.SelectedItem;
|
||||
}
|
||||
|
||||
private void btnInit_Click(object sender, EventArgs e)
|
||||
#endregion
|
||||
|
||||
#region Buttons
|
||||
|
||||
/// <summary>
|
||||
/// Initializes meter using external configuration.
|
||||
/// </summary>
|
||||
private async void btnInit_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() =>
|
||||
int slot;
|
||||
|
||||
try { slot = GetSlotSafe(); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
int slot = GetSlot();
|
||||
MessageBox.Show(ex.Message, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
var requestPort = CreatePortConfig(txtRequestPort.Text, txtRequestBaudRate.Text);
|
||||
var streamingPort = CreatePortConfig(txtStreamingPort.Text, txtStreamingBaudRate.Text);
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
var request = new GciPublicModels.GciInitSlotRequest
|
||||
{
|
||||
SlotId = slot,
|
||||
ConfigSource = GetConfigSource(),
|
||||
PasswordSource = GetPasswordSource(),
|
||||
RequestPort = CreateGciPortConfig(txtRequestPort.Text, txtRequestBaudRate.Text),
|
||||
StreamingPort = CreateGciPortConfig(txtStreamingPort.Text, txtStreamingBaudRate.Text)
|
||||
};
|
||||
|
||||
_api.InitOneMeterFromExtern(
|
||||
slot,
|
||||
GetConfigSource(),
|
||||
GetPasswordSource(),
|
||||
requestPort,
|
||||
streamingPort);
|
||||
var result = await _api.InitSlotAsync(request);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception(result.Message);
|
||||
|
||||
MessageBox.Show("Meter initialized.");
|
||||
});
|
||||
}
|
||||
|
||||
private void btnConnectOne_Click(object sender, EventArgs e)
|
||||
/// <summary>
|
||||
/// Connects to one meter.
|
||||
/// </summary>
|
||||
private async void btnConnectOne_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() =>
|
||||
int slot;
|
||||
|
||||
try { slot = GetSlotSafe(); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_api.ConnectOneMeter(GetSlot());
|
||||
MessageBox.Show(ex.Message, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
var result = await _api.ConnectOneSlotAsync(slot);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception(result.Message);
|
||||
|
||||
MessageBox.Show($"Connected.\r\nPCB ID: {result.PcbId}");
|
||||
});
|
||||
}
|
||||
|
||||
private void btnConnectAll_Click(object sender, EventArgs e)
|
||||
/// <summary>
|
||||
/// Disconnects from current meter.
|
||||
/// </summary>
|
||||
private async void btnDisconnect_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() =>
|
||||
int slot;
|
||||
|
||||
try { slot = GetSlotSafe(); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_api.ConnectAllMeters(GetSlot());
|
||||
MessageBox.Show(ex.Message, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
await _api.DisconnectAsync(slot);
|
||||
MessageBox.Show("Disconnected.");
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// Reads PCB ID asynchronously.
|
||||
/// </summary>
|
||||
private async void btnGetPcbId_Click(object sender, EventArgs e)
|
||||
{
|
||||
int slot;
|
||||
|
||||
try { slot = GetSlotSafe(); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
GciPublicModels.GciGetPcbIdResult result =
|
||||
await _api.GetPcbIdAsync(slot);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception(result.Message);
|
||||
|
||||
MessageBox.Show($"PCB ID: {result.PcbId}");
|
||||
});
|
||||
}
|
||||
|
||||
private void btnDisconnect_Click(object sender, EventArgs e)
|
||||
/// <summary>
|
||||
/// Reads register value.
|
||||
/// </summary>
|
||||
private async void btnReadRegister_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() =>
|
||||
{
|
||||
_api.Disconnect();
|
||||
});
|
||||
}
|
||||
int slot;
|
||||
string registerName = txtRegister.Text;
|
||||
|
||||
private void btnGetPcbId_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() =>
|
||||
try
|
||||
{
|
||||
_api.GetPcbId(GetSlot());
|
||||
});
|
||||
}
|
||||
|
||||
private void btnReadRegister_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() =>
|
||||
{
|
||||
string registerName = txtRegister.Text;
|
||||
slot = GetSlotSafe();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(registerName))
|
||||
throw new Exception("Register name is empty.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
_api.ReadRegister(registerName);
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
var result = await _api.ReadRegisterAsync(slot, registerName);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception(result.ErrorMessage);
|
||||
|
||||
MessageBox.Show($"Register: {result.RegisterName}\r\nRaw: {result.RawHex}");
|
||||
});
|
||||
}
|
||||
|
||||
private void btnWriteRegister_Click(object sender, EventArgs e)
|
||||
private async void btnConnectAll_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() =>
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
string registerName = txtRegister.Text;
|
||||
string valueText = txtValue.Text;
|
||||
var slots = _api.GetSelectedSlots();
|
||||
|
||||
if (slots.Count == 0)
|
||||
throw new Exception("No slots selected.");
|
||||
|
||||
foreach (int slot in slots)
|
||||
{
|
||||
var result = await _api.ConnectOneSlotAsync(slot);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception($"Slot {slot}: {result.Message}");
|
||||
}
|
||||
|
||||
MessageBox.Show("Selected meters connected.");
|
||||
});
|
||||
}
|
||||
|
||||
private async void btnWriteRegister_Click(object sender, EventArgs e)
|
||||
{
|
||||
int slot;
|
||||
string registerName = txtRegister.Text;
|
||||
string valueText = txtValue.Text;
|
||||
|
||||
try
|
||||
{
|
||||
slot = GetSlotSafe();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(registerName))
|
||||
throw new Exception("Register name is empty.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
object value = ParseValue(valueText);
|
||||
|
||||
_api.WriteRegister(registerName, value, false, false);
|
||||
var result = await _api.WriteRegisterAsync(
|
||||
slot,
|
||||
registerName,
|
||||
value,
|
||||
false,
|
||||
false);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception(result.ErrorMessage);
|
||||
|
||||
MessageBox.Show($"Write OK: {registerName} = {value}");
|
||||
});
|
||||
}
|
||||
|
||||
private void btnSetPassword_Click(object sender, EventArgs e)
|
||||
private async void btnSetPassword_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() =>
|
||||
int slot;
|
||||
string password = txtPassword.Text;
|
||||
|
||||
try
|
||||
{
|
||||
string password = txtPassword.Text;
|
||||
slot = GetSlotSafe();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
throw new Exception("Password is empty.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
_api.SetMeterPassword(password);
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
bool ok = await _api.SetMeterPasswordAsync(slot, password);
|
||||
|
||||
if (!ok)
|
||||
throw new Exception("Set password failed.");
|
||||
|
||||
MessageBox.Show("Password set successfully.");
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
49
GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/MeterInitView.Designer.cs
generated
Normal file
49
GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/MeterInitView.Designer.cs
generated
Normal file
@ -0,0 +1,49 @@
|
||||
namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
{
|
||||
partial class MeterInitView
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
private System.Windows.Forms.Button btnReloadSetup;
|
||||
private System.Windows.Forms.Button btnSaveSetup;
|
||||
private System.Windows.Forms.Button btnAddSlot;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && components != null)
|
||||
components.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.btnReloadSetup = new System.Windows.Forms.Button();
|
||||
this.btnSaveSetup = new System.Windows.Forms.Button();
|
||||
this.btnAddSlot = new System.Windows.Forms.Button();
|
||||
|
||||
this.SuspendLayout();
|
||||
|
||||
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";
|
||||
this.btnSaveSetup.Click += new System.EventHandler(this.btnSaveSetup_Click);
|
||||
|
||||
this.btnAddSlot.SetBounds(20, 65, 290, 32);
|
||||
this.btnAddSlot.Text = "Add Slot";
|
||||
this.btnAddSlot.Click += new System.EventHandler(this.btnAddSlot_Click);
|
||||
|
||||
this.Controls.Add(this.btnReloadSetup);
|
||||
this.Controls.Add(this.btnSaveSetup);
|
||||
this.Controls.Add(this.btnAddSlot);
|
||||
|
||||
this.Name = "MeterInitView";
|
||||
this.Size = new System.Drawing.Size(340, 130);
|
||||
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using GenesisCordonelInterface.API;
|
||||
|
||||
namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
{
|
||||
public partial class MeterInitView : UserControl
|
||||
{
|
||||
private readonly InterfaceOutsideToGCI _api;
|
||||
private readonly Action _addSlotAction;
|
||||
private readonly Action _saveAction;
|
||||
|
||||
public MeterInitView(InterfaceOutsideToGCI api, Action addSlotAction, Action saveAction)
|
||||
{
|
||||
_api = api;
|
||||
_addSlotAction = addSlotAction;
|
||||
_saveAction = saveAction;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
#region BUTTONS
|
||||
// ----------------------------------------------------
|
||||
|
||||
private void btnReloadSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() => _api.ReloadSlotSetup());
|
||||
}
|
||||
|
||||
private void btnSaveSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
_saveAction?.Invoke();
|
||||
}
|
||||
|
||||
private void btnAddSlot_Click(object sender, EventArgs e)
|
||||
{
|
||||
_addSlotAction?.Invoke();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
private void ExecuteApiAction(Action action)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetBusy(true);
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "API call failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBusy(bool busy)
|
||||
{
|
||||
Cursor = busy ? Cursors.WaitCursor : Cursors.Default;
|
||||
|
||||
btnReloadSetup.Enabled = !busy;
|
||||
btnSaveSetup.Enabled = !busy;
|
||||
btnAddSlot.Enabled = !busy;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/MetersActionView.Designer.cs
generated
Normal file
99
GenesisCordonelInterface/UI/StaraTuraAPI_GenesisCordonelInterface/MetersActionView.Designer.cs
generated
Normal file
@ -0,0 +1,99 @@
|
||||
namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
{
|
||||
partial class MetersActionView
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
private System.Windows.Forms.Label lblRegister;
|
||||
private System.Windows.Forms.TextBox txtRegister;
|
||||
private System.Windows.Forms.Label lblValue;
|
||||
private System.Windows.Forms.TextBox txtValue;
|
||||
private System.Windows.Forms.Label lblPassword;
|
||||
private System.Windows.Forms.TextBox txtPassword;
|
||||
|
||||
private System.Windows.Forms.Button btnConnectSelected;
|
||||
private System.Windows.Forms.Button btnReadRegister;
|
||||
private System.Windows.Forms.Button btnWriteRegister;
|
||||
private System.Windows.Forms.Button btnSetPassword;
|
||||
private System.Windows.Forms.Button btnDisconnectSelected;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && components != null)
|
||||
components.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.lblRegister = new System.Windows.Forms.Label();
|
||||
this.txtRegister = new System.Windows.Forms.TextBox();
|
||||
this.lblValue = new System.Windows.Forms.Label();
|
||||
this.txtValue = new System.Windows.Forms.TextBox();
|
||||
this.lblPassword = new System.Windows.Forms.Label();
|
||||
this.txtPassword = new System.Windows.Forms.TextBox();
|
||||
|
||||
this.btnConnectSelected = new System.Windows.Forms.Button();
|
||||
this.btnReadRegister = new System.Windows.Forms.Button();
|
||||
this.btnWriteRegister = new System.Windows.Forms.Button();
|
||||
this.btnSetPassword = new System.Windows.Forms.Button();
|
||||
this.btnDisconnectSelected = new System.Windows.Forms.Button();
|
||||
|
||||
this.SuspendLayout();
|
||||
|
||||
this.lblRegister.SetBounds(20, 20, 100, 20);
|
||||
this.lblRegister.Text = "Register:";
|
||||
this.txtRegister.SetBounds(130, 17, 250, 20);
|
||||
this.txtRegister.Text = "GENESISFLOW_TriggerTest";
|
||||
|
||||
this.lblValue.SetBounds(20, 55, 100, 20);
|
||||
this.lblValue.Text = "Value:";
|
||||
this.txtValue.SetBounds(130, 52, 250, 20);
|
||||
this.txtValue.Text = "1";
|
||||
|
||||
this.lblPassword.SetBounds(20, 90, 100, 20);
|
||||
this.lblPassword.Text = "Password:";
|
||||
this.txtPassword.SetBounds(130, 87, 250, 20);
|
||||
this.txtPassword.Text = "1234";
|
||||
|
||||
this.btnConnectSelected.SetBounds(20, 140, 170, 32);
|
||||
this.btnConnectSelected.Text = "Connect Selected";
|
||||
this.btnConnectSelected.Click += new System.EventHandler(this.btnConnectSelected_Click);
|
||||
|
||||
this.btnDisconnectSelected.SetBounds(210, 140, 170, 32);
|
||||
this.btnDisconnectSelected.Text = "Disconnect Selected";
|
||||
this.btnDisconnectSelected.Click += new System.EventHandler(this.btnDisconnectSelected_Click);
|
||||
|
||||
this.btnReadRegister.SetBounds(20, 185, 170, 32);
|
||||
this.btnReadRegister.Text = "Read Register";
|
||||
this.btnReadRegister.Click += new System.EventHandler(this.btnReadRegister_Click);
|
||||
|
||||
this.btnWriteRegister.SetBounds(210, 185, 170, 32);
|
||||
this.btnWriteRegister.Text = "Write Register";
|
||||
this.btnWriteRegister.Click += new System.EventHandler(this.btnWriteRegister_Click);
|
||||
|
||||
this.btnSetPassword.SetBounds(20, 230, 170, 32);
|
||||
this.btnSetPassword.Text = "Set Password";
|
||||
this.btnSetPassword.Click += new System.EventHandler(this.btnSetPassword_Click);
|
||||
|
||||
this.Controls.Add(this.lblRegister);
|
||||
this.Controls.Add(this.txtRegister);
|
||||
this.Controls.Add(this.lblValue);
|
||||
this.Controls.Add(this.txtValue);
|
||||
this.Controls.Add(this.lblPassword);
|
||||
this.Controls.Add(this.txtPassword);
|
||||
this.Controls.Add(this.btnConnectSelected);
|
||||
this.Controls.Add(this.btnDisconnectSelected);
|
||||
this.Controls.Add(this.btnReadRegister);
|
||||
this.Controls.Add(this.btnWriteRegister);
|
||||
this.Controls.Add(this.btnSetPassword);
|
||||
|
||||
this.Name = "MetersActionView";
|
||||
this.Size = new System.Drawing.Size(420, 320);
|
||||
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using GenesisCordonelInterface.API;
|
||||
|
||||
namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
{
|
||||
public partial class MetersActionView : UserControl
|
||||
{
|
||||
private readonly InterfaceOutsideToGCI _api;
|
||||
|
||||
public MetersActionView(InterfaceOutsideToGCI api)
|
||||
{
|
||||
_api = api ?? throw new ArgumentNullException(nameof(api));
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async Task ExecuteApiActionAsync(Func<Task> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetBusy(true);
|
||||
await action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "API call failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBusy(bool busy)
|
||||
{
|
||||
Cursor = busy ? Cursors.WaitCursor : Cursors.Default;
|
||||
|
||||
btnConnectSelected.Enabled = !busy;
|
||||
btnReadRegister.Enabled = !busy;
|
||||
btnWriteRegister.Enabled = !busy;
|
||||
btnSetPassword.Enabled = !busy;
|
||||
btnDisconnectSelected.Enabled = !busy;
|
||||
}
|
||||
|
||||
private object ParseValue(string input)
|
||||
{
|
||||
if (int.TryParse(input, out int i)) return i;
|
||||
if (uint.TryParse(input, out uint ui)) return ui;
|
||||
if (bool.TryParse(input, out bool b)) return b;
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
private void ValidateSelectedSlots()
|
||||
{
|
||||
if (_api.GetSelectedSlots().Count == 0)
|
||||
throw new Exception("No slots selected.");
|
||||
}
|
||||
|
||||
private async void btnConnectSelected_Click(object sender, EventArgs e)
|
||||
{
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
ValidateSelectedSlots();
|
||||
|
||||
foreach (int slot in _api.GetSelectedSlots())
|
||||
{
|
||||
var result = await _api.ConnectOneSlotAsync(slot);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception($"Slot {slot}: {result.Message}");
|
||||
}
|
||||
|
||||
MessageBox.Show("Selected meters connected.");
|
||||
});
|
||||
}
|
||||
|
||||
private async void btnReadRegister_Click(object sender, EventArgs e)
|
||||
{
|
||||
string registerName = txtRegister.Text;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(registerName))
|
||||
{
|
||||
MessageBox.Show("Register name is empty.", "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
ValidateSelectedSlots();
|
||||
|
||||
foreach (int slot in _api.GetSelectedSlots())
|
||||
{
|
||||
var result = await _api.ReadRegisterAsync(slot, registerName);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception($"Slot {slot}: {result.ErrorMessage}");
|
||||
}
|
||||
|
||||
MessageBox.Show("Read register finished.");
|
||||
});
|
||||
}
|
||||
|
||||
private async void btnWriteRegister_Click(object sender, EventArgs e)
|
||||
{
|
||||
string registerName = txtRegister.Text;
|
||||
string valueText = txtValue.Text;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(registerName))
|
||||
{
|
||||
MessageBox.Show("Register name is empty.", "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
ValidateSelectedSlots();
|
||||
|
||||
object value = ParseValue(valueText);
|
||||
|
||||
foreach (int slot in _api.GetSelectedSlots())
|
||||
{
|
||||
var result = await _api.WriteRegisterAsync(
|
||||
slot,
|
||||
registerName,
|
||||
value,
|
||||
false,
|
||||
false);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception($"Slot {slot}: {result.ErrorMessage}");
|
||||
}
|
||||
|
||||
MessageBox.Show("Write register finished.");
|
||||
});
|
||||
}
|
||||
|
||||
private async void btnSetPassword_Click(object sender, EventArgs e)
|
||||
{
|
||||
string password = txtPassword.Text;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
MessageBox.Show("Password is empty.", "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
ValidateSelectedSlots();
|
||||
|
||||
foreach (int slot in _api.GetSelectedSlots())
|
||||
{
|
||||
bool ok = await _api.SetMeterPasswordAsync(slot, password);
|
||||
|
||||
if (!ok)
|
||||
throw new Exception($"Slot {slot}: Set password failed.");
|
||||
}
|
||||
|
||||
MessageBox.Show("Password set for selected meters.");
|
||||
});
|
||||
}
|
||||
|
||||
private async void btnDisconnectSelected_Click(object sender, EventArgs e)
|
||||
{
|
||||
await ExecuteApiActionAsync(async () =>
|
||||
{
|
||||
ValidateSelectedSlots();
|
||||
|
||||
foreach (int slot in _api.GetSelectedSlots())
|
||||
{
|
||||
await _api.DisconnectAsync(slot);
|
||||
}
|
||||
|
||||
MessageBox.Show("Selected meters disconnected.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
35
TBF/Build/CopyGci.targets.xml
Normal file
35
TBF/Build/CopyGci.targets.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!--
|
||||
Copies the prepared GCI runtime package into the TBF output directory.
|
||||
This ensures all GCI dependencies are available next to TBF.exe.
|
||||
-->
|
||||
<Target Name="CopyGciRuntimePackageToTbfOutput" AfterTargets="Build">
|
||||
|
||||
<!-- Source directory containing the prepared GCI runtime package -->
|
||||
<PropertyGroup>
|
||||
<GciRuntimePackageDir>$(ProjectDir)..\GenesisCordonelInterface\RuntimePackage\Package\</GciRuntimePackageDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Collect runtime package files -->
|
||||
<ItemGroup>
|
||||
<GciRuntimeFiles Include="$(GciRuntimePackageDir)*.dll" />
|
||||
<GciRuntimeFiles Include="$(GciRuntimePackageDir)*.exe" />
|
||||
<GciRuntimeFiles Include="$(GciRuntimePackageDir)*.config" />
|
||||
<GciRuntimeFiles Include="$(GciRuntimePackageDir)*.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Build output messages -->
|
||||
<Message Text="Copying GCI runtime package to TBF output: $(TargetDir)" Importance="High" />
|
||||
<Message Text="Source: $(GciRuntimePackageDir)" Importance="High" />
|
||||
<Message Text="Files: @(GciRuntimeFiles)" Importance="Normal" />
|
||||
|
||||
<!-- Copy package files into TBF output directory -->
|
||||
<Copy
|
||||
SourceFiles="@(GciRuntimeFiles)"
|
||||
DestinationFolder="$(TargetDir)"
|
||||
SkipUnchangedFiles="false" />
|
||||
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
2
TBF/Properties/Settings.Designer.cs
generated
2
TBF/Properties/Settings.Designer.cs
generated
@ -12,7 +12,7 @@ namespace TBF.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
///
|
||||
using GenesisCordonelInterface.API;
|
||||
using log4net;
|
||||
///
|
||||
/// Copyright (c) 2015-2021 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using TBF.Rig.Generic;
|
||||
using GciGUIType = GenesisCordonelInterface.UI.MainView;
|
||||
using GciType = GenesisCordonelInterface.API.InterfaceOutsideToGCI;
|
||||
using UdsReaderType = TBF.Rig.Input.DataStorage.UniDataStorageReader.Reader;
|
||||
using UdsWriterType = TBF.Rig.Output.DataStorage.UniDataStorageWriter.Writer;
|
||||
using ExternalInterfaceType = GenesisCordonelInterface.API.InterfaceOutsideToGCI;
|
||||
using ExternalInterfaceGUIType = GenesisCordonelInterface.UI.MainForm;
|
||||
|
||||
namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
{
|
||||
@ -19,31 +23,21 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
public class GciBridge : ComponentBase
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(GciBridge));
|
||||
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}({1})", ClassName, Cfg.ToString(-1));
|
||||
}
|
||||
|
||||
readonly GciBridgeCfg gciBridgeCfg;
|
||||
|
||||
/// <summary>
|
||||
/// Linked UniDataStorage reader component.
|
||||
/// </summary>
|
||||
readonly UdsReaderType reader;
|
||||
|
||||
/// <summary>
|
||||
/// Linked UniDataStorage writer component.
|
||||
/// </summary>
|
||||
readonly UdsWriterType writer;
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder for GCI GUI entry point.
|
||||
/// Replace object with real GCI MainForm type later.
|
||||
/// </summary>
|
||||
ExternalInterfaceGUIType gciGUI;
|
||||
GciGUIType gciGUI;
|
||||
Form gciGuiHostForm;
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder for GCI external/public interface.
|
||||
/// Replace object with real GCI interface type later.
|
||||
/// </summary>
|
||||
ExternalInterfaceType gciExternalInterface;
|
||||
GciType gciExternalInterface;
|
||||
|
||||
public bool HasReader { get { return reader != null; } }
|
||||
public bool HasWriter { get { return writer != null; } }
|
||||
@ -52,6 +46,16 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
public ExternalInterfaceType GciExternalInterface { get { return gciExternalInterface; } }
|
||||
public GciBridgeCfg GciBridgeCfg { get { return gciBridgeCfg; } }
|
||||
|
||||
public UdsReaderType GetReader()
|
||||
{
|
||||
return reader;
|
||||
}
|
||||
|
||||
public UdsWriterType GetWriter()
|
||||
{
|
||||
return writer;
|
||||
}
|
||||
|
||||
public GciBridge() { }
|
||||
|
||||
public GciBridge(IComponentCfg cfg, IList<IComponent> components)
|
||||
@ -93,36 +97,40 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
log.FatalFormat("{0} initialized: {1}", Name, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes access to GCI GUI.
|
||||
/// Replace placeholder implementation with real MainForm creation.
|
||||
/// </summary>
|
||||
void TryInitializeGui()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (gciGUI = new ExternalInterfaceGUIType())
|
||||
{
|
||||
gciGUI.ShowDialog(/*this*/);
|
||||
}
|
||||
if (gciGuiHostForm != null && !gciGuiHostForm.IsDisposed)
|
||||
return;
|
||||
|
||||
log.InfoFormat("{0}: GCI GUI initialized.", Name);
|
||||
gciGUI = new GciGUIType();
|
||||
gciGUI.Dock = DockStyle.Fill;
|
||||
|
||||
gciGuiHostForm = new Form();
|
||||
gciGuiHostForm.Text = "Genesis Cordonel Interface";
|
||||
gciGuiHostForm.Width = 1300;
|
||||
gciGuiHostForm.Height = 600;
|
||||
gciGuiHostForm.StartPosition = FormStartPosition.CenterScreen;
|
||||
|
||||
gciGuiHostForm.Controls.Add(gciGUI);
|
||||
|
||||
log.InfoFormat("{0}: GCI GUI view initialized.", Name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error("Failed to initialize GCI GUI.", ex);
|
||||
log.Error("Failed to initialize GCI GUI view.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes access to GCI external/public interface.
|
||||
/// Replace placeholder implementation with real interface creation.
|
||||
/// </summary>
|
||||
void TryInitializeExternalInterface()
|
||||
{
|
||||
try
|
||||
{
|
||||
gciExternalInterface = new ExternalInterfaceType();
|
||||
if (gciExternalInterface != null)
|
||||
return;
|
||||
|
||||
gciExternalInterface = new GciType();
|
||||
|
||||
log.InfoFormat("{0}: GCI external interface initialized.", Name);
|
||||
}
|
||||
@ -132,80 +140,147 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows GCI GUI if GUI access is enabled.
|
||||
/// </summary>
|
||||
public void ShowGui()
|
||||
{
|
||||
if (!gciBridgeCfg.EnableGuiAccess) return;
|
||||
|
||||
if (!IsGuiInitialized)
|
||||
if (gciGuiHostForm == null || gciGuiHostForm.IsDisposed)
|
||||
{
|
||||
TryInitializeGui();
|
||||
}
|
||||
|
||||
if (gciGuiHostForm == null) return;
|
||||
|
||||
gciGuiHostForm.Show();
|
||||
gciGuiHostForm.BringToFront();
|
||||
|
||||
log.InfoFormat("{0}: ShowGui invoked.", Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides GCI GUI if GUI access is enabled.
|
||||
/// </summary>
|
||||
public void HideGui()
|
||||
{
|
||||
if (!gciBridgeCfg.EnableGuiAccess) return;
|
||||
if (!IsGuiInitialized) return;
|
||||
if (gciGuiHostForm == null || gciGuiHostForm.IsDisposed) return;
|
||||
|
||||
gciGuiHostForm.Hide();
|
||||
|
||||
using (gciGUI)
|
||||
{
|
||||
gciGUI.Hide(/*this*/);
|
||||
}
|
||||
log.InfoFormat("{0}: HideGui invoked.", Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects the external GCI interface.
|
||||
/// </summary>
|
||||
public void ConnectExternal()
|
||||
void EnsureExternalInterface()
|
||||
{
|
||||
if (!gciBridgeCfg.EnableExternalAccess) return;
|
||||
if (!gciBridgeCfg.EnableExternalAccess)
|
||||
throw new Exception("GCI external access is disabled.");
|
||||
|
||||
if (!IsExternalInitialized)
|
||||
{
|
||||
TryInitializeExternalInterface();
|
||||
}
|
||||
|
||||
/// TODO:
|
||||
/// Replace with real external interface connect call.
|
||||
log.InfoFormat("{0}: ConnectExternal invoked.", Name);
|
||||
if (gciExternalInterface == null)
|
||||
throw new Exception("GCI external interface is not initialized.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects the external GCI interface.
|
||||
/// </summary>
|
||||
public void DisconnectExternal()
|
||||
void EnsureReader()
|
||||
{
|
||||
if (!gciBridgeCfg.EnableExternalAccess) return;
|
||||
if (!IsExternalInitialized) return;
|
||||
|
||||
/// TODO:
|
||||
/// Replace with real external interface disconnect call.
|
||||
log.InfoFormat("{0}: DisconnectExternal invoked.", Name);
|
||||
if (reader == null)
|
||||
throw new Exception("UniDataStorageReader is not linked to GciBridge.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns linked reader component.
|
||||
/// </summary>
|
||||
public UdsReaderType GetReader()
|
||||
// API:
|
||||
#region ======================================= GCI Public Interface =======================================
|
||||
|
||||
public async Task<GciPublicModels.GciInitSlotResult> InitSlotAsync(GciPublicModels.GciInitSlotRequest request)
|
||||
{
|
||||
return reader;
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (request == null)
|
||||
throw new ArgumentNullException("request");
|
||||
|
||||
if (request.SlotId <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
GciPublicModels.GciInitSlotResult result =
|
||||
await gciExternalInterface.InitSlotAsync(request);
|
||||
|
||||
log.InfoFormat("{0}: InitSlotAsync invoked. {1}, Result={2}", Name, request, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns linked writer component.
|
||||
/// </summary>
|
||||
public UdsWriterType GetWriter()
|
||||
public async Task<GciPublicModels.GciSlotInfo> GetSlotAsync(int slotId)
|
||||
{
|
||||
return writer;
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (slotId <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
GciPublicModels.GciSlotInfo result =
|
||||
await gciExternalInterface.GetSlotAsync(slotId);
|
||||
|
||||
log.InfoFormat("{0}: GetSlotAsync({1}) invoked. Result={2}", Name, slotId, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GciPublicModels.GciCleanSlotsResult> CleanSlotsAsync()
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
GciPublicModels.GciCleanSlotsResult result =
|
||||
await gciExternalInterface.CleanSlotsAsync();
|
||||
|
||||
log.InfoFormat("{0}: CleanSlotsAsync invoked.", Name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GciPublicModels.GciGetPcbIdResult> GetPcbIdAsync(
|
||||
int slotId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (slotId <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await gciExternalInterface.GetPcbIdAsync(slotId, token);
|
||||
|
||||
log.InfoFormat("{0}: GetPcbIdAsync({1}) invoked. Result={2}", Name, slotId, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GciPublicModels.GciConnectResult> ConnectAsync(
|
||||
int slotId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (slotId <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await gciExternalInterface.ConnectOneSlotAsync(slotId, token);
|
||||
|
||||
log.InfoFormat("{0}: ConnectAsync({1}) invoked. Result={2}", Name, slotId, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GciPublicModels.GciDisconnectResult> DisconnectAsync(
|
||||
int slotId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (slotId <= 0)
|
||||
throw new ArgumentException("Invalid slot id.");
|
||||
|
||||
var result = await gciExternalInterface.DisconnectAsync(slotId, token);
|
||||
|
||||
log.InfoFormat("{0}: DisconnectAsync({1}) invoked. Result={2}", Name, slotId, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -172,12 +172,42 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
private void connectExternalButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
/// TODO
|
||||
try
|
||||
{
|
||||
GciBridge bridge = TbfComponents.FindComponent(config.Name) as GciBridge;
|
||||
|
||||
if (bridge == null)
|
||||
{
|
||||
MessageBox.Show("GciBridge component was not found.", "GCI Bridge");
|
||||
return;
|
||||
}
|
||||
|
||||
//bridge.ConnectExternal();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "GCI Bridge error");
|
||||
}
|
||||
}
|
||||
|
||||
private void showGuiButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
/// TODO
|
||||
try
|
||||
{
|
||||
GciBridge bridge = TbfComponents.FindComponent(config.Name) as GciBridge;
|
||||
|
||||
if (bridge == null)
|
||||
{
|
||||
MessageBox.Show("GciBridge component was not found.", "GCI Bridge");
|
||||
return;
|
||||
}
|
||||
|
||||
bridge.ShowGui();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "GCI Bridge error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
TBF/Rig/BridgeComponents/GciBridge/GciBridgeOp.cs
Normal file
14
TBF/Rig/BridgeComponents/GciBridge/GciBridgeOp.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using log4net;
|
||||
///
|
||||
/// Copyright (c) 2015-2021 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using TBF.Rig.Sequences;
|
||||
|
||||
namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
{
|
||||
public class GciBridgeOp
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -176,6 +176,19 @@
|
||||
<Reference Include="Xylem.Common.CommonCore">
|
||||
<HintPath>..\GenesisCordonelInterface\bin\Debug\Xylem.Common.CommonCore.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xylem.Common.Hardware.Interfaces.Ports.PortCore, Version=2.8.18.15967, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Common\Xylem.Common.Hardware.Interfaces.Ports.PortCore.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig">
|
||||
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore">
|
||||
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xylem.Common.Hardware.WaterMeter.WaterMeterCore">
|
||||
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.WaterMeterCore.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xylem.Common.Hardware.WaterMeter.WaterMeterRegisters">
|
||||
<HintPath>..\GenesisCordonelInterface\bin\Debug\Xylem.Common.Hardware.WaterMeter.WaterMeterRegisters.dll</HintPath>
|
||||
</Reference>
|
||||
@ -205,6 +218,7 @@
|
||||
<Compile Include="Rig\BridgeComponents\GciBridge\GciBridgeCfgCtrl.Designer.cs">
|
||||
<DependentUpon>GciBridgeCfgCtrl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Rig\BridgeComponents\GciBridge\GciBridgeOp.cs" />
|
||||
<Compile Include="Rig\BuiltIn\PumpTandem\Pump.cs" />
|
||||
<Compile Include="Rig\BuiltIn\PumpTandem\PumpCfg.cs" />
|
||||
<Compile Include="Rig\BuiltIn\PumpTandem\PumpCfgCtrl.cs">
|
||||
@ -1781,6 +1795,20 @@
|
||||
<DependentUpon>TestMethodCfgCtrl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Rig\TestMethods\FlyingStartMassCollection\Single\TestParams.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\CalibrationStruct.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\ConfigStruct.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\Enums.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\Factory.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHead.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfg.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfgCtrl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfgCtrl.designer.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\OptoReceivedEventArgs.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\OptoTelegramRaw.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\ProcParams.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\StatusStruct.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\Factory.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisCommunicationSeq.cs" />
|
||||
<Compile Include="Rig\TestMethods\GenesisCommunication\iPerlCommunicationParams.cs" />
|
||||
@ -3739,6 +3767,7 @@
|
||||
<EmbeddedResource Include="Rig\TestMethods\FlyingStartTankCollection\Single\TestMethodCfgCtrl.resx">
|
||||
<DependentUpon>TestMethodCfgCtrl.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadCfgCtrl.resx" />
|
||||
<EmbeddedResource Include="Rig\TestMethods\GenesisCommunication\TestMethodCfgCtrl.resx">
|
||||
<DependentUpon>TestMethodCfgCtrl.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
@ -4164,6 +4193,7 @@
|
||||
<DependentUpon>TestProgressCtrl.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<None Include="app.config" />
|
||||
<Content Include="Build\CopyGci.targets.xml" />
|
||||
<Content Include="CameraBinaryFiles\GrabImage">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@ -4340,10 +4370,6 @@
|
||||
<Project>{439D0878-C76E-452B-B17D-209A89E91D36}</Project>
|
||||
<Name>Dirichlet.Numerics</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\Hardware\WaterMeter\Genesis\GenesisCore\GenesisCore.csproj">
|
||||
<Project>{04201e95-90a0-4b73-a72f-379739b2a3ae}</Project>
|
||||
<Name>GenesisCore</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GemCard\GemCard.csproj">
|
||||
<Project>{8B10D15A-39DE-4B56-8DD1-710C1EB3A697}</Project>
|
||||
<Name>GemCard</Name>
|
||||
@ -4385,6 +4411,7 @@
|
||||
<Name>Results</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="Build\CopyGci.targets.xml" />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@ -1,39 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="oracle.manageddataaccess.client" type="OracleInternal.Common.ODPMSectionHandler, Oracle.ManagedDataAccess, Version=4.122.19.1, Culture=neutral, PublicKeyToken=89b483f429c47342" />
|
||||
<section name="oracle.manageddataaccess.client" type="OracleInternal.Common.ODPMSectionHandler, Oracle.ManagedDataAccess, Version=4.122.19.1, Culture=neutral, PublicKeyToken=89b483f429c47342"/>
|
||||
</configSections>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
|
||||
</startup>
|
||||
<system.data>
|
||||
<DbProviderFactories>
|
||||
<remove invariant="Oracle.ManagedDataAccess.Client" />
|
||||
<add name="ODP.NET, Managed Driver" invariant="Oracle.ManagedDataAccess.Client" description="Oracle Data Provider for .NET, Managed Driver" type="Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess, Version=4.122.19.1, Culture=neutral, PublicKeyToken=89b483f429c47342" />
|
||||
<remove invariant="Oracle.ManagedDataAccess.Client"/>
|
||||
<add name="ODP.NET, Managed Driver" invariant="Oracle.ManagedDataAccess.Client" description="Oracle Data Provider for .NET, Managed Driver" type="Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess, Version=4.122.19.1, Culture=neutral, PublicKeyToken=89b483f429c47342"/>
|
||||
</DbProviderFactories>
|
||||
</system.data>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<publisherPolicy apply="no" />
|
||||
<assemblyIdentity name="Oracle.ManagedDataAccess" publicKeyToken="89b483f429c47342" culture="neutral" />
|
||||
<bindingRedirect oldVersion="4.121.0.0 - 4.65535.65535.65535" newVersion="4.122.19.1" />
|
||||
<publisherPolicy apply="no"/>
|
||||
<assemblyIdentity name="Oracle.ManagedDataAccess" publicKeyToken="89b483f429c47342" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="4.121.0.0 - 4.65535.65535.65535" newVersion="4.122.19.1"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Iesi.Collections" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
<assemblyIdentity name="Iesi.Collections" publicKeyToken="aa95f207798dfdb4" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="mscorlib" publicKeyToken="b77a5c561934e089" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
<assemblyIdentity name="mscorlib" publicKeyToken="b77a5c561934e089" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.4000" newVersion="4.0.0.4000" />
|
||||
<assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.4000" newVersion="4.0.0.4000"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Logic.ProductionToProductMapper" publicKeyToken="null" culture="neutral" />
|
||||
@ -84,7 +84,7 @@
|
||||
<oracle.manageddataaccess.client>
|
||||
<version number="*">
|
||||
<dataSources>
|
||||
<dataSource alias="SampleDataSource" descriptor="(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL))) " />
|
||||
<dataSource alias="SampleDataSource" descriptor="(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL))) "/>
|
||||
</dataSources>
|
||||
</version>
|
||||
</oracle.manageddataaccess.client>
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
<package id="Castle.Core" version="5.1.1" targetFramework="net472" />
|
||||
<package id="FluentNHibernate" version="2.0.3.0" targetFramework="net40" />
|
||||
<package id="Iesi.Collections" version="4.0.0.4000" targetFramework="net40" />
|
||||
<package id="MySql.Data" version="6.6.5" targetFramework="net20" />
|
||||
<package id="MySql.Data" version="6.6.5" targetFramework="net20" requireReinstallation="true" />
|
||||
<package id="NHibernate" version="4.0.4.4000" targetFramework="net40" />
|
||||
<package id="NLog" version="5.2.2" targetFramework="net472" />
|
||||
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net472" />
|
||||
<package id="Oracle.ManagedDataAccess" version="19.11.0" targetFramework="net472" />
|
||||
<package id="System.Data.SQLite" version="1.0.90.0" targetFramework="net40" />
|
||||
<package id="System.Data.SQLite" version="1.0.90.0" targetFramework="net40" requireReinstallation="true" />
|
||||
<package id="System.Drawing.Common" version="9.0.5" targetFramework="net472" />
|
||||
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net472" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.3" targetFramework="net472" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user