LmxProxy is no longer needed. Moved the entire lmxproxy/ workspace, DCL adapter files, and related docs to deprecated/. Removed LmxProxy registration from DataConnectionFactory, project reference from DCL, protocol option from UI, and cleaned up all requirement docs.
167 lines
6.9 KiB
C#
167 lines
6.9 KiB
C#
using System;
|
|
using ArchestrA.MxAccess;
|
|
using ZB.MOM.WW.LmxProxy.Host.Domain;
|
|
|
|
namespace ZB.MOM.WW.LmxProxy.Host.Implementation
|
|
{
|
|
/// <summary>
|
|
/// Event handlers for MxAccessClient to process data changes, write completions, and operation completions.
|
|
/// </summary>
|
|
public sealed partial class MxAccessClient
|
|
{
|
|
/// <summary>
|
|
/// Handles data change events from the MxAccess server.
|
|
/// </summary>
|
|
/// <param name="hLMXServerHandle">Server handle.</param>
|
|
/// <param name="phItemHandle">Item handle.</param>
|
|
/// <param name="pvItemValue">Item value.</param>
|
|
/// <param name="pwItemQuality">Item quality code.</param>
|
|
/// <param name="pftItemTimeStamp">Item timestamp.</param>
|
|
/// <param name="ItemStatus">Status array.</param>
|
|
private void OnDataChange(int hLMXServerHandle, int phItemHandle, object pvItemValue,
|
|
int pwItemQuality, object pftItemTimeStamp, ref MXSTATUS_PROXY[] ItemStatus)
|
|
{
|
|
try
|
|
{
|
|
if (!_subscriptionsByHandle.TryGetValue(phItemHandle, out SubscriptionInfo? subscription))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Convert quality from integer
|
|
Quality quality = ConvertQuality(pwItemQuality);
|
|
DateTime timestamp = ConvertTimestamp(pftItemTimeStamp);
|
|
var vtq = new Vtq(pvItemValue, timestamp, quality);
|
|
|
|
// Invoke callback
|
|
subscription.Callback?.Invoke(subscription.Address, vtq);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Error(ex, "Error processing data change for handle {Handle}", phItemHandle);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles write completion events from the MxAccess server.
|
|
/// </summary>
|
|
/// <param name="hLMXServerHandle">Server handle.</param>
|
|
/// <param name="phItemHandle">Item handle.</param>
|
|
/// <param name="ItemStatus">Status array.</param>
|
|
private void OnWriteComplete(int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus)
|
|
{
|
|
try
|
|
{
|
|
WriteOperation? writeOp;
|
|
|
|
lock (_lock)
|
|
{
|
|
if (_pendingWrites.TryGetValue(phItemHandle, out writeOp))
|
|
{
|
|
_pendingWrites.Remove(phItemHandle);
|
|
}
|
|
}
|
|
|
|
if (writeOp != null)
|
|
{
|
|
try
|
|
{
|
|
if (ItemStatus is { Length: > 0 })
|
|
{
|
|
var status = ItemStatus[0];
|
|
if (status.success == 0)
|
|
{
|
|
string errorMsg = GetWriteErrorMessage(status.detail);
|
|
Logger.Warning(
|
|
"Write failed for {Address} (handle {Handle}): {Error} (Category={Category}, Detail={Detail})",
|
|
writeOp.Address, phItemHandle, errorMsg, status.category, status.detail);
|
|
|
|
writeOp.CompletionSource.TrySetException(new InvalidOperationException(
|
|
$"Write failed: {errorMsg}"));
|
|
}
|
|
else
|
|
{
|
|
Logger.Debug("Write completed successfully for {Address} (handle {Handle})",
|
|
writeOp.Address, phItemHandle);
|
|
writeOp.CompletionSource.TrySetResult(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Logger.Debug("Write completed for {Address} (handle {Handle}) with no status",
|
|
writeOp.Address, phItemHandle);
|
|
writeOp.CompletionSource.TrySetResult(true);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
// Clean up the item after write completes
|
|
lock (_lock)
|
|
{
|
|
if (_lmxProxy != null)
|
|
{
|
|
try
|
|
{
|
|
_lmxProxy.UnAdvise(_connectionHandle, phItemHandle);
|
|
_lmxProxy.RemoveItem(_connectionHandle, phItemHandle);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Debug(ex, "Error cleaning up after write for handle {Handle}", phItemHandle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ItemStatus is { Length: > 0 })
|
|
{
|
|
var status = ItemStatus[0];
|
|
if (status.success == 0)
|
|
{
|
|
Logger.Warning("Write failed for unknown handle {Handle}: Category={Category}, Detail={Detail}",
|
|
phItemHandle, status.category, status.detail);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Error(ex, "Error processing write complete for handle {Handle}", phItemHandle);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles operation completion events from the MxAccess server.
|
|
/// </summary>
|
|
/// <param name="hLMXServerHandle">Server handle.</param>
|
|
/// <param name="phItemHandle">Item handle.</param>
|
|
/// <param name="ItemStatus">Status array.</param>
|
|
private void OnOperationComplete(int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus)
|
|
{
|
|
// Log operation completion
|
|
Logger.Debug("Operation complete for handle {Handle}", phItemHandle);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts an integer MxAccess quality code to <see cref="Quality" />.
|
|
/// </summary>
|
|
/// <param name="mxQuality">The MxAccess quality code.</param>
|
|
/// <returns>The corresponding <see cref="Quality" /> value.</returns>
|
|
private Quality ConvertQuality(int mxQuality) => (Quality)mxQuality;
|
|
|
|
/// <summary>
|
|
/// Converts a timestamp object to <see cref="DateTime" /> in UTC.
|
|
/// </summary>
|
|
/// <param name="timestamp">The timestamp object.</param>
|
|
/// <returns>The UTC <see cref="DateTime" /> value.</returns>
|
|
private DateTime ConvertTimestamp(object timestamp)
|
|
{
|
|
if (timestamp is DateTime dt)
|
|
{
|
|
return dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime();
|
|
}
|
|
|
|
return DateTime.UtcNow;
|
|
}
|
|
}
|
|
}
|