using ZB.MOM.WW.OtOpcUa.Commons.Types;
namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
///
/// v3 dual-namespace NodeId + namespace-URI authority. Two OPC UA namespaces expose the same
/// underlying values under two identity schemes:
///
/// - Raw (): the device-oriented tree
/// Folder→Driver→Device→TagGroup→Tag. A node's s= identifier IS its
/// RawPath — folders/drivers/devices/groups included (each keys
/// on its own RawPath prefix).
/// - Uns (): the equipment-oriented tree
/// Area→Line→Equipment→signal. A node's s= identifier is the slash-joined
/// Area/Line/Equipment[/EffectiveName].
///
/// These replace the single https://zb.com/otopcua/ns namespace and the retired
/// EquipmentNodeIds ({equipmentId}/{folderPath}/{name}) scheme. Realm travels alongside
/// the identifier as an at every sink seam — the namespace is
/// never parsed back out of the id string. Both schemes share
/// and its ordinal, case-sensitive segment charset so identity is enforced in one place.
///
public static class V3NodeIds
{
/// The Raw namespace URI (device-oriented subtree).
public const string RawNamespaceUri = "https://zb.com/otopcua/raw";
/// The UNS namespace URI (equipment-oriented subtree).
public const string UnsNamespaceUri = "https://zb.com/otopcua/uns";
/// The namespace URI for a realm.
/// The address-space realm.
/// The realm's namespace URI.
/// Unknown realm.
public static string NamespaceUri(AddressSpaceRealm realm) => realm switch
{
AddressSpaceRealm.Raw => RawNamespaceUri,
AddressSpaceRealm.Uns => UnsNamespaceUri,
_ => throw new ArgumentOutOfRangeException(nameof(realm), realm, "Unknown address-space realm."),
};
// ----- Raw realm -----
///
/// The Raw-namespace s= identifier for a raw node — identical to its
/// RawPath (folders, drivers, devices, groups, and tags all key
/// on their own RawPath). A pass-through seam so call sites read intent rather than a bare
/// string; the argument is expected to already be a RawPaths-built path.
///
/// The (already-built) RawPath.
/// The Raw-namespace s= identifier (== ).
public static string Raw(string rawPath)
{
ArgumentException.ThrowIfNullOrEmpty(rawPath);
return rawPath;
}
// ----- Uns realm -----
///
/// The UNS-namespace s= identifier for a folder or variable, slash-joined from
/// ordered segments (Area, then Line, then Equipment, then an optional EffectiveName).
/// Every segment is validated via (no embedded
/// separator, no leading/trailing whitespace) so a UNS id round-trips like a RawPath.
///
/// The ordered, non-empty segments (Area → leaf).
/// The slash-joined UNS s= identifier.
/// A segment is invalid, or the sequence is empty.
public static string Uns(params string[] segments) => UnsFromSegments(segments);
/// Build a UNS s= identifier from ordered segments. See .
/// The ordered, non-empty segments (Area → leaf).
/// The slash-joined UNS s= identifier.
public static string Uns(IEnumerable segments) => UnsFromSegments(segments);
///
/// Append a child segment (a Line under an Area, an Equipment under a Line, an
/// EffectiveName under an Equipment) to an already-built UNS path.
///
/// The parent UNS path (assumed already valid).
/// The child segment to append.
/// The combined UNS s= identifier.
/// The child segment is invalid, or the parent is blank.
public static string UnsChild(string parentUnsPath, string childSegment)
{
ArgumentException.ThrowIfNullOrEmpty(parentUnsPath);
var error = RawPaths.ValidateSegment(childSegment);
if (error is not null)
throw new ArgumentException($"Invalid UNS NodeId segment ('{childSegment}'): {error}", nameof(childSegment));
return parentUnsPath + RawPaths.Separator + childSegment;
}
private static string UnsFromSegments(IEnumerable segments)
{
ArgumentNullException.ThrowIfNull(segments);
var list = segments as IReadOnlyList ?? segments.ToList();
if (list.Count == 0)
throw new ArgumentException("A UNS NodeId must have at least one segment.", nameof(segments));
for (var i = 0; i < list.Count; i++)
{
var error = RawPaths.ValidateSegment(list[i]);
if (error is not null)
throw new ArgumentException($"Invalid UNS NodeId segment at index {i} ('{list[i]}'): {error}", nameof(segments));
}
return string.Join(RawPaths.Separator, list);
}
}