Skip to content

CTT: keep GeneratesEvent off Object instances and Views off non-hierarchical references; classify Deadband Err-006 as CTT defect - #4667

Merged
marcschier merged 4 commits into
masterfrom
romanett/ctt-views-generatesevent-deadband
Oct 5, 2026
Merged

marcschier merged 4 commits into
masterfrom
romanett/ctt-views-generatesevent-deadband

Conversation

@romanett

@romanett romanett commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Description

Three findings from the 2026-10-03 CTT run with the patched 1.05.513 scripts. Two are server/model defects and are fixed here. The third is a CTT script defect, recorded as C56 in plans/ctt-issues.md.

1. Monitored Items Deadband Filter Err-006.js: CTT defect (C56, no server change)

The script modifies a MonitoredItem on the first static numeric scalar (a plain BaseDataVariable with no EURange) to use a PercentDeadband. It accepts only Good or Bad_MonitoredItemFilterUnsupported. Since #4629 the server returns Bad_DeadbandFilterInvalid, and that is the code the spec defines for this case:

  • OPC 10000-8 §7.2: PercentDeadband "applies only to AnalogItems with an EURange Property".
  • OPC 10000-8 §7.3.2 Table 61: Bad_DeadbandFilterInvalid is returned when "a PercentDeadband is not supported, since an EURange is not configured".

CreateMonitoredItems and ModifyMonitoredItems both go through ValidateMonitoringFilterAsync and return the same code. Monitor Basic Err-022.js already accepts Bad_FilterNotAllowed for non-analog items, so the scripts disagree with each other here.

Draft Mantis note (not filed yet):

Monitored Items Deadband Filter Err-006.js rejects Bad_DeadbandFilterInvalid for a PercentDeadband on a node without EURange
Err-006.js picks the first node of /Server Test/NodeIds/Static/All Profiles/Scalar/NumericSettings, which is normally a BaseDataVariable without an EURange. It then calls ModifyMonitoredItems with a PercentDeadband (10 %) and accepts only Good or Bad_MonitoredItemFilterUnsupported. OPC 10000-8 §7.2 limits PercentDeadband to AnalogItems with an EURange. §7.3.2 Table 61 defines Bad_DeadbandFilterInvalid for "a PercentDeadband is not supported, since an EURange is not configured", so a compliant server fails this test. Monitor Basic Err-022.js already accepts Bad_FilterNotAllowed for the non-analog fallback.
Proposed fix: add StatusCode.BadDeadbandFilterInvalid (and, consistent with Err-022.js, StatusCode.BadFilterNotAllowed) to the ExpectedAndAcceptedResults of the ModifyMonitoredItems call.

2. GeneratesEvent on Objects (Base Info Core Structure 2 001.js): source generator defect

The CTT reported ns=9;i=234 (the <ServiceName> placeholder of AuthorizationServicesFolderType) and ns=9;s=AuthorizationServices/Default as Objects that are the source of GeneratesEvent references. OPC 10000-3 §7.15 says: "The SourceNode of References of this type shall be an ObjectType, a VariableType or a Method".

OpcUaGdsModel.xml declares both references (AccessTokenRequested/Issued audit events) correctly, on AuthorizationServiceType. The source generator copied the type's own non-hierarchical references onto every Object or Variable built from the type: onto instance declarations (the placeholder) and into CreateInstanceOf<Type>, which AuthorizationServiceState initialises from, and that is how the Default service gets them. UA-ModelCompiler does the same thing, so the official Opc.Ua.Gds.NodeSet2.xml in UA-Nodeset has the same two references on ns=1;i=234.

Fix:

  • TranslateReferences now marks references declared on a type (HierarchyReference.DefinedOnType, which already existed but was never set).
  • NodeStateGenerator.GetReferences drops forward GeneratesEvent references (including subtypes such as AlwaysGeneratesEvent) for Object and Variable nodes when the reference came from the type.
  • ObjectTypes, VariableTypes and Methods keep these references. For example, the RegisterApplication methods and the Acknowledge/AddComment methods of the alarm types are unchanged.
  • References that a model declares explicitly on an instance are emitted as before.

