using System;
using ArchestrA.MxAccess;
using ZB.MOM.WW.LmxProxy.Host.Domain;
namespace ZB.MOM.WW.LmxProxy.Host.Implementation
{
///
/// Event handlers for MxAccessClient to process data changes, write completions, and operation completions.
///
public sealed partial class MxAccessClient
{
///
/// Handles data change events from the MxAccess server.
///
/// Server handle.
/// Item handle.
/// Item value.
/// Item quality code.
/// Item timestamp.
/// Status array.
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);
}
}
///
/// Handles write completion events from the MxAccess server.
///
/// Server handle.
/// Item handle.
/// Status array.
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);
}
}
///
/// Handles operation completion events from the MxAccess server.
///
/// Server handle.
/// Item handle.
/// Status array.
private void OnOperationComplete(int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus)
{
// Log operation completion
Logger.Debug("Operation complete for handle {Handle}", phItemHandle);
}
///
/// Converts an integer MxAccess quality code to .
///
/// The MxAccess quality code.
/// The corresponding value.
private Quality ConvertQuality(int mxQuality) => (Quality)mxQuality;
///
/// Converts a timestamp object to in UTC.
///
/// The timestamp object.
/// The UTC value.
private DateTime ConvertTimestamp(object timestamp)
{
if (timestamp is DateTime dt)
{
return dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime();
}
return DateTime.UtcNow;
}
}
}