namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
///
/// Streaming builder API a driver uses to register OPC UA nodes during discovery.
/// Core owns the tree; driver streams AddFolder / AddVariable calls
/// as it discovers nodes — no buffering of the whole tree.
///
///
/// Per docs/v2/plan.md decision #52 — drivers register nodes via this builder
/// rather than returning a tree object. Supports incremental / large address spaces
/// without forcing the driver to buffer the whole tree.
///
public interface IAddressSpaceBuilder
{
///
/// Add a folder node. Returns a child builder scoped to inside this folder, so subsequent
/// calls on the child place nodes under it.
///
/// OPC UA browse name (the segment of the path under the parent).
/// Human-readable display name. May equal .
IAddressSpaceBuilder Folder(string browseName, string displayName);
///
/// Add a variable node corresponding to a tag. Driver-side full reference + data-type
/// metadata come from the DTO.
///
/// OPC UA browse name (the segment of the path under the parent folder).
/// Human-readable display name. May equal .
/// Driver-side metadata for the variable.
IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo);
///
/// Add a property to the current node (folder or variable). Properties are static metadata
/// read once at build time (e.g. OPC 40010 Identification fields per the schemas-repo
/// _base equipment-class template).
///
void AddProperty(string browseName, DriverDataType dataType, object? value);
///
/// Register a type-definition node (ObjectType / VariableType / DataType / ReferenceType)
/// mirrored from an upstream OPC UA server. Optional surface — drivers that don't mirror
/// types simply never call it; address-space builders that don't materialise upstream
/// types can leave the default no-op in place. Default implementation drops the call so
/// adding this method doesn't break existing
/// implementations.
///
/// Metadata describing the type-definition node to mirror.
///
///
/// The OPC UA Client driver is the primary caller — it walks i=86
/// (TypesFolder) during DiscoverAsync when
/// OpcUaClientDriverOptions.MirrorTypeDefinitions is set so downstream clients
/// see the upstream type system instead of rendering structured-type values as opaque
/// strings.
///
///
/// The default no-op is intentional — most builders (Galaxy, Modbus, FOCAS, S7,
/// TwinCAT, AB-CIP) don't have a meaningful type folder to project into and would
/// otherwise need empty-stub overrides.
///
///
void RegisterTypeNode(MirroredTypeNodeInfo info) { /* default: no-op */ }
///
/// Register a method node mirrored from an upstream OPC UA server. The method is
/// registered as a child of the current builder scope (i.e. the folder representing
/// the upstream Object that owns the method). Optional surface — drivers that don't
/// mirror methods simply never call it; address-space builders that don't materialise
/// method nodes can leave the default no-op in place. Default implementation drops
/// the call so adding this method doesn't break existing
/// implementations.
///
/// Metadata describing the method node, including input/output argument schemas.
///
///
/// The OPC UA Client driver is the primary caller — it picks up
/// NodeClass.Method nodes during the HierarchicalReferences browse
/// pass, then walks each method's HasProperty references to harvest the
/// InputArguments / OutputArguments property values.
///
///
/// The OPC UA server-side DriverNodeManager overrides this to materialize
/// a real MethodNode in the local address space and wire its
/// OnCallMethod handler to the driver's
/// . Other builders (Galaxy, Modbus,
/// FOCAS, S7, TwinCAT, AB-CIP, AB-Legacy) ignore the projection because their
/// backends don't expose method nodes.
///
///
void RegisterMethodNode(MirroredMethodNodeInfo info) { /* default: no-op */ }
}
///
/// Metadata describing a single method node mirrored from an upstream OPC UA server.
/// Built by the OPC UA Client driver during the discovery browse pass and consumed by
/// .
///
/// OPC UA BrowseName segment from the upstream BrowseName.
/// Human-readable display name; falls back to .
///
/// Stringified NodeId of the parent Object that owns this method — the ObjectId
/// argument the dispatcher passes back to .
///
///
/// Stringified NodeId of the method node itself — the MethodId argument.
///
///
/// Declaration of the method's input arguments, in order. null or empty when the
/// method takes no inputs (or the upstream property couldn't be read).
///
///
/// Declaration of the method's output arguments, in order. null or empty when the
/// method returns no outputs (or the upstream property couldn't be read).
///
public sealed record MirroredMethodNodeInfo(
string BrowseName,
string DisplayName,
string ObjectNodeId,
string MethodNodeId,
IReadOnlyList? InputArguments,
IReadOnlyList? OutputArguments);
///
/// One row of an OPC UA Argument array — name + data type + array hint. Mirrors the
/// Opc.Ua.Argument structure but without the SDK-only types so this DTO can live
/// in Core.Abstractions.
///
/// Argument name from the upstream Argument structure.
///
/// Mapped local . Unknown / structured upstream types fall
/// through to — same convention as variable mirroring.
///
///
/// OPC UA ValueRank: -1 = scalar, 0 = OneOrMoreDimensions, 1+ = array
/// dimensions. Driven directly from the upstream Argument's ValueRank.
///
///
/// Human-readable description from the upstream Argument structure; null when the
/// upstream doesn't carry one.
///
public sealed record MethodArgumentInfo(
string Name,
DriverDataType DriverDataType,
int ValueRank,
string? Description);
///
/// Categorises a mirrored type-definition node so the receiving builder can route it into
/// the right OPC UA standard subtree (ObjectTypesFolder, VariableTypesFolder,
/// DataTypesFolder, ReferenceTypesFolder) when projecting upstream types into
/// the local address space.
///
public enum MirroredTypeKind
{
ObjectType,
VariableType,
DataType,
ReferenceType,
}
///
/// Metadata describing a single type-definition node mirrored from an upstream OPC UA
/// server. Built by the OPC UA Client driver during type-mirror pass and consumed by
/// .
///
/// Type category — drives which standard sub-folder the node lives under.
///
/// Stringified upstream NodeId (e.g. "ns=2;i=1234") — preserves the original identity
/// so a builder that wants to project the type with a stable cross-namespace reference can do
/// so. The driver applies any configured namespace remap before stamping this field.
///
/// OPC UA BrowseName segment from the upstream BrowseName.
/// Human-readable display name; falls back to .
///
/// Stringified upstream NodeId of the super-type (parent type), or null when the node
/// sits directly under the root (e.g. BaseObjectType, BaseVariableType). Lets
/// the builder reconstruct the inheritance chain.
///
///
/// true when the upstream node has the IsAbstract flag set (Object / Variable /
/// ReferenceType). DataTypes also expose this — the driver passes it through verbatim.
///
public sealed record MirroredTypeNodeInfo(
MirroredTypeKind Kind,
string UpstreamNodeId,
string BrowseName,
string DisplayName,
string? SuperTypeNodeId,
bool IsAbstract);
/// Opaque handle for a registered variable. Used by Core for subscription routing.
public interface IVariableHandle
{
/// Driver-side full reference for read/write addressing.
string FullReference { get; }
///
/// Annotate this variable with an OPC UA AlarmConditionState. Drivers with
/// = true call this during discovery so the
/// concrete address-space builder can materialize a sibling condition node. The returned
/// sink receives lifecycle transitions raised through
/// — the generic node manager wires the subscription; the concrete builder decides how
/// to surface the state (e.g. OPC UA AlarmConditionState.Activate,
/// Acknowledge, Deactivate).
///
IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info);
}
///
/// Metadata used to materialize an OPC UA AlarmConditionState sibling for a variable.
/// Populated by the driver's discovery step; concrete builders decide how to surface it.
///
/// Human-readable alarm name used for the SourceName event field.
/// Severity at address-space build time; updates arrive via .
/// Initial description; updates arrive via .
public sealed record AlarmConditionInfo(
string SourceName,
AlarmSeverity InitialSeverity,
string? InitialDescription);
///
/// Sink a concrete address-space builder returns from .
/// The generic node manager routes per-alarm payloads here —
/// the sink translates the transition into an OPC UA condition state change or whatever the
/// concrete builder's backing address space supports.
///
public interface IAlarmConditionSink
{
/// Push an alarm transition (Active / Acknowledged / Inactive) for this condition.
void OnTransition(AlarmEventArgs args);
}