v3(focas): adopt Modbus RawTagEntry exemplar (multi-device)
- Rename FocasEquipmentTagParser -> FocasTagDefinitionFactory; TryParse ->
FromTagConfig(tagConfig, rawPath, out def). Drop leading-{ heuristic + the
deviceHostAddress blob key; def.Name = rawPath. Add inverse ToTagConfig.
writable: absent -> read-only, explicit true honoured (note preserved).
- FocasDriverOptions.Tags -> RawTags (IReadOnlyList<RawTagEntry>).
- FocasDriver: _tagsByRawPath (Ordinal); byName-only resolver; build table from
RawTags via FromTagConfig; multi-device routing threads RawTagEntry.DeviceName
-> ResolveDeviceHost match against options.Devices -> def.DeviceHostAddress
(TODO v3 WaveC: live host from Device row DeviceConfig). Tolerant per-tag
skip+log (mapper/address/matrix miss -> BadNodeIdUnknown); unknown device
fails init fast.
- Factory: bind List<RawTagEntry> RawTags; retire FocasTagDto/ParseDataType.
- CLI FocasCommandBase.BuildOptions -> RawTags via ToTagConfig + DeviceName.
- Tests: FocasRawTags helper; migrate Tags= -> RawTags=; rename parser tests to
FromTagConfig; rewrite retired blob-ref tests (equipment-tag/capability-gate/
resolve-host) onto authored RawPath. 272 driver + 52 CLI green.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Wire;
|
||||
|
||||
@@ -68,18 +69,11 @@ public static class FocasDriverFactoryExtensions
|
||||
Series: ParseSeries(d.Series ?? dto.Series),
|
||||
PositionDecimalPlaces: d.PositionDecimalPlaces ?? 0))]
|
||||
: [],
|
||||
Tags = dto.Tags is { Count: > 0 }
|
||||
? [.. dto.Tags.Select(t => new FocasTagDefinition(
|
||||
Name: t.Name ?? throw new InvalidOperationException(
|
||||
$"FOCAS config for '{driverInstanceId}' has a tag missing Name"),
|
||||
DeviceHostAddress: t.DeviceHostAddress ?? throw new InvalidOperationException(
|
||||
$"FOCAS tag '{t.Name}' in '{driverInstanceId}' missing DeviceHostAddress"),
|
||||
Address: t.Address ?? throw new InvalidOperationException(
|
||||
$"FOCAS tag '{t.Name}' in '{driverInstanceId}' missing Address"),
|
||||
DataType: ParseDataType(t.DataType, t.Name!, driverInstanceId),
|
||||
Writable: t.Writable ?? true,
|
||||
WriteIdempotent: t.WriteIdempotent ?? false))]
|
||||
: [],
|
||||
// v3: the deploy artifact (Wave C) populates rawTags with the cluster's authored raw FOCAS
|
||||
// tags (RawPath + TagConfig blob + WriteIdempotent + DeviceName). The driver maps each
|
||||
// RawTagEntry's TagConfig blob through FocasTagDefinitionFactory at Initialize and routes it to
|
||||
// its device by DeviceName; the legacy pre-declared DTO tag path is retired.
|
||||
RawTags = dto.RawTags is { Count: > 0 } ? [.. dto.RawTags] : [],
|
||||
Probe = new FocasProbeOptions
|
||||
{
|
||||
Enabled = dto.Probe?.Enabled ?? true,
|
||||
@@ -190,18 +184,6 @@ public static class FocasDriverFactoryExtensions
|
||||
$"FOCAS Series '{raw}' is not one of {string.Join(", ", Enum.GetNames<FocasCncSeries>())}");
|
||||
}
|
||||
|
||||
private static FocasDataType ParseDataType(string? raw, string tagName, string driverInstanceId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
throw new InvalidOperationException(
|
||||
$"FOCAS tag '{tagName}' in '{driverInstanceId}' missing DataType");
|
||||
return Enum.TryParse<FocasDataType>(raw, ignoreCase: true, out var dt)
|
||||
? dt
|
||||
: throw new InvalidOperationException(
|
||||
$"FOCAS tag '{tagName}' has unknown DataType '{raw}'. " +
|
||||
$"Expected one of {string.Join(", ", Enum.GetNames<FocasDataType>())}");
|
||||
}
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
@@ -254,8 +236,11 @@ public static class FocasDriverFactoryExtensions
|
||||
/// <summary>Gets or sets the list of CNC devices to manage.</summary>
|
||||
public List<FocasDeviceDto>? Devices { get; init; }
|
||||
|
||||
/// <summary>Gets or sets the list of FOCAS tags to poll.</summary>
|
||||
public List<FocasTagDto>? Tags { get; init; }
|
||||
/// <summary>
|
||||
/// Gets or sets the authored raw tags (RawPath + TagConfig blob + WriteIdempotent + DeviceName)
|
||||
/// the driver serves. Bound verbatim into <see cref="FocasDriverOptions.RawTags"/>.
|
||||
/// </summary>
|
||||
public List<RawTagEntry>? RawTags { get; init; }
|
||||
|
||||
/// <summary>Gets or sets the probe configuration options.</summary>
|
||||
public FocasProbeDto? Probe { get; init; }
|
||||
@@ -290,27 +275,6 @@ public static class FocasDriverFactoryExtensions
|
||||
public int? PositionDecimalPlaces { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class FocasTagDto
|
||||
{
|
||||
/// <summary>Gets or sets the tag name.</summary>
|
||||
public string? Name { get; init; }
|
||||
|
||||
/// <summary>Gets or sets the hostname or IP address of the CNC device for this tag.</summary>
|
||||
public string? DeviceHostAddress { get; init; }
|
||||
|
||||
/// <summary>Gets or sets the FOCAS address or path for this tag.</summary>
|
||||
public string? Address { get; init; }
|
||||
|
||||
/// <summary>Gets or sets the data type for this tag.</summary>
|
||||
public string? DataType { get; init; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether this tag is writable.</summary>
|
||||
public bool? Writable { get; init; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether writes to this tag are idempotent.</summary>
|
||||
public bool? WriteIdempotent { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class FocasProbeDto
|
||||
{
|
||||
/// <summary>Gets or sets a value indicating whether probing is enabled.</summary>
|
||||
|
||||
Reference in New Issue
Block a user