Commit Graph
8 Commits
Author SHA1 Message Date
Marc Schier 736b92ac7e Fix #3768: stop emitting unimplemented Optional children on singleton instances (#3828)
# Description

Singleton instances declared in `StandardTypes.xml` and
`Opc.Ua.NodeSet2.xml` (`Server`, `Server.ServerConfiguration`,
`HistoryServerCapabilities`, `OPCUANamespaceMetadata`, the well-known
role objects, the Part 17 `Aliases` category, etc.) were emitting
Optional Variable/Method children that the SDK does not implement — for
example `ServerConfiguration.ApplicationUri`, `ProductUri`,
`ApplicationType`, `HasSecureElement`, `CancelChanges`,
`ResetToServerDefaults`, `Server.UrisVersion`,
`Server.EstimatedReturnTime`, `Server.LocalTime`,
`Server.ServerCapabilities.ConformanceUnits` and friends.

The cause was the source generator promoting every type-definition
child's modelling rule into the singleton-instance factory whenever
`UseTypeDefinitionModellingRules` was enabled (#3799), which made every
Optional child unconditionally appear in the singleton's `CreateXxx`
body.

This PR fixes the regression at the generator level (without touching
`StandardTypes.xml`) and lets the SDK opt back in to just the Optional
children it actually implements.


## New: chainable `Add{Child}` API with configure-callback overloads

Per review feedback ("the existing predefined node is used. A user adds
optional children using `.AddXY(NodeId? nodeId = default)`. The Add
calls should be possible to chain. … Instead of AddAndGet, emit an Add
overload with a callback to configure the created node state further,
add another Add to take a condition predicate that decides whether the
Create is executed or not"), the source generator now emits **five**
chainable `Add{Child}` overloads per Optional child of every typed state
class. All return the **owner** (`this`) so multi-child wiring collapses
into a single fluent chain:

```csharp
// (1) base — idempotent ensure-child
public TOwner Add{Child}(ISystemContext context, NodeId? nodeId = default);

// (2) Action — always run, mutate the just-created child in-line
public TOwner Add{Child}(ISystemContext context, Action<TChild> configure, NodeId? nodeId = default);

// (3) Action — conditional; when false: no Add, no callback
public TOwner Add{Child}(ISystemContext context, bool condition, Action<TChild> configure, NodeId? nodeId = default);

// (4) Func — always run, transform/replace the typed slot
public TOwner Add{Child}(ISystemContext context, Func<TChild, TChild> configure, NodeId? nodeId = default);

// (5) Func — conditional
public TOwner Add{Child}(ISystemContext context, bool condition, Func<TChild, TChild> configure, NodeId? nodeId = default);
```

The previous `AddAndGet{Child}(context, NodeId?)` helper is **removed**;
every call site is migrated to one of the new overloads. The Action
overloads cover the common "set Value / wire OnCallAsync" case in-line;
the Func overloads allow callers to replace the typed slot with a
wrapper or subclass instance. The boolean overloads short-circuit when
the condition is `false`.

When `nodeId` is `null`: if `context.NodeIdFactory` is non-null the
helper mints a fresh NodeId via `context.NodeIdFactory.New(context,
child)`; otherwise it leaves the type-level NodeId assigned by the
generated factory unchanged (the caller can patch later).

`NodeState.Create(context, source)` stays authoritative (the loaded
NodeSet XML / passive source is the source of truth for what children
exist). The new chainable `Add{Child}` overloads are the
runtime-extension API for SDK code and downstream users that want to add
Optional children programmatically after load.

A new `Tokens.OwnerClassName` carries the owning typed state class name
through the per-child template loop so `Add{Child}` can return `this`
typed as the owner; the template engine's outer-scope lookup cascades
the value from `AddNodeStateClassTypeReplacements` /
`AddNodeStateClassMethodTypeReplacements` into the per-child template
scope.

### SDK migration in `Libraries/Opc.Ua.Server`

- `DiagnosticsNodeManager` add-back helpers collapse into single fluent
chains; the `SetSubscriptionDurable` conditional folds into the Server
chain via the bool-condition overload
(`AddSetSubscriptionDurable(context, m_durableSubscriptionsEnabled, _ =>
{ }, MethodIds.Server_SetSubscriptionDurable)`); the `OperationLimits`
add-back inlines via
`.AddOperationLimits(...).OperationLimits!.AddMax...()`.
- `ConfigurationNodeManager` chains
`AddGetCertificates(context).AddCreateSelfSignedCertificate(context)`
before `Create(context, passiveNode)`.
- `HistoricalDataConfigurationInstaller.PopulateProperties` is now a
single fluent chain using the conditional Action overload —
`.AddMaxTimeInterval(context, capabilities.MaxTimeInterval > 0, c =>
c.Value = capabilities.MaxTimeInterval)` replaces the `if (cond) {
config.AddAndGetMaxTimeInterval(context).Value = ...; }` ceremony.
- `KeyCredentialPushSubject` folds `OnCall = null; OnCallAsync = ...`
into Action callbacks on the chain: `state.AddUpdateCredential(context,
c => { c.OnCall = null; c.OnCallAsync = OnUpdateCredentialAsync;
}).AddDeleteCredential(...)`.
- `WotConnectivityNodeManager.WireManagementMethods` chains
`.AddDiscoverAssets(ctx, c => c.OnCallAsync =
...).AddCreateAssetForEndpoint(...).AddConnectionTest(...).AddSupportedWoTBindings(...)`.
- `AssetRegistry` and `AlarmConditionTypeHolder` updated from `state.X
??= state.AddX(context)` to plain `state.AddX(context)` + null-forgiving
access on the typed slot (idempotent `Add{Child}` makes the `??=`
ceremony obsolete).

## Generator fix

`Tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateGenerator.cs`:

`NodeToGenerate` gains a structural `IsUnderSingletonInstance` flag, set
`true` on the singleton-instance root in `CollectNodesToGenerate` and
propagated to every descendant in `GetChildren`. Mirrors the existing
`RootIsTypeDefinition` propagation pattern; the gate becomes an O(1)
lookup.

```csharp
bool isSingletonInstanceContext =
    m_context.Options.UseTypeDefinitionModellingRules &&
    node.IsUnderSingletonInstance &&
    node.Parent != null &&
    instance is VariableDesign or MethodDesign;

ModellingRule effectiveRule = isSingletonInstanceContext
    ? GetEffectiveModellingRule(node, instance)
    : instance.ModellingRule;
```

The `topLevelInstanceException` is preserved for **TYPE-DEF** roots —
its `UseTypeDefinitionModellingRules` gate only fires when the parent is
an `InstanceDesign` singleton. Without this, the type-def factories
(e.g. `CreateNamespaceMetadataType`) stop emitting their Optional
children which breaks deep code paths that rely on the type-def factory
shape (e.g.
`ConfigurationNodeManager.CreateNamespaceMetadataStateAsync`).

### Why the gate is transitive but narrowed to Variables/Methods

The gate applies **transitively** to every Variable/Method descendant of
a singleton-instance subtree. Deep Optional Variable/Method descendants
such as `Server.ServerCapabilities.MaxArrayLength`,
`Server.ServerCapabilities.OperationLimits.MaxNodesPerRead`,
`Server.ServerRedundancy.RedundantServerArray`, `Server.UrisVersion`,
`Server.EstimatedReturnTime`, `Server.LocalTime`,
`PublishSubscribe.AddConnection` / `RemoveConnection`, the Optional
methods on every well-known role, the OPCUANamespaceMetadata Default*
properties, and `Aliases.LastChange` are now all suppressed by the
generator.

Optional **Object** instances under singletons are intentionally exempt:
they are still emitted by the singleton-specific factory with their
correct instance-level NodeIds. Their subtrees use well-known descendant
NodeIds (e.g.
`ServerConfiguration.CertificateGroups.DefaultHttpsGroup.TrustList.Open
= NodeId 14095`) that the SDK and `ConfigurationNodeManager` bind
against directly. Optional Variable/Method children of those Objects are
still gated transitively (e.g. `OperationLimits.MaxNodesPerRead` is
suppressed even though `OperationLimits` itself stays emitted).

### EURange regression analysis

The earlier attempt at this fix regressed `AnalogItemType.EURange`
(which is `Mandatory` on `AnalogItemType` per the OPC UA spec — a
type-level promotion of `BaseAnalogType.EURange`).
`IsUnderSingletonInstance` is `false` on type-def and synthetic
type-instance roots, so the gate cannot fire on either type-template
tree. Combined with the topLevelInstanceException preserved for type-def
roots, every type-def factory continues to emit its full child set.

## SDK programmatic Add* (Opc.Ua.Server)

`DiagnosticsNodeManager.AddSdkImplementedOptionalChildren` now
dispatches per loaded predefined node type. Each lazy-add call patches
the resulting NodeId (and Method `InputArguments` / `OutputArguments`
NodeIds where applicable) to the well-known singleton-instance value via
`ObjectIds.*` / `MethodIds.*` / `VariableIds.*`. The generator-emitted
`Add{Child}` extension uses the type-level factory which assigns the
type NodeId; the SDK overrides each NodeId after construction.

- **`Server` (ServerObjectState)**: `GetMonitoredItems`, `ResendData`,
`SetSubscriptionDurable` (when enabled), `Namespaces`, `UrisVersion`,
`EstimatedReturnTime`, `LocalTime`.
- **`Server.ServerCapabilities`** (14 Properties): `MaxArrayLength`,
`MaxStringLength`, `MaxByteStringLength`, `MaxSessions`,
`MaxSubscriptions`, `MaxMonitoredItems`, `MaxSubscriptionsPerSession`,
`MaxMonitoredItemsPerSubscription`, `MaxSelectClauseParameters`,
`MaxWhereClauseParameters`, `MaxMonitoredItemsQueueSize`,
`ConformanceUnits`, `RoleSet`, `OperationLimits`. Required to keep
`ServerInternalData.CreateServerObjectAsync`'s non-null assertions
working and to keep `Session.FetchOperationLimitsAsync` round-trip
values matching master.
- **`Server.ServerCapabilities.OperationLimits`** (12 Properties): every
`MaxNodesPerXxx` and `MaxMonitoredItemsPerCall`.
- **`Server.ServerRedundancy.RedundantServerArray`**: re-added for
`DefaultServerRedundancyHandler` (client-side) compatibility.
- **`HistoryServerCapabilities.ServerTimestampSupported`**.
- **6 modifiable well-known roles** (Observer, Operator, Engineer,
Supervisor, ConfigureAdmin, SecurityAdmin) × 11 RoleType children
(`ApplicationsExclude`, `Applications`, `EndpointsExclude`, `Endpoints`,
`CustomConfiguration`, `AddIdentity`, `RemoveIdentity`,
`AddApplication`, `RemoveApplication`, `AddEndpoint`, `RemoveEndpoint`).
The three immutable roles (Anonymous, AuthenticatedUser,
TrustedApplication) have no well-known instance NodeIds for these
methods in the spec and are left untouched.
- **`OPCUANamespaceMetadata`** (i=15957, standard Part 6 namespace
metadata loaded from `Opc.Ua.NodeSet2.xml`): `DefaultRolePermissions`,
`DefaultUserRolePermissions`, `DefaultAccessRestrictions`.
- **Part 17 `Aliases`** (i=23470): `LastChange`. Required by
`AliasNameResolverRefreshMode.AutoOnLastChangeMonitoredItem` in
`Opc.Ua.Client` which subscribes to LastChange to invalidate its cache.

`ConfigurationNodeManager` continues to assign
`CreateSelfSignedCertificate = new
CreateSelfSignedCertificateMethodState(activeNode)` before `Create(...)`
(it is no longer emitted by the singleton factory).

## Related Issues

- Fixes #3768
- Follow-up to #3799

## Verification

- **Generated code inspection**: `CreateServerConfiguration` does
**not** emit `ApplicationUri` / `ProductUri` / `ApplicationType` /
`HasSecureElement` / `CancelChanges` / `ResetToServerDefaults` at
`forInstance=true`. `CreateServer_ServerCapabilities` no longer emits
the 14 SDK-handled Optional Properties at `forInstance=true`; the SDK
re-adds them with the correct instance NodeIds.
`CreateServerConfiguration_CertificateGroups` still emits
`DefaultHttpsGroup` and `DefaultUserTokenGroup` (Object exemption)
preserving the subtree NodeIds that `ConfigurationNodeManager` binds
against. `CreateNamespaceMetadataType` (the type-def factory) emits all
Optional Properties unconditionally so
`ConfigurationNodeManager.CreateNamespaceMetadataStateAsync` keeps
working for runtime-created metadata instances.
- **Strengthened regression test**
`ServerConfiguration_OptionalUnimplementedChildren_NotInAddressSpaceAsync`:
- Negative: 8 expected-suppressed Variable/Method NodeIds
(ServerConfiguration + PublishSubscribe) are absent.
- Positive: all SDK-added children resolve at their well-known
singleton-instance NodeIds — 14 ServerCapabilities Properties + 12
OperationLimits Properties + RedundantServerArray + 3 Server Optional
Variables + the existing Server.Namespaces / GetMonitoredItems /
ResendData / HistoryServerCapabilities.ServerTimestampSupported set.
- Regression guard:
`Server_ServerConfiguration_CertificateGroups_DefaultApplicationGroup_CertificateTypes`
resolves at its well-known NodeId, and the Optional `DefaultHttpsGroup`
/ `DefaultUserTokenGroup` roots remain emitted (Object exemption).
- WellKnownRole regression guard: one method per modifiable role
resolves at its well-known NodeId (Observer.AddIdentity,
Observer.ApplicationsExclude, Observer.AddApplication,
Operator.AddEndpoint, Engineer.RemoveIdentity,
Supervisor.RemoveApplication, ConfigureAdmin.RemoveEndpoint,
SecurityAdmin.AddIdentity).
- `Opc.Ua.Server.Tests`: **1683 / 1683** pass on net10 (5 pre-existing
skips).
- `Opc.Ua.Core.Security.Tests`: **547 / 547** pass on net10 (68
pre-existing skips). Includes the RoleManagementTests,
SecurityRoleServerBase2Tests, and SecurityRoleServerDefaultPermsTests
fixtures.
- `Opc.Ua.Features.Tests`: **128 / 128** pass on net10 (22 pre-existing
skips). Includes the RoleManagementClient and
MonitoredItemAliasNameRefreshStrategySignAndEncryptTests fixtures.
- `Opc.Ua.InformationModel.Tests`: **1052 / 1052** pass on net10 (24
pre-existing skips).
- `Opc.Ua.Gds.Tests`: **875 / 875** pass on net10 (29 pre-existing
skips).
- `Opc.Ua.SourceGeneration.Core.Tests`: **3514 / 3514** pass on net10 (8
long-running compile-cycle integration tests skipped by the framework).
- No `AnalogItemType.EURange` regression.

## Checklist

- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [ ] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [x] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
2026-06-12 15:13:41 +02:00
Marc Schier 4756e7c245 DI (OPC 10000-100) with samples (Pump, SW Update), fluent API gap closures and model dependency implementation (#3808)
This PR implements the **OPC UA Device Integration (DI, OPC 10000-100)**
companion-spec library trio (`Opc.Ua.Di` / `Opc.Ua.Di.Server` /
`Opc.Ua.Di.Client`) plus a worked example
(`Applications/MinimalPumpServer`) demonstrating the OPC 40223 Pumps
companion specification end-to-end with full simulation. It also closes
11 fluent-API gaps in `Opc.Ua.Server.Fluent`, three source-generator
bugs, integrates the DI layer into the unified `services.AddOpcUa()`
Microsoft.Extensions DI hosting pattern, and adds 250+ new tests.

**Worked example — `Applications/MinimalPumpServer/`**
- AOT-compatible console host using `Host.CreateApplicationBuilder` +
`AddOpcUa()`.
- `PumpNodeManager` inherits from `DiNodeManager`; multi-namespace (DI +
Machinery + Pumps) via runtime NodeSet2 loading and
`ModelLoaderBuilder`.
- Demonstrates declarative second-pump creation via
`.ConfigureDevicesFor<PumpNodeManager>(...)` and software-package
seeding from the same configurator.
- Full simulation: pressure, temperatures, power, flow, efficiency,
level, cavitation/overheat fault injection, alarm setup, NAMUR-style
supervision.

**Documentation**
- `Docs/Opc.Ua.Di/` — README, DeviceBuilder, Hosting, LockService,
SoftwareUpdate, ClientHelpers (6 new developer guides).
- `Docs/Opc.Ua.Di/ComplianceMatrix.md` — every DI declaration mapped to
In-v1 / Generated / Deferred / Excluded.
- `Docs/CompanionSpecLibraries.md` — packaging guide for new
companion-spec libraries.
- `Docs/FluentApiGaps.md` — historical record of G1–G11.
- `Docs/SourceGeneratedNodeManagers.md` updated for richer node
managers.
- `Docs/plans/StateMachineBuilder.md` — deferred state-machine builder
design.
2026-06-04 05:57:57 +02:00
Marc SchierandCopilot 94bc03b9f6 fix address space issues (#3705)
* fix missing mandatory children

* Source generator: fix encoding nodes, EventNotifier, and type RBAC

- Emit DataType encoding nodes (Default Binary/XML/JSON) into the
  generated address space. CollectEncodingNodes() discovers
  DataTypeEncodingType instances and builds synthetic hierarchies
  with their HasEncoding/HasDescription references.

- Auto-set EventNotifier=SubscribeToEvents on Object nodes that have
  forward HasEventSource or HasNotifier references. The fix works at
  two levels: ModelDesignValidator infers SupportsEvents on types with
  event references in design XML, and NodeStateGenerator checks the
  resolved reference set for merged instances that inherit references
  from their type definitions.

- Wrap AccessRestrictions/RolePermissions in if(forInstance) guards
  for ObjectType, VariableType, DataType, and ReferenceType templates
  so type definition nodes remain accessible to all users.

- Allow ModellingRule=None Object children on type definitions to be
  emitted (e.g. state machine states/transitions).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update missing nodes analysis after source generator fixes

Retest with admin credentials against ConsoleReferenceServer v1.05.07.
Categorize 794 truly missing nodes (after filtering optional/placeholder):
- 384 encoding nodes (NodeSet2.xml-only, not in design XML)
- 172 role instance children
- 84 other nodes
- Remaining: trust list, PubSub, diagnostics, etc.

RBAC and EventNotifier fixes confirmed working.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update

* plans: detailed analysis of 292 remaining missing nodes

Replaced the two previous analysis files with a single consolidated
validation report. Each of the 292 nodes is classified with a reason:

- 212 type-level children (ObjectType definitions not instantiated)
- 48 instance-level children (diagnostics, HA, FileSystem, PubSub)
- 32 orphan property templates (design-time canonical definitions)

Only 1 fix recommended: Server.Auditing (i=2994) should be exposed
as false per Part 5 section 6.3.3.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: add GDS node manager to ConsoleReferenceServer

Add Global Discovery Server functionality to the reference server:
- Add Opc.Ua.Gds.Server.Common project reference
- Add GdsNodeManagerFactory that creates ApplicationsNodeManager
  with JsonApplicationsDatabase and CertificateGroup (auto-approve)
- Add GlobalDiscoveryServerConfiguration extension to config XML
- Add GDS to ServerCapabilities
- Registration is opt-in: only when config extension is present

Also fix pre-existing source generator test failures:
- Add missing CreateOrReplaceTransitionNumber stub method to
  StateMachineTransitionState in CompilerUtils.cs (8 tests fixed)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: update missing nodes report with GDS retest results

Retested after adding GDS node manager to ConsoleReferenceServer.
Total missing reduced from 1325 to 1243 (82 fewer).
335 remaining 'other' nodes fully classified with per-node reasons.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: Server.Auditing is present (access-restricted, not missing)

Live validation confirmed i=2994 exists with value=true, visible to
admin users. Returns BadUserAccessDenied for anonymous. No fixes needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix CttTestRunner build errors for v1.6 API and Jint 4.8.0

- Update System.CommandLine 2.0.7 API: SetHandler->SetAction, InvokeAsync->Parse().InvokeAsync()
- Remove ObjectInstance inheritance from CttAssert, CttServiceRegister, CttIdentifierConstants, CttMonitoredItemHelper; use ToJsObject() factory methods instead
- Replace Jint.Native.JsValueType with .IsObject()/.IsNumber()/.IsString() etc.
- Add missing using directives (Jint.Runtime, Microsoft.Extensions.Logging, etc.)
- Fix NodeId.Identifier obsolete -> TryGetIdentifier<uint>
- Fix DataValue.Value obsolete -> WrappedValue.AsBoxedObject()
- Fix ApplicationInstance() obsolete -> ApplicationInstance(ITelemetryContext)
- Fix LoadApplicationConfiguration -> LoadApplicationConfigurationAsync
- Fix CheckApplicationInstanceCertificate -> CheckApplicationInstanceCertificatesAsync
- Fix CoreClientUtils.SelectEndpointAsync signature (requires telemetry param)
- Fix DefaultSessionFactory() obsolete -> DefaultSessionFactory(ITelemetryContext)
- Fix DiscoveryClient.Create -> DiscoveryClient.CreateAsync
- Fix FindServers/GetEndpoints -> FindServersAsync/GetEndpointsAsync
- Fix StatusCode to uint conversions
- Fix ArrayOf<T> iteration (use foreach instead of LINQ on IConvertableToArray)
- Fix XmlNodeList null coalescing in CttProjectSettings
- Fix JsValue.False -> JsValue.FromObject(engine, false)
- Add --json option to Program.cs for machine-readable output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: emit type-level children with ModellingRule=None for all node classes

The source generator's GetChildren method only included
ModellingRule=None children when they were ObjectDesign instances
(state machine states/transitions). This excluded Variables and
Methods that are explicitly defined on types without a modelling
rule, such as:

- ConditionType.ConditionRefresh (Method)
- ConditionType.ConditionRefresh2 (Method)
- ConditionType.SupportsFilteredRetain (Variable)
- ProgramStateMachineType.Creatable (Variable)
- Various TrueState/FalseState display-hint properties

Remove the ObjectDesign restriction so all explicitly defined
type-level children with ModellingRule=None are included in the
generated address space. This adds ~52 previously missing nodes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: update report after ModellingRule=None generator fix

Type-level children dropped from 261 to 203 (-58) after fixing the
source generator to emit Variables and Methods with ModellingRule=None.
Total 'other' reduced from 335 to 283. No fixes needed for any
remaining nodes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: all 203 type-level children confirmed present via Read

Verified all 203 'type-level children' exist in the server (none
return BadNodeIdUnknown). They return Good, BadUserAccessDenied,
or BadSecurityModeInsufficient -- all confirming the node exists
but is access-restricted for anonymous users.

All 1,316 export-missing nodes are now accounted for. Zero nodes
are actually missing from the server address space.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix CttTestRunner session service methods for CTT JS script compatibility

- Implement ServiceCreateSession with real Session.CreateAsync connection
- Implement ServiceActivateSession with proper response fields
- Implement ServiceCloseSession with session cleanup
- Fix UaSession constructor to provide service methods via CreateServiceObject
- Convert all type constructors from ClrFunction to JS functions for 'new' support
  (Jint 4.x ClrFunction does not support [[Construct]])
- Add UaChannel.connect/disconnect stubs for OpenSecureChannel flow
- Add UaDiscovery with getEndpoints/findServers from discovery session
- Add missing types: UaSubscriptionDiagnosticsDataType, UaHistoryUpdateResult, etc.
- Add missing enums: IdentifierType, Constants (DateTime/numeric limits)
- Add UaPkiCertificate.fromDER, UaByteString.fromStringData, PkiType, UaPkiUtility
- Add UaDateTime.Now constructor, equals/msecsTo methods
- Add UaStatusCode with isGood/isBad/isUncertain as JS constructor
- Fix response headers with Timestamp (UaDateTime), ServiceDiagnostics, StringTable
- Fix endpoint descriptions with Server, UserIdentityTokens, ServerCertificate
- Add CreateResponseHeader helper for consistent response header construction
- Override warnOnce.js loggers to avoid Function.caller incompatibility
- Add stopTest, checkResourceError (lowercase) global functions
- Add UaOpcServer constructor with all method stubs
- Handle JS parse errors in include() for malformed CTT files
- Test.Connect() now calls InstanciateHelpers with DiscoverySession

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add CttTestRunner: .NET replacement for CTT JavaScript host environment

Implements a Jint-based JavaScript engine that can execute the OPC Foundation's
Compliance Test Tool .js test scripts using the OPC UA .NET Standard stack.

Features:
- Parses CTT .ctt.xml project files (Qt AbstractItemModelData format)
- Loads and resolves CTT include() dependency chains (~100 library files)
- Registers ~250 Ua* type constructors for JavaScript
- Implements session.read/write/browse/getEndpoints/findServers
- Provides all OPC UA enumerations, NodeId constants, Assert methods
- CLI with --list, --file, --conformance-unit, --result, --json flags
- Headless operation for agent/CI integration

Current status: Framework connects to OPC UA server and loads CTT test
infrastructure. Session establishment flow needs additional property
compatibility for CreateSession/ActivateSession request objects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: do not apply DefaultAccessRestrictions/DefaultRolePermissions to type definitions

ObjectType and VariableType nodes should never have access
restrictions or role permissions from DefaultAccessRestrictions/
DefaultRolePermissions. These defaults are meant to apply to
instances of the type, not to the type definition node itself.

Previously, CertificateGroupType, TrustListType, SecurityGroupType
and other types were incorrectly emitted with AccessRestrictions
inherited from their DefaultAccessRestrictions, making them
unreadable without encryption even though the spec defines no
restrictions on these type definitions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* CttTestRunner: fix session establishment flow

- Fix constructor patterns (new/no-new dual call support)
- Fix ResponseHeader with ServiceDiagnostics/StringTable
- Populate ServerNonce, ServerCertificate, ServerEndpoints in CreateSession response
- Add UaVariant setter methods (setNodeId/setString/setBoolean/etc.)
- Add UaCreateSessionRequest/ActivateSessionRequest with proper nested structures
- Fix UaPkiUtility/UaPkiCertificate with Thumbprint/toDERFile/loadCertificateFromFile
- Add missing enums: PerformUpdateType, SecurityPolicy, StructureType, etc.
- Add HostInfo, Bit helper, KeyPairCollection, ExpectedAndAcceptedResults
- Add _error.store(), MessageSecurityMode.toString()

Status: Session creates successfully, CreateSession validation runs,
hits validateCertificate in PKI chain (next to implement).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: update report after DefaultAccessRestrictions type definition fix

ObjectTypes no longer have incorrect access restrictions. Type
children reduced from 203 to 164 (39 now visible). Total export-
missing reduced from 1316 to 1146. Remaining 164 type children are
individual methods/variables with spec-correct access restrictions.

Two source generator fixes applied:
- ModellingRule=None children for Variables/Methods
- DefaultAccessRestrictions not applied to ObjectType/VariableType

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* CttTestRunner: fix PKI, cert validation, warnOnce, session response

- Intercept warnOnce.js with Jint-compatible replacement (no Function.caller)
- Add _log.store() global helper
- Add HOSTNAMES/HostnameFromUrl helpers for cert hostname validation
- Add UaPkiCertificate properties: PublicKey, Issuer, Subject, KeyUsage, Hostnames
- Add UaPkiUtility.validateCertificate stub
- Add UaByteString static helpers: FromByteArray, ToByteArray, fromHexString
- Use JS UaNodeId constructor for proper equals() support
- Fill AuthenticationToken and 32-byte ServerNonce in CreateSession response
- Add clone/append to ByteString objects

Session flow: CreateSession validates, ActivateSession reached, hits
Channel.ServerCertificate.clone() (IsSecure check — next to fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* CttTestRunner: complete session establishment, crypto stubs, ByteString helpers

- Add _makeByteString() helper for consistent ByteString objects with clone/append/isEmpty
- Add UaCryptoProvider with asymmetricSign/Verify stubs
- Add UaExtensionObject setter methods (setAnonymousIdentityToken, etc.)
- Fix UaChannel with full properties (RequestedSecurityPolicyUri, IsSecure, etc.)
- Fix duplicate type registrations overriding custom constructors
- Fix ActivateSession response header with ServiceDiagnostics
- Remove UaCreateSessionResponse/ActivateSessionRequest/CloseSessionRequest duplicates

Status: Full session lifecycle works (GetEndpoints → OpenSecureChannel →
CreateSession → ActivateSession). Test scripts execute and hit server
capabilities initialization (MonitoredItem NodeId settings configuration).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update

* plans: add spec references for access restriction reasoning

Added links to Part 3 sections 5.2.9, 5.2.11, and 6.3.3 explaining
why DefaultRolePermissions are applied to instance children but not
to type definitions. The NodeSet2.xml has no explicit RolePermissions
on these 164 nodes -- restrictions come from model design defaults.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: add GDS project references to ConsoleReferenceServer Dockerfile

Add Opc.Ua.Gds.Common and Opc.Ua.Gds.Server.Common csproj COPY
lines to the Docker restore layer, required since the GDS node
manager was added to the ConsoleReferenceServer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update

* feat: add IsPartOfTypeHierarchy flag to bypass access restrictions on types

Type hierarchy nodes (ObjectType/VariableType definitions and their
children) should be universally accessible regardless of
AccessRestrictions and RolePermissions.

Changes:
- Add IsPartOfTypeHierarchy property to NodeState
- Set flag on ObjectType/VariableType in generated code (!forInstance)
- Propagate flag from parent to children in AddPredefinedNode
- Add flag to NodeMetadata, propagate in GetPermissionMetadata
- Bypass ValidateRolePermissions and ValidateAccessRestrictions
  when IsPartOfTypeHierarchy is true
- Revert isTypeDefinition guard: AccessRestrictions and
  RolePermissions are now emitted on all nodes as metadata,
  but enforcement is skipped for type hierarchy nodes
- Fix forInstance propagation in type factories: use
  forInstance: forInstance for type children so deep children
  (TrueState/FalseState etc.) are included when !forInstance

Type-level missing nodes reduced from 201 to 99. Remaining 99 are
in the server but not reachable by the export tool's browse path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: update report with final IsPartOfTypeHierarchy results

Missing from export reduced to 998 (from 1325 original).
Type children: 201 -> 99 (forInstance propagation fix).
All nodes verified present via OPC UA Read.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: propagate IsPartOfTypeHierarchy in GetNodeMetadataAsync

The Browse service's per-reference ValidateRolePermissionsAsync
calls GetNodeMetadataAsync (not GetPermissionMetadataAsync). The
IsPartOfTypeHierarchy flag was only set in GetPermissionMetadataAsync,
causing type hierarchy nodes to be filtered out of Browse results.

This fixes 99 ObjectType children that were previously invisible
in Browse from their parent types. Type-level missing: 99 -> 0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: final report -- 0 type-level children missing (694 total)

Type-level missing: 99 -> 0 after fixing IsPartOfTypeHierarchy
propagation in GetNodeMetadataAsync. Total export-missing: 694
(384 encoding + 236 access-restricted + 74 instance/orphan).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 14:20:37 +02:00
Marc Schier 94653f1aee Add a MCP server that enables LLM/Copilot to be a OPC UA client (#3701)
* Opc ua client Mcp Server
* fix for #3699
2026-04-24 19:03:45 +02:00
Marc Schier fd41681f4b Add support to publish OPC UA applications as Native AOT (#3633)
* make most ptojects aot compatible
* Remove newtonsoft
* Migrate all projects from Mono.Options to System.CommandLine 2.0.5
* Fix array clone
* Replace serializers for storage using new attribute driven  type geberator
* Add new AOT integration tests covering all functional areas
* Documentation updates
2026-04-13 20:52:29 +02:00
Marc Schier 03777ecb09 New Json decoder/encoders, array/matrix abstractions, ByteString, and more. (#3619)
- **Collections overhaul** — new/rewritten collection types (`ArrayOf`, `Collections` commits)
- **`Variant` builder/accessor** — new APIs for constructing and accessing Variants
- **`NodeId` operators made explicit** — breaking change for implicit conversions
- **Version bumped to 1.6** with preview build support
- **NumericRange** uses Variant
- **object** type accessors and general usage removed.

### 🏗️ Infrastructure
- Build config updated; `.NET 10` targeting
- InternalsVisibleTo moved to `.csproj`
- Pipeline/CI adjustments
2026-03-18 12:23:30 +01:00
Marc Schier 601cabf86f Replace all use of object in code and API with Variant (#3550) 2026-02-15 06:19:38 +01:00
Marc Schier f66ceffd75 Immutable built in types and code generation (#3534)
Generate stack and application code using source generators
Change built in types to readonly structs
  Update localized text implementation to use JSON text serializer
  Status code with symbolic names
  Immutable node ids
  No boxing for all built in types <= 8 bytes in Variant.
Split user identity token and token handling via token handler
Add migration guide for breaking changes
2026-02-12 16:27:07 +01:00