You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add structure-backed OptionSet DataType support in ComplexTypeSystem (#3672)
Concrete sub-types of the abstract OptionSet DataType (i=12755, e.g. AccessRights, CarExtras, OptionSetBase) were silently skipped by ComplexTypeSystem.LoadBaseStructureDataTypesAsync when only an EnumDefinition or OptionSetValues property was available. This change registers them with a new runtime class Opc.Ua.Encoders.OptionSet that reuses the generated Opc.Ua.OptionSet wire format (Value/ValidBits ByteStrings) and self-registers via IEncodeableType.
Key changes:
- New Stack/Opc.Ua/Types/OptionSet.cs with EnumDefinition-driven bit accessors (by name and index), GetSetFieldNames helper, and a fixed ByteLength derived from the highest declared bit per OPC UA Part 3 paragraph 8.40 / 3.2.8. SetBit throws ArgumentOutOfRangeException outside that range and always materializes Value/ValidBits to the fixed length.
- New AddOptionSetType entry point on IComplexTypeBuilder (source-breaking addition for custom implementations); DefaultComplexTypeBuilder implements it. The Reflection.Emit ComplexTypeBuilder throws NotSupportedException.
- ComplexTypeSystem detects OptionSet descendants via a HasSubtype walk and resolves the EnumDefinition from DataTypeDefinition or the OptionSetValues property fallback before the old skip path.
- Removed stale V1.04 OptionSet limitation remark from ComplexTypeSystem.
- 10 unit tests in Tests/Opc.Ua.Client.Tests/ComplexTypes/DefaultOptionSetTests.cs covering factory registration, bit accessors, binary round-trip via ExtensionObject, hand-crafted Part 6 wire format, Clone/CreateInstance semantics, and the ByteLength invariant.
- Live-verified against opc.tcp://DESKTOP-BGFJS91:48010 (UA C++ SDK demo): AccessRights/CarExtras/OptionSetBase now appear in the registered custom-type list; no skip-log entries; LoadAsync(throwOnError:true) returns true.
- Docs/ComplexTypes.md Known Limitations reworded; Docs/MigrationGuide.md gains an OptionSet DataType support subsection.
UInteger-backed OptionSet DataTypes remain intentionally opaque (carried as their unsigned integer in a Variant).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: Docs/ComplexTypes.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -837,7 +837,9 @@ TypeInfo TypeInfo { get; } // Type info of the field
837
837
838
838
## Known Limitations
839
839
840
-
1.**OptionSet Support**: OPC UA 1.04 OptionSet types do not automatically create enumeration flags
840
+
1.**OptionSet Support**: Concrete Structure-backed sub-types of the abstract `OptionSet` DataType (`i=12755`, e.g. `AccessRights`, `CarExtras`) are automatically registered by the default `ComplexTypeSystem` builder as `Opc.Ua.Encoders.OptionSet` runtime instances, driven by either the `EnumDefinition` carried in `DataTypeDefinition` or a fallback synthesized from the `OptionSetValues` property. The runtime class exposes the two canonical `Value` / `ValidBits` ByteStrings plus bit accessors keyed by field name or bit index. Per Part 3 §8.40 / §3.2.8, the overall ByteString length is fixed by the sub-type's declared bits (exposed as `ByteLength`); setting a bit outside that range throws `ArgumentOutOfRangeException`. Remaining limitations:
841
+
- UInteger-backed OptionSet DataTypes (DataTypes deriving from an unsigned integer with `IsOptionSet=true`) continue to be represented as their underlying unsigned integer in a `Variant` — no per-bit metadata is surfaced.
842
+
- The legacy Reflection.Emit builder in `Opc.Ua.Client.ComplexTypes` throws `NotSupportedException` for OptionSet sub-types; switch to the default builder (`new ComplexTypeSystem(session)`) for OptionSet support.
841
843
2.**Legacy Dictionary Support**: Some OPC UA 1.03 structured types that cannot be mapped to OPC UA 1.04 definitions are ignored
842
844
3.**Type Modifications**: Once loaded, types cannot be dynamically updated during a session. Reconnect to reload modified types.
-[Migrating from 1.05.377 to 1.05.378](#migrating-from-105377-to-105378)
48
49
-[Asynchronous as default](#asynchronous-as-default)
@@ -616,6 +617,17 @@ Core complex type interfaces and default (non-reflection-emit) implementations m
616
617
Namespace remains `Opc.Ua.Client.ComplexTypes`. If you used the default constructors without specifying the builder, and want to use the Reflection.Emit based type builders,
617
618
you need to change your code to call `ComplexTypeSystem.Create(...)` instead of `new ComplexTypeSystem(...)` which now uses the new default builder not supporting Reflection.Emit.
618
619
620
+
#### OptionSet DataType support
621
+
622
+
Concrete Structure-backed sub-types of the abstract `OptionSet` DataType (`i=12755`) are now automatically registered by the default `ComplexTypeSystem` builder with a new runtime class `Opc.Ua.Encoders.OptionSet` (in `Stack/Opc.Ua.Types`). Bit-field metadata is resolved from `DataTypeDefinition` (`EnumDefinition`) or, as a fallback, synthesized from the `OptionSetValues` property (`LocalizedText[]`).
623
+
624
+
Impact on existing code:
625
+
626
+
-**Source-breaking for custom `IComplexTypeBuilder` implementations**: a new member `AddOptionSetType(QualifiedName, ExpandedNodeId, ExpandedNodeId, ExpandedNodeId, ExpandedNodeId, EnumDefinition)` was added to `IComplexTypeBuilder`. Custom implementations must provide it.
627
+
- The Reflection.Emit builder in `Opc.Ua.Client.ComplexTypes` throws `NotSupportedException` from `AddOptionSetType`; callers relying on the Reflection.Emit path for OptionSet sub-types should switch to the default builder (`new ComplexTypeSystem(session)`).
628
+
- No wire-format changes: encoders/decoders continue to route through `IEncodeableFactory` → `IEncodeableType.CreateInstance`, which now yields `Opc.Ua.Encoders.OptionSet` for registered sub-types.
629
+
- UInteger-backed OptionSet DataTypes remain treated as their underlying unsigned integer in a `Variant` (unchanged).
630
+
619
631
### Other Breaking Changes
620
632
621
633
-**Session/Browser state format**: Persistence switched from `DataContractSerializer` XML to `BinaryEncoder`. Delete old persisted files and re-establish sessions.
0 commit comments