feat(drivers): shared EquipmentTagRefResolver (by-name + parse-on-miss + cache)

This commit is contained in:
Joseph Doherty
2026-06-13 11:07:23 -04:00
parent 6b041c6daa
commit eaa335d779
2 changed files with 100 additions and 0 deletions
@@ -0,0 +1,46 @@
using System.Collections.Concurrent;
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
/// <summary>
/// Resolves a driver subscription/read/write <c>fullReference</c> to a driver tag-definition,
/// bridging the two authoring models: a legacy authored tag-table entry (looked up by name)
/// OR an equipment tag whose reference is its raw <c>TagConfig</c> JSON (parsed on first use
/// and cached). Negative results are cached too, so a genuinely-unknown reference is parsed once.
/// </summary>
/// <typeparam name="TDef">The driver's internal tag-definition type.</typeparam>
public sealed class EquipmentTagRefResolver<TDef> where TDef : class
{
private readonly Func<string, TDef?> _byName;
private readonly Func<string, TDef?> _parseRef;
private readonly ConcurrentDictionary<string, TDef?> _cache = new(StringComparer.Ordinal);
/// <param name="byName">Authored tag-table lookup (returns null on miss).</param>
/// <param name="parseRef">Parses an equipment-tag reference (TagConfig JSON) into a transient def, or null.</param>
public EquipmentTagRefResolver(Func<string, TDef?> byName, Func<string, TDef?> parseRef)
{
ArgumentNullException.ThrowIfNull(byName);
ArgumentNullException.ThrowIfNull(parseRef);
_byName = byName;
_parseRef = parseRef;
}
/// <summary>True when <paramref name="fullReference"/> resolves to a def (authored or equipment).</summary>
/// <param name="fullReference">The wire reference handed to the driver.</param>
/// <param name="def">The resolved tag-definition when this returns true.</param>
/// <returns><see langword="true"/> when a definition was found.</returns>
public bool TryResolve(string fullReference, out TDef def)
{
var authored = _byName(fullReference);
if (authored is not null) { def = authored; return true; }
var resolved = _cache.GetOrAdd(fullReference, _parseRef);
if (resolved is not null) { def = resolved; return true; }
def = null!;
return false;
}
/// <summary>Drops the transient-parse cache (call on driver reinitialise so a config change re-parses).</summary>
public void Clear() => _cache.Clear();
}