mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-18 14:43:08 +08:00
81f200c9d2
## Summary Align the generic WoT/NodeSet converter, WoT Connectivity registry, and runtime binding implementation with the OPC UA WoT Binding 1.1 draft. ### Conversion and preservation - fix the standard placeholder ModellingRule NodeIds; - make every produced/restored NodeSet importable by completing required aliases; - support `ua:ComponentOf`, arbitrary companion ReferenceTypes, BrowseNames, InverseNames, symmetric references, and exact direction; - enforce the projection annotation allowlist and deterministic total order; - emit and consume Method argument schemas, EventType data, Alarms & Conditions, event severity, engineering units/ranges, localization, ValueRank, and ArrayDimensions; - preserve unsupported content through JSON residue/native projection rather than silently dropping it; - compare residue conflicts with RFC 8785 canonical equality while retaining byte-exact residue digests; - stamp generated documents with `uav:bindingVersion: "1.1"`. ### Runtime bindings - resolve an EventType Thing Model referenced with `tm:ref`, recursively derive its `data` fields as the baseline selection, and overlay explicit `uav:eventSelectClauses` that also reference EventType TMs; - pre-resolve EventType links asynchronously through the bounded sibling-document resolver before synchronous protocol planning; - retain read compatibility with legacy implementation-specific `uav:eventFields`; - expose nested event `data` matching the TD while retaining the flat transport field index for compatibility; - apply minimum-security constraints through deterministic endpoint selection and fail-closed session verification; - use one Unicode code-point comparer for projection membership and endpoint ties. ### Validation and fixtures - add strict, profile-aware validation while keeping default parsing permissive and residue-preserving; - pin 27 normative examples byte-for-byte to the specification repository, with an offline SHA-256 manifest and regeneration test; - regenerate checked-in WoT documents with the final canonical writer and binding version; - document the generic converter separately from the legacy OPC 10100-1 asset path. ## Notable compatibility changes - converted NodeSets may gain a completed `<Aliases>` table so standard ReferenceType names can be imported; - previously inverted/dangling placeholder ModellingRules are corrected; - generated documents gain binding-version, localization, rank/dimension, and exact ReferenceType metadata; - event data now has the nested shape described by the binding; - the former NodeId-based `uav:typeDefinitionId` clause is removed; event selections now use EventType-TM `tm:ref` links; - `uav:eventFields` remains readable but new documents use `uav:eventSelectClauses`; - unknown well-formed future revision/profile claims produce unsupported warnings rather than strict rejection. ## Generated files Most of the 621-file diff is deterministic regeneration: 517 checked-in WoT documents gain the binding version and final canonical spelling. Regeneration was run twice and is byte-idempotent. ## Validation - `dotnet restore UA.slnx`; - `dotnet build UA.slnx -c Release` across all configured TFMs; - `Opc.Ua.Types.Tests`: 9,215 net10.0; 9,208 net48; 9,208 net472; - `Opc.Ua.WotCon.Bindings.Tests`: 611 each on net10.0, net9.0, and net8.0; - `Opc.Ua.WotCon.Tests`: 1,136 net10.0; - `Opc.Ua.WotCon.Samples.Tests`: 14 net10.0; - NativeAOT test executable: 136/136; - all 27 strict fixture conversion/import checks; - formatting, analyzers, `git diff --check`, and regeneration idempotence. The full net48 run retains one unrelated existing MAX_PATH failure in `FileWotRegistryStoreTests.MultiLevelDirectoryCreationSyncsEveryModifiedParentInOrder`. Solution-wide stress runs also include an intentional 60-minute soak and unrelated locale-sensitive/flaky tests; all affected WoT projects and tests pass. ## Specification Depends on OPCF-Members/spec-drafts#31. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6a9f41b1-72f0-4c74-bd7f-26fa531e5738
227 lines
11 KiB
C#
227 lines
11 KiB
C#
/* ========================================================================
|
|
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
|
*
|
|
* OPC Foundation MIT License 1.00
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* The complete license agreement can be found here:
|
|
* http://opcfoundation.org/License/MIT/1.00/
|
|
* ======================================================================*/
|
|
|
|
using System.Collections.Generic;
|
|
|
|
namespace Opc.Ua.Server.Tests.CoverageNodeSet
|
|
{
|
|
/// <summary>
|
|
/// The expected node / attribute / reference catalogue authored in
|
|
/// <c>Opc.Ua.CoverageTest.NodeSet2.xml</c>. The same catalogue drives the
|
|
/// data-driven assertion battery for both the source-generation server and
|
|
/// the runtime-import server so that any attribute or reference dropped by
|
|
/// either pipeline fails a test.
|
|
/// </summary>
|
|
public static class CoverageTestCatalogue
|
|
{
|
|
/// <summary>
|
|
/// The model namespace URI.
|
|
/// </summary>
|
|
public const string NamespaceUri = "http://opcfoundation.org/UA/CoverageTest/";
|
|
|
|
/// <summary>
|
|
/// The secondary (dependent) model namespace URI.
|
|
/// </summary>
|
|
public const string SecondaryNamespaceUri =
|
|
"http://opcfoundation.org/UA/CoverageTest/Secondary/";
|
|
|
|
/// <summary>
|
|
/// An expected node: its numeric identifier in the model namespace,
|
|
/// its BrowseName, and its NodeClass.
|
|
/// </summary>
|
|
public sealed record ExpectedNode(uint Id, string BrowseName, NodeClass NodeClass);
|
|
|
|
/// <summary>
|
|
/// An expected reference edge. <paramref name="Target"/> is either a
|
|
/// model-namespace identifier (when <paramref name="TargetIsOwned"/>)
|
|
/// or an absolute standard <see cref="NodeId"/> numeric identifier in
|
|
/// namespace 0.
|
|
/// </summary>
|
|
public sealed record ExpectedReference(
|
|
uint Source,
|
|
uint ReferenceType,
|
|
uint Target,
|
|
bool TargetIsOwned,
|
|
bool IsForward);
|
|
|
|
/// <summary>
|
|
/// Every node authored in the model namespace.
|
|
/// </summary>
|
|
public static IReadOnlyList<ExpectedNode> Nodes { get; } =
|
|
[
|
|
// Reference types.
|
|
new(5001, "CoverageHierarchicalReference", NodeClass.ReferenceType),
|
|
new(5002, "CoverageSymmetricReference", NodeClass.ReferenceType),
|
|
new(5003, "CoverageAbstractReference", NodeClass.ReferenceType),
|
|
new(5004, "CoverageConcreteReference", NodeClass.ReferenceType),
|
|
|
|
// Data types + members + encodings.
|
|
new(5100, "CoverageEnumeration", NodeClass.DataType),
|
|
new(5101, "EnumValues", NodeClass.Variable),
|
|
new(5110, "CoverageOptionSet", NodeClass.DataType),
|
|
new(5111, "OptionSetValues", NodeClass.Variable),
|
|
new(5120, "CoverageAbstractStructure", NodeClass.DataType),
|
|
new(5130, "CoverageStructure", NodeClass.DataType),
|
|
new(5131, "Default Binary", NodeClass.Object),
|
|
new(5132, "Default XML", NodeClass.Object),
|
|
new(5133, "Default JSON", NodeClass.Object),
|
|
new(5140, "CoverageOptionalStructure", NodeClass.DataType),
|
|
new(5141, "Default Binary", NodeClass.Object),
|
|
new(5150, "CoverageUnion", NodeClass.DataType),
|
|
new(5151, "Default Binary", NodeClass.Object),
|
|
new(5160, "CoveragePlainStructure", NodeClass.DataType),
|
|
new(5161, "Default Binary", NodeClass.Object),
|
|
|
|
// Object / variable / event / interface types.
|
|
new(5200, "CoverageAbstractObjectType", NodeClass.ObjectType),
|
|
new(5210, "CoverageObjectType", NodeClass.ObjectType),
|
|
new(5211, "MandatoryVariable", NodeClass.Variable),
|
|
new(5212, "OptionalVariable", NodeClass.Variable),
|
|
new(5213, "MandatoryPlaceholder", NodeClass.Object),
|
|
new(5214, "OptionalPlaceholder", NodeClass.Object),
|
|
new(5215, "ExposesArrayVariable", NodeClass.Variable),
|
|
new(5216, "MandatoryMethod", NodeClass.Method),
|
|
new(5217, "InputArguments", NodeClass.Variable),
|
|
new(5218, "OutputArguments", NodeClass.Variable),
|
|
new(5230, "CoverageEventType", NodeClass.ObjectType),
|
|
new(5240, "CoverageInterfaceType", NodeClass.ObjectType),
|
|
new(5300, "CoverageVariableType", NodeClass.VariableType),
|
|
|
|
// Instances.
|
|
new(5400, "CoverageRoot", NodeClass.Object),
|
|
new(5402, "TypedObject", NodeClass.Object),
|
|
new(5403, "MandatoryVariable", NodeClass.Variable),
|
|
new(5404, "MandatoryMethod", NodeClass.Method),
|
|
new(5405, "InputArguments", NodeClass.Variable),
|
|
new(5406, "OutputArguments", NodeClass.Variable),
|
|
new(5410, "EventSource", NodeClass.Object),
|
|
new(5411, "SubNotifier", NodeClass.Object),
|
|
new(5412, "CoverageCondition", NodeClass.Object),
|
|
new(5420, "BooleanValue", NodeClass.Variable),
|
|
new(5421, "Int32Value", NodeClass.Variable),
|
|
new(5422, "StringArrayValue", NodeClass.Variable),
|
|
new(5423, "DoubleMatrixValue", NodeClass.Variable),
|
|
new(5424, "DateTimeValue", NodeClass.Variable),
|
|
new(5425, "EnumerationValue", NodeClass.Variable),
|
|
new(5426, "OrderedValue", NodeClass.Variable),
|
|
new(5450, "AddNumbers", NodeClass.Method),
|
|
new(5451, "LockedMethod", NodeClass.Method),
|
|
new(5452, "InputArguments", NodeClass.Variable),
|
|
new(5453, "OutputArguments", NodeClass.Variable),
|
|
new(5460, "CoverageView", NodeClass.View),
|
|
new(5470, "CoverageStateMachine", NodeClass.Object),
|
|
new(5471, "StateA", NodeClass.Object),
|
|
new(5472, "StateB", NodeClass.Object),
|
|
new(5473, "TransitionAB", NodeClass.Object),
|
|
|
|
// Deep, branching instance tree.
|
|
new(5490, "TreeRoot", NodeClass.Object),
|
|
new(5491, "BranchA", NodeClass.Object),
|
|
new(5492, "LeafA1", NodeClass.Variable),
|
|
new(5493, "SubBranchA", NodeClass.Object),
|
|
new(5494, "LeafA2", NodeClass.Variable),
|
|
new(5495, "BranchB", NodeClass.Object),
|
|
new(5496, "LeafB1", NodeClass.Variable),
|
|
];
|
|
|
|
/// <summary>
|
|
/// Every node authored in the secondary (dependent) model namespace.
|
|
/// </summary>
|
|
public static IReadOnlyList<ExpectedNode> SecondaryNodes { get; } =
|
|
[
|
|
new(6001, "SecondaryObjectType", NodeClass.ObjectType),
|
|
new(6010, "SecondaryInstance", NodeClass.Object),
|
|
];
|
|
|
|
/// <summary>
|
|
/// At least one forward (or explicitly authored inverse) occurrence of
|
|
/// every reference relation the model exercises.
|
|
/// </summary>
|
|
public static IReadOnlyList<ExpectedReference> References { get; } =
|
|
[
|
|
// Hierarchical.
|
|
new(5400, ReferenceTypes.Organizes, Objects.ObjectsFolder, false, false),
|
|
new(5400, ReferenceTypes.HasComponent, 5424, true, true),
|
|
new(5400, ReferenceTypes.HasOrderedComponent, 5426, true, true),
|
|
new(5100, ReferenceTypes.HasProperty, 5101, true, true),
|
|
new(5200, ReferenceTypes.HasSubtype, 5210, true, true),
|
|
new(5410, ReferenceTypes.HasEventSource, 5411, true, true),
|
|
|
|
// Non-hierarchical / instance wiring.
|
|
new(5420, ReferenceTypes.HasTypeDefinition, VariableTypes.BaseDataVariableType, false, true),
|
|
new(5211, ReferenceTypes.HasModellingRule, ObjectIds_ModellingRule_Mandatory, false, true),
|
|
new(5212, ReferenceTypes.HasModellingRule, ObjectIds_ModellingRule_Optional, false, true),
|
|
new(5213, ReferenceTypes.HasModellingRule, ObjectIds_ModellingRule_MandatoryPlaceholder, false, true),
|
|
new(5214, ReferenceTypes.HasModellingRule, ObjectIds_ModellingRule_OptionalPlaceholder, false, true),
|
|
new(5215, ReferenceTypes.HasModellingRule, ObjectIds_ModellingRule_ExposesItsArray, false, true),
|
|
new(5130, ReferenceTypes.HasEncoding, 5131, true, true),
|
|
new(5210, ReferenceTypes.HasInterface, 5240, true, true),
|
|
new(5210, ReferenceTypes.GeneratesEvent, 5230, true, true),
|
|
new(5216, ReferenceTypes.AlwaysGeneratesEvent, 5230, true, true),
|
|
new(5410, ReferenceTypes.HasCondition, 5412, true, true),
|
|
|
|
// State machine.
|
|
new(5473, ReferenceTypes.FromState, 5471, true, true),
|
|
new(5473, ReferenceTypes.ToState, 5472, true, true),
|
|
new(5473, ReferenceTypes.HasCause, 5450, true, true),
|
|
new(5473, ReferenceTypes.HasEffect, 5230, true, true),
|
|
new(5471, ReferenceTypes.HasTrueSubState, 5472, true, true),
|
|
new(5471, ReferenceTypes.HasFalseSubState, 5472, true, true),
|
|
|
|
// Custom reference types + inverse authoring.
|
|
new(5400, 5001, 5420, true, true),
|
|
new(5400, 5002, 5421, true, true),
|
|
new(5400, 5004, 5422, true, true),
|
|
|
|
// Deep instance tree (nested HasComponent chain).
|
|
new(5400, ReferenceTypes.HasComponent, 5490, true, true),
|
|
new(5490, ReferenceTypes.HasComponent, 5491, true, true),
|
|
new(5491, ReferenceTypes.HasComponent, 5493, true, true),
|
|
new(5493, ReferenceTypes.HasComponent, 5494, true, true),
|
|
];
|
|
|
|
// Standard modelling-rule object identifiers (namespace 0). OPC 10000-5
|
|
// assigns OptionalPlaceholder 11508 and MandatoryPlaceholder 11510; the
|
|
// two are neither adjacent nor in name order, so they are easy to swap.
|
|
private const uint ObjectIds_ModellingRule_Mandatory = 78;
|
|
private const uint ObjectIds_ModellingRule_Optional = 80;
|
|
private const uint ObjectIds_ModellingRule_ExposesItsArray = 83;
|
|
private const uint ObjectIds_ModellingRule_OptionalPlaceholder = 11508;
|
|
private const uint ObjectIds_ModellingRule_MandatoryPlaceholder = 11510;
|
|
|
|
/// <summary>
|
|
/// Builds the model-namespace <see cref="NodeId"/> for an owned node.
|
|
/// </summary>
|
|
public static NodeId NodeId(uint id, ushort namespaceIndex)
|
|
{
|
|
return new NodeId(id, namespaceIndex);
|
|
}
|
|
}
|
|
}
|