Renames all 11 projects (5 src + 6 tests), the .slnx solution file, all source-file namespaces, all axaml namespace references, and all v1 documentation references in CLAUDE.md and docs/*.md (excluding docs/v2/ which is already in OtOpcUa form). Also updates the TopShelf service registration name from "LmxOpcUa" to "OtOpcUa" per Phase 0 Task 0.6.
Preserves runtime identifiers per Phase 0 Out-of-Scope rules to avoid breaking v1/v2 client trust during coexistence: OPC UA `ApplicationUri` defaults (`urn:{GalaxyName}:LmxOpcUa`), server `EndpointPath` (`/LmxOpcUa`), `ServerName` default (feeds cert subject CN), `MxAccessConfiguration.ClientName` default (defensive — stays "LmxOpcUa" for MxAccess audit-trail consistency), client OPC UA identifiers (`ApplicationName = "LmxOpcUaClient"`, `ApplicationUri = "urn:localhost:LmxOpcUaClient"`, cert directory `%LocalAppData%\LmxOpcUaClient\pki\`), and the `LmxOpcUaServer` class name (class rename out of Phase 0 scope per Task 0.5 sed pattern; happens in Phase 1 alongside `LmxNodeManager → GenericDriverNodeManager` Core extraction). 23 LmxOpcUa references retained, all enumerated and justified in `docs/v2/implementation/exit-gate-phase-0.md`.
Build clean: 0 errors, 30 warnings (lower than baseline 167). Tests at strict improvement over baseline: 821 passing / 1 failing vs baseline 820 / 2 (one flaky pre-existing failure passed this run; the other still fails — both pre-existing and unrelated to the rename). `Client.UI.Tests`, `Historian.Aveva.Tests`, `Client.Shared.Tests`, `IntegrationTests` all match baseline exactly. Exit gate compliance results recorded in `docs/v2/implementation/exit-gate-phase-0.md` with all 7 checks PASS or DEFERRED-to-PR-review (#7 service install verification needs Windows service permissions on the reviewer's box).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
92 lines
3.4 KiB
C#
92 lines
3.4 KiB
C#
using System;
|
|
using Opc.Ua;
|
|
using ZB.MOM.WW.OtOpcUa.Host.Domain;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Host.OpcUa
|
|
{
|
|
/// <summary>
|
|
/// Converts between domain Vtq and OPC UA DataValue. Handles all data_type_mapping.md types. (OPC-005, OPC-007)
|
|
/// </summary>
|
|
public static class DataValueConverter
|
|
{
|
|
/// <summary>
|
|
/// Converts a bridge VTQ snapshot into an OPC UA data value.
|
|
/// </summary>
|
|
/// <param name="vtq">The VTQ snapshot to convert.</param>
|
|
/// <returns>An OPC UA data value suitable for reads and subscriptions.</returns>
|
|
public static DataValue FromVtq(Vtq vtq)
|
|
{
|
|
var statusCode = new StatusCode(QualityMapper.MapToOpcUaStatusCode(vtq.Quality));
|
|
|
|
var dataValue = new DataValue
|
|
{
|
|
Value = ConvertToOpcUaValue(vtq.Value),
|
|
StatusCode = statusCode,
|
|
SourceTimestamp = vtq.Timestamp.Kind == DateTimeKind.Utc
|
|
? vtq.Timestamp
|
|
: vtq.Timestamp.ToUniversalTime(),
|
|
ServerTimestamp = DateTime.UtcNow
|
|
};
|
|
|
|
return dataValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts an OPC UA data value back into a bridge VTQ snapshot.
|
|
/// </summary>
|
|
/// <param name="dataValue">The OPC UA data value to convert.</param>
|
|
/// <returns>A VTQ snapshot containing the converted value, timestamp, and derived quality.</returns>
|
|
public static Vtq ToVtq(DataValue dataValue)
|
|
{
|
|
var quality = MapStatusCodeToQuality(dataValue.StatusCode);
|
|
var timestamp = dataValue.SourceTimestamp != DateTime.MinValue
|
|
? dataValue.SourceTimestamp
|
|
: DateTime.UtcNow;
|
|
|
|
return new Vtq(dataValue.Value, timestamp, quality);
|
|
}
|
|
|
|
private static object? ConvertToOpcUaValue(object? value)
|
|
{
|
|
if (value == null) return null;
|
|
|
|
return value switch
|
|
{
|
|
bool _ => value,
|
|
int _ => value,
|
|
float _ => value,
|
|
double _ => value,
|
|
string _ => value,
|
|
DateTime dt => dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime(),
|
|
TimeSpan ts => ts.TotalSeconds, // ElapsedTime → Double seconds
|
|
short s => (int)s,
|
|
long l => l,
|
|
byte b => (int)b,
|
|
bool[] _ => value,
|
|
int[] _ => value,
|
|
float[] _ => value,
|
|
double[] _ => value,
|
|
string[] _ => value,
|
|
DateTime[] _ => value,
|
|
_ => value.ToString()
|
|
};
|
|
}
|
|
|
|
private static Quality MapStatusCodeToQuality(StatusCode statusCode)
|
|
{
|
|
var code = statusCode.Code;
|
|
if (StatusCode.IsGood(statusCode)) return Quality.Good;
|
|
if (StatusCode.IsUncertain(statusCode)) return Quality.Uncertain;
|
|
|
|
return code switch
|
|
{
|
|
StatusCodes.BadNotConnected => Quality.BadNotConnected,
|
|
StatusCodes.BadCommunicationError => Quality.BadCommFailure,
|
|
StatusCodes.BadConfigurationError => Quality.BadConfigError,
|
|
StatusCodes.BadOutOfService => Quality.BadOutOfService,
|
|
StatusCodes.BadWaitingForInitialData => Quality.BadWaitingForInitialData,
|
|
_ => Quality.Bad
|
|
};
|
|
}
|
|
}
|
|
} |