Files
UA-.NETStandard/docs/UAFX.md

14 KiB
Raw Permalink Blame History

UAFX information models

OPCFoundation.NetStandard.Opc.Ua.Fx supplies generated OPC 10000-81 model contracts for the FX Data, AutomationComponent and ConnectionManager namespaces. It uses the existing Device Integration model and the stack's source generator; it adds no native runtime or third-party package.

Register the models

Register dependencies before their consumers. The same registration order applies to encodeable factories, node-state factories and predefined nodes. The namespace table may already contain application namespaces.

using Opc.Ua;
using Opc.Ua.Di;
using Opc.Ua.Fx.Data;
using Opc.Ua.Fx.Ac;
using Opc.Ua.Fx.Cm;

messageContext.Factory.Builder
    .AddOpcUaDi()
    .AddOpcUaFxData()
    .AddOpcUaFxAc()
    .AddOpcUaFxCm()
    .Commit();

nodes.AddOpcUaDi(systemContext)
    .AddOpcUaFxData(systemContext)
    .AddOpcUaFxAc(systemContext)
    .AddOpcUaFxCm(systemContext);

The generated types include asynchronous EstablishConnections and CloseConnections Method handlers and client proxies, endpoint configuration and result structures, and PubSub communication-identifier reservation structures. PubSubReserveCommunicationIds2DataType and PubSubReserveCommunicationIdsResult2DataType preserve the standard RequestTransportSpecificInfo and TransportSpecificInfo fields.

Bind verification and reservation

Opc.Ua.PubSub.Server provides the experimental PubSubFxReservationMethods binding for the VerifyAssetCmd, VerifyFunctionalEntityCmd, and ReserveCommunicationIdsCmd commands of EstablishConnections. It can delegate the remaining EstablishConnections commands and bind CloseConnections to a host-owned IPubSubFxConnectionLifecycle implementation. The host supplies that capability because it owns the endpoint nodes, ConnectionManager authorization, and shared-resource ownership. The binding delegates asset verification to each addressed IAssetRevisionType.VerifyAsset method, delegates functional-entity verification to each related FunctionalEntityType.Verify method, and uses the application's existing Session-owned communication-ID transaction for reservations. The host owns the AutomationComponent instance and its NodeId; model loading does not create or bind one automatically. Functional entities are accepted only when their IsHostedBy Asset is in the bound component's in-memory parent hierarchy; an address-space-wide type match is not sufficient.

Register the binding with the same PubSub server application:

services.AddOpcUa()
    .AddPubSubServer(
        server => { },
        pubsub => { },
        options => options.ExposeFxModel = true)
    .WithFxConnectionLifecycle(hostConnectionLifecycle)
    .WithFxCommunicationIdReservations();

// In the host's node manager, after creating and initializing its local component:
serviceProvider.GetRequiredService<PubSubFxReservationMethods>()
    .Bind(automationComponent, ResolveActivePubSubUascListenerAsync);

Explicit construction is also supported:

var reservations = new PubSubFxReservationMethods(application, pubSubServerOptions);
reservations.Bind(automationComponent);

Both paths require the application to implement IPubSubCommunicationIdApplication. The server's PubSub node manager forwards Session closure to the reservation provider. A standalone host must perform that forwarding itself, as described in PubSub communication-ID reservations. Registration does not enable configuration methods, expose the model, replace an existing handler, or provide a concrete host lifecycle implementation. Registering a lifecycle capability causes Bind to install the generated CloseConnections Method handler only when that Method has no existing handler. When an EstablishConnections call selects endpoint lifecycle commands, the binding invokes the same capability after all selected verification and reservation commands succeed. The host processes the selected endpoint commands in Part 81 order and returns one ordered endpoint result per request. The capability receives the ordered verification and communication-ID reservation results so the host can use reserved identifiers during endpoint configuration.

