feat(lmxproxy): add MxAccess status detail mapping for richer error messages

- MxStatusMapper: maps all 40+ MxStatusDetail codes, MxStatusCategory,
  and MxStatusSource to human-readable names and client messages
- OnDataChange: checks MXSTATUS_PROXY.success and overrides quality with
  specific OPC UA code when MxAccess reports a failure (e.g., CommFailure,
  ConfigError, WaitingForInitialData)
- OnWriteComplete: uses MxStatusMapper.FormatStatus for structured logging
- Write errors: catches COMException separately with HRESULT in message
- Read errors: distinguishes COM, timeout, and generic failures in logging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-03-22 05:10:50 -04:00
parent 73b2b2f6d7
commit 5a9574fb95
3 changed files with 217 additions and 19 deletions

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ArchestrA.MxAccess;
using Serilog;
using ZB.MOM.WW.LmxProxy.Host.Domain;
@@ -31,6 +29,17 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess
{
var quality = MapQuality(pwItemQuality);
var timestamp = ConvertTimestamp(pftItemTimeStamp);
// Check MXSTATUS_PROXY — if success is false, override quality
// with a more specific code derived from the MxAccess status fields
if (ItemStatus != null && ItemStatus.Length > 0 && ItemStatus[0].success == 0)
{
var status = ItemStatus[0];
quality = MxStatusMapper.CategoryToQuality((int)status.category, status.detail);
Log.Debug("OnDataChange status failure for handle {Handle}: {Status}",
phItemHandle, MxStatusMapper.FormatStatus(status.detail, (int)status.category, (int)status.detectedBy));
}
var vtq = new Vtq(pvItemValue, timestamp, quality);
// Resolve address from handle map
@@ -84,9 +93,8 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess
var status = ItemStatus[0];
if (status.success == 0)
{
string errorMsg = GetWriteErrorMessage(status.detail);
Log.Warning("OnWriteComplete callback: write failed for handle {Handle}: {Error} (Category={Category}, Detail={Detail})",
phItemHandle, errorMsg, status.category, status.detail);
Log.Warning("OnWriteComplete callback: write failed for handle {Handle}: {Status}",
phItemHandle, MxStatusMapper.FormatStatus(status.detail, (int)status.category, (int)status.detectedBy));
}
else
{
@@ -104,20 +112,6 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess
}
}
/// <summary>
/// Gets a human-readable error message for a write error code.
/// </summary>
private static string GetWriteErrorMessage(int errorCode)
{
switch (errorCode)
{
case 1008: return "User lacks proper security for write operation";
case 1012: return "Secured write required";
case 1013: return "Verified write required";
default: return string.Format("Unknown error code: {0}", errorCode);
}
}
/// <summary>
/// Converts a timestamp object to DateTime in UTC.
/// </summary>