3. View targeted by HasCause/HasEffect (Base Info Core Structure 2 001.js): sample model defect

#4485 added Views_Operations HasCause/HasEffect Views_Engineering so that View Minimum Continuation Point 01 012.js and View Basic 2 018.js have at least two references to View nodes on the configured View (/Server Test/NodeIds/NodeClasses/View = Views_Operations). OPC 10000-3 §5.4 says Views "can be the SourceNode of any hierarchical Reference. They shall not be the SourceNode of any NonHierarchical Reference."

Views_Operations now Organizes Views_Engineering and a new View, Views_Maintenance, which organizes DataAccess. This keeps two View-class references and adds no loops (ContainsNoLoops stays true).

Tests

  • NodeStateGeneratorTests.TypeGeneratesEventReferencesAreNotCopiedToObjectInstances: covers the type, the placeholder, the instance, CreateInstanceOf, Methods and an explicit instance reference. It fails without the fix.
  • ReferenceServerTest.SampleViewsHaveNoNonHierarchicalReferencesAsync. The existing NodeClassSampleNodesReferenceTheirOwnNodeClassAsync still passes.
  • Results with net10.0:
    • SourceGeneration.Core.Tests: 4267 passed, 0 failed
    • InformationModel.Tests: 1078 passed, 0 failed
    • Di.Tests, including RobotOpenUsdE2e: 438 passed, 0 failed
    • OpenUsd.Tests: 921 passed, 0 failed
    • Server.Tests ReferenceServerTest: 56 passed, 0 failed
  • UA.slnx builds with net10.0. The changed test projects build with net48.

CTT (patched 1.05.513 scripts, compared with the 2026-10-03 patched baseline)

Part Change
p02-base-info Base Info Core Structure 2 001.js: Error → Skipped. The GeneratesEvent and View errors are gone; the remaining skips are the CTT's own "type not defined within the test-script" notes. No other change.
p03-base-view-method No change. View Basic 2 018.js and View Minimum Continuation Point 01 012.js still pass.
p13-monitored-items Deadband Err-006.js still Error (C56). With the script fix it passes. Monitor Basic 007.js gave a response-timestamp delay warning (>200 ms) caused by machine load; it also appears in the 2026-09-29 base run.

🤖 Generated with Claude Code

…ical references; classify Deadband Err-006

- Source generator: an ObjectType's GeneratesEvent/AlwaysGeneratesEvent
  references were copied onto every Object/Variable built from the type
  (instance declarations such as the GDS AuthorizationServicesFolderType
  <ServiceName> placeholder, and CreateInstanceOf<Type>, hence the GDS
  AuthorizationServices/Default service). Part 3 §7.15 allows only an
  ObjectType, VariableType or Method as source. References declared on a
  type are now marked (HierarchyReference.DefinedOnType) and not emitted
  for Object/Variable nodes; types, Methods and references declared
  explicitly on an instance are unchanged.
- Reference server sample: Views_Operations was the source of HasCause and
  HasEffect to Views_Engineering (test data for View Minimum Continuation
  Point 01 012.js / View Basic 2 018.js). Views shall not be the source of
  non-hierarchical references (Part 3 §5.4), so Views_Operations now
  Organizes Views_Engineering and a new View Views_Maintenance.
- plans/ctt-issues.md: C54, Monitored Items Deadband Filter Err-006.js
  does not accept Bad_DeadbandFilterInvalid, which Part 8 §7.3.2 Table 61
  defines for a PercentDeadband without EURange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 4, 2026 15:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Variable instance behavior lacks regression coverage, and the CTT identifier range is now stale.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
What changed in this PR

Fixes CTT findings involving invalid inherited GeneratesEvent references and non-hierarchical View references, while documenting a deadband CTT defect.