Calls require an actual Session with the ConfigureAdmin role and ExposeConfigurationMethods = true. The binding accepts non-empty combinations of the nine defined EstablishConnections command bits. A command that creates or changes endpoints requires a registered IPubSubFxConnectionLifecycle; without one, those command bits fail with BadInvalidState before verification or reservation begins. Each selected command requires its input, and each unselected command requires an empty input. VerifyFunctionalEntityCmd requires at least one non-empty ExpectedVerificationVariables array. Endpoint definitions select the Node member unless CreateConnectionEndpointCmd supplies a Parameter member. ControlGroups, ConfigurationData, and CommunicationLinks are accepted only with their corresponding control, configuration-data, or communication configuration commands. SetCommunicationConfigurationCmd requires one communication-configuration input. A call is limited to 128 combined asset-verification, endpoint, and reservation requests.

Asset verification preserves input order, invokes each asset's VerifyAsset method with the supplied mode and expected-variable arrays, and returns the method status and outputs in AssetVerificationResults. An unknown asset reports BadNodeIdUnknown; a node that is not an IAssetRevisionType reports BadInvalidArgument. The binding processes the complete verification array before deciding whether later commands can run. A bad method status, or NotSet or Mismatch result always aborts subsequent commands. A Compatible result also aborts when ExpectedVerificationResult is Match. Unexecuted reservation results report BadNothingToDo and the EstablishConnections Method returns Uncertain.

Functional-entity verification preserves endpoint-configuration order. An empty ExpectedVerificationVariables array returns Good_NoData, NotSet, and no variable errors without calling Verify. Non-empty arrays are passed unchanged to the target FunctionalEntityType.Verify Method. Missing nodes report BadNodeIdUnknown and Mismatch; wrong-type or unrelated nodes report BadInvalidArgument and Mismatch. Method status and outputs are copied to VerificationStatus, VerificationResult, and VerificationVariablesErrors. A missing/invalid FunctionalEntity, method error, Mismatch, or NotSet with populated errors aborts later commands after all endpoint configurations have been processed. Unexecuted reservation results report BadNothingToDo, and the Method returns Uncertain.

Reservation input uses a non-empty array of PubSubReserveCommunicationIds2DataType values when the command is set. Results use PubSubReserveCommunicationIdsResult2DataType with the policy's typed default PublisherId and UInt16 identifier arrays.

When requested, transport-specific information is supported only for DataChannelProfiles.PubSubUasc. The asynchronous listener resolver must attest that an active local endpoint supports that capability, and return its opc.tcp or opc.wss URL as a String, limited to 4,096 UTF-8 bytes. It must return null when unavailable. A Reverse Connect-only host omits the resolver; the binding never fabricates a listening address. Other profiles can reserve identifiers under their configured policy when transport-specific information is not requested; requesting information for an unsupported profile fails before allocation.

Requests execute in input order. A request failure aborts further reservations: the failing item reports its status, later items report BadNothingToDo, and the Method reports Uncertain. Earlier successful reservations remain owned by the Session; the reservation command does not roll them back. Cancellation before processing returns BadRequestCancelledByClient. Cancellation during processing reports that status on the interrupted item and preserves earlier results under the same abort rules. Session closure prevents delayed work from recreating released reservations.

The optional CloseConnections handler passes the actual Method context, bound host AutomationComponent, ordered endpoint NodeIds, Remove flag, and cancellation token to one IPubSubFxConnectionLifecycle.CloseConnectionsAsync call. The host capability must authorize the actual ConnectionManager and perform the complete endpoint list as one transaction. It returns exactly one ordered StatusCode per requested endpoint; a wrong-length result is rejected as BadUnexpectedError. Host ServiceResultException and requested cancellation are returned as explicit Method failures. The binder never searches for, removes, or disables host nodes or PubSub resources itself. When no lifecycle capability is registered, CloseConnections remains unbound; an existing host handler is never replaced.

Project endpoint health

The experimental PubSubNodeManager.BindFxEndpointStatus projects real Part 14 DataSetReader and DataSetWriter health onto an initialized, host-owned PubSubConnectionEndpointState.Status. The existing injectable node manager provides this binding; no additional service or transport is created.

// The host has already resolved and associated these runtime components.
using IDisposable statusBinding = pubSubNodeManager.BindFxEndpointStatus(
    systemContext, connectionEndpoint, reader, writer);

For a Publisher endpoint supply only its writer; for a Subscriber supply only its reader; for PublisherSubscriber supply both. A missing required component reports Initial. Otherwise, an Error component takes precedence; a Disabled or Paused component reports Ready, followed by PreOperational, then Operational when every required component is Operational. These are the Part 81 Figures 31–33 matrices, not a transport connection or management-Call success indicator. Status reads use the current component states, and state transitions publish the variable's normal change masks. Reading Status does not refresh the reader's receive timeout, enable communication, or clear a fault. The Part 14 per-instance Enable Method likewise leaves a newly enabled reader PreOperational until a valid DataSetMessage arrives; repeating Enable does not clear a receive-timeout fault. Per-instance Enable and Disable execute asynchronously, propagate Call cancellation to the runtime, and report the resulting component state rather than assuming the requested state was reached.

The host owns membership resolution and the standard ToDataSetReader/Writer references or paths. It must dispose the binding before changing the endpoint mode, replacing its Status variable, or removing/replacing an associated runtime component; then bind the new association, or bind without the missing component to report Initial. The binding never searches by ambiguous names, replaces an existing Status read handler, or disposes the host's nodes or runtime components. Disposing the PubSub node manager detaches all remaining bindings. This status projection does not implement endpoint creation, configuration, enablement, shared ownership, persistence or CloseConnections.

NativeAOT execution

The native test host exercises both server bindings without reflection-based mocks. FxReservationHandlerAndDependencyInjectionAreAotSafeAsync resolves the reservation binder from DI and invokes both its generated asynchronous handler and MethodState.CallAsync with generated argument metadata. It checks all five input argument results, all four typed output arrays, derived reservation results, listening information, Session closure and access denial. This is in-process Method dispatch, not a network Call-service or authenticated live-Session test. FxEndpointStatusReadNotificationsAndDisposalAreAotSafeAsync uses a real DataSetReader to exercise generated endpoint Value reads, change notifications, fault projection and callback disposal. These checks complement the generated-model encoding tests; they do not establish complete Part 81 lifecycle or Part 84 profile conformance.

Source provenance

The three unmodified UAFX 1.00.04 NodeSets in src/Opc.Ua.Fx/Model come from UA-Nodeset at dd178cf655ed3d74f1d085ef04e0e226a1d3ed9f. Their Git blob identifiers are:

Input Git blob
opc.ua.fx.data.nodeset2.xml 12212843b7499c48059ca081963d349cd06d19a4
opc.ua.fx.ac.nodeset2.xml 224276cd06fb200f7421d5370698fd187b199893
opc.ua.fx.cm.nodeset2.xml 674ed1aa7ec9baad7119eba35ed1b87c10d2f0a8

Generated C# is produced during compilation and is not checked in.

Implementation boundary

These are model contracts, not a complete ConnectionManager or AutomationComponent lifecycle implementation. The binding above implements verification and reservation and delegates endpoint lifecycle commands to an optional host capability. Applications remain responsible for supplying that capability and for endpoint association, status binding, persistence, and cleanup according to Part 81 and their selected Part 84 profiles. Loading a model or completing a Method call is not evidence of valid PubSub DataSet reception or UAFX conformance.

Opc.Ua.PubSub.Server can load these declarations into its node manager when PubSubServerOptions.ExposeFxModel is enabled. This is opt-in and registers the generated encodeable types and the DI, FX Data, AutomationComponent, and ConnectionManager namespaces. Model exposure alone does not bind EstablishConnections or CloseConnections. The binding above implements asset and functional-entity verification plus communication-ID reservation. EstablishConnections endpoint commands and CloseConnections are delegated to the optional host capability. This package does not provide a concrete host lifecycle implementation for endpoint creation, control, configuration, or enablement.

The experimental Data Channel binding provides the ConfigureUafx transport-configuration bridge. It does not install Part 81 configuration or grant a ConnectionManager authority over authenticated data peers. A host lifecycle capability must still implement and authorize the endpoint operations delegated by EstablishConnections and CloseConnections.