Changes:

  • Tracks and filters type-declared GeneratesEvent references from instances.
  • Reworks sample View relationships to use Organizes.
  • Adds regression tests and records CTT issue C54.
File Description
tools/​Opc.Ua.SourceGeneration.Core/​Schema/​ModelDesignValidator.cs Marks type-declared references.
tools/​Opc.Ua.SourceGeneration.Core/​Schema/​ModelDesign.cs Clarifies reference metadata.
tools/​Opc.Ua.SourceGeneration.Core/​Generators/​NodeStateGenerator.cs Filters invalid inherited event references.
tests/​Opc.Ua.SourceGeneration.Core.Tests/​Resources/​TypeGeneratesEvent.ModelDesign.xml Adds generator test model.
tests/​Opc.Ua.SourceGeneration.Core.Tests/​Generators/​NodeStateGeneratorTests.cs Tests Object reference generation.
tests/​Opc.Ua.Server.Tests/​ReferenceServerTest.cs Validates sample View references.
samples/​Quickstarts.Servers/​ReferenceServer/​Model/​ReferenceServer.NodeSet2.xml Replaces invalid View relationships.
plans/​ctt-issues.md Records deadband test defect C54.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateGenerator.cs
Comment thread plans/ctt-issues.md Outdated
…54 in the CTT id range

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Code coverage

✅ Coverage gate passed.

Check Result Threshold
✅ Project line rate 88.44% (299479/338626 lines) >= 70.00%
✅ Project branch rate 78.84% >= 60.00%
✅ Patch coverage 95.45% (21/22 changed lines) >= 60.00% (<= 100 changed lines, advisory)
ℹ️ Baseline delta (advisory) +14.84 pp 73.60% recorded
Uncovered changed lines
  • tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateGenerator.cs: 4333

Coverage is above the recorded baseline - consider ratcheting coverage-thresholds.json.

Thresholds live in coverage-thresholds.json. Whole report before exclusions: line 87.56%, branch 78.04%.

…generatesevent-deadband

# Conflicts:
#	plans/ctt-issues.md
@marcschier marcschier added the ready Ready to merge once CI Passes label Oct 4, 2026
@codecov

codecov Bot commented Oct 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.36364% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.60%. Comparing base (4c1c3fd) to head (0792616).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
...ceGeneration.Core/Generators/NodeStateGenerator.cs 85.71% 1 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #4667   +/-   ##
=======================================
  Coverage   82.60%   82.60%           
=======================================
  Files        2198     2198           
  Lines      337670   337692   +22     
  Branches    58834    58840    +6     
=======================================
+ Hits       278921   278940   +19     
+ Misses      39001    39000    -1     
- Partials    19748    19752    +4     
Flag Coverage Δ
actions 82.60% <86.36%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...Opc.Ua.SourceGeneration.Core/Schema/ModelDesign.cs 97.07% <ø> (ø)
...urceGeneration.Core/Schema/ModelDesignValidator.cs 78.94% <100.00%> (+<0.01%) ⬆️
...ceGeneration.Core/Generators/NodeStateGenerator.cs 87.75% <85.71%> (-0.02%) ⬇️

... and 30 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…generatesevent-deadband

# Conflicts:
#	plans/ctt-issues.md
@marcschier
marcschier enabled auto-merge (squash) October 5, 2026 05:48
@marcschier
marcschier merged commit cce2004 into master Oct 5, 2026
171 checks passed
@romanett
romanett deleted the romanett/ctt-views-generatesevent-deadband branch October 7, 2026 03:48
marcschier added a commit that referenced this pull request Oct 8, 2026
)

Fixes #4669.

OPC 10000-3 §7.15 says the SourceNode of `GeneratesEvent` "shall be an
ObjectType, a VariableType or a Method". CTT `Base Info Core Structure 2
001.js` flags every Object that is the source of one. #4667 fixed this
in the source generator. This PR fixes the three remaining WoT places.

### 1. WotCon asset registry
- `AssetRegistry.BuildEventNode` / `RemoveEventTypes` no longer add or
remove `GeneratesEvent` from the asset Object. Event delivery already
relies on `EventNotifier = SubscribeToEvents` and root-notifier
registration.
- Tests now assert the reference is **absent** and the notifier bit is
set. The re-apply test asserts that the old EventType node is gone.

### 2. WoT NodeSet converter (`src/Opc.Ua.Types/Wot`)
- **TD → NodeSet:** a last pass (`RelocateInstanceGeneratesEvent`) moves
forward `GeneratesEvent` references off an Object/Variable root:
- An event the bound type already declares needs nothing more. Matching
is by qualified BrowseName against the local context's declarations, the
same rule as the declaration merge.
- Any other event is declared by a synthesized non-abstract
`<rootLocal>Type`. It derives from the bound type (or from
`BaseObjectType` / `BaseDataVariableType` when the TD binds none) and
becomes the instance's `HasTypeDefinition`. A Variable root gets a
`UAVariableType` with the instance's DataType and ValueRank.
- Thing Models are unchanged: the projected type keeps its
`GeneratesEvent`.
- **NodeSet → TD:**
- An instance's event affordances are now collected from its type
definition and that type's supertypes. Instance-level `GeneratesEvent`
is still read for third-party NodeSets.
- In a single readable document, a type that only carries one instance's
events (only `HasSubtype` + `GeneratesEvent`, typing exactly one
instance) is not chosen as root. It is folded back: its supertype
becomes the type binding, and no link is written when that supertype is
the default base type. The TD → NodeSet → TD round trip stays readable,
with no `uav:nodes` fallback.
- A document set gives the carrier its own Thing Model, and the instance
TD binds to it.
- **Archive check:** an event affordance is accepted when the root's
type definition, or one of its supertypes, generates it.
- `docs/WoTNodeSetConversion.md` documents the behaviour in a new "Event
source" row.

### 3. Aggregation sample
- `SamplePump.NodeSet2.xml`: each pump is typed by its own `PumpType`
subtype, `ns=1;s=Pump1.Type` (`Pump_1Type`) and `Pump2.Type`, which
declares its alarm EventTypes.
- It is per pump because each pump owns its alarm EventTypes.
`Pump2.Type` is cloned with the rest of Pump2.
- The pumps keep `EventNotifier="1"` and no longer carry
`GeneratesEvent`.
- The checked-in documents are regenerated, adding
`sample-pump-pump-1type.json` / `-2type.json`. The pump TDs still list
the alarm event affordances.
- The sample binding lookup ignores Thing Models, because the type's TM
declares the same EventType as the pump TD. A declaration missing from
the instance still fails as before.
- Docs: `WoTConnectivity.md` (asset event affordances, §12.4.2) and
`WotBindings.md`. The `GeneratesEvent` clause in the sample-limitation
note was already stale, so I removed it.

### Runtime check
`WotNativeOwnershipIndex` only uses `GeneratesEvent` in its alias table.
Projected event routing takes the owner from `uav:componentOf` /
`uav:id` and the notifier from Parent/`HasNotifier`. Nothing in
`Opc.Ua.WotCon.Server` reads an instance-level `GeneratesEvent`.

### Tests (net10.0)
- `Opc.Ua.Types.Tests`: 11446 passed, 1 skipped. New
`WotInstanceEventSourceTests` covers object and variable roots, TMs, the
TD with no events, the folded round trip, the legacy instance reference
and the document set. Two bound-type cases were added to
`WotDeclarationMergeBoundaryTests`.
- `Opc.Ua.WotCon.Bindings.Tests`: 714 passed.
- `Opc.Ua.WotCon.Tests`: 1496 passed, including the byte-exact sample
regeneration tests.
- net48 build of both test projects is clean, and `dotnet format
whitespace` is clean.

Not run: CTT `Base Info Core Structure 2 001.js` (p02-base-info) against
a WotCon server.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready Ready to merge once CI Passes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants