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

# 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](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
romanett
2026-10-05 07:50:30 +02:00
committed by GitHub
parent b25c1f6ad4
commit cce2004fdd
8 changed files with 265 additions and 4 deletions
@@ -4208,6 +4208,17 @@ namespace Opc.Ua.SourceGeneration
bool isInverse = reference.IsInverse;
// The type's GeneratesEvent references describe its instances; an Object or
// Variable cannot be their source (Part 3 §7.15), so they stay on the type.
if (!isInverse &&
reference.DefinedOnType &&
reference.SourcePath == node.Path &&
root is ObjectDesign or VariableDesign &&
IsGeneratesEventReference(reference.ReferenceType))
{
continue;
}
if (reference.TargetId != null)
{
if (!m_context.ModelDesign.TryFindNode(
@@ -4302,6 +4313,37 @@ namespace Opc.Ua.SourceGeneration
return references;
}
/// <summary>
/// True for GeneratesEvent and its subtypes such as AlwaysGeneratesEvent.
/// </summary>
private bool IsGeneratesEventReference(XmlQualifiedName referenceTypeId)
{
var generatesEvent = new XmlQualifiedName("GeneratesEvent", Ua.Types.Namespaces.OpcUa);
if (referenceTypeId == generatesEvent)
{
return true;
}
if (referenceTypeId == null ||
!m_context.ModelDesign.TryFindNode(
referenceTypeId,
string.Empty,
string.Empty,
out NodeDesign design))
{
return false;
}
for (TypeDesign type = (design as ReferenceTypeDesign)?.BaseTypeNode;
type != null;
type = type.BaseTypeNode)
{
if (type.SymbolicId == generatesEvent)
{
return true;
}
}
return false;
}
private List<InstanceDesign> GetAdditionalChildren(NodeToGenerate node)
{
List<InstanceDesign> additionalChildren = [];
@@ -718,7 +718,7 @@ namespace Opc.Ua.Schema.Model
public XmlQualifiedName TargetId { get; set; }
/// <summary>
/// Defined on type
/// Declared on a type rather than on an instance or instance declaration.
/// </summary>
public bool DefinedOnType { get; set; }
@@ -6082,6 +6082,7 @@ namespace Opc.Ua.Schema.Model
currentPath,
source.SymbolicId,
source.References[ii]);
reference.DefinedOnType = source is TypeDesign;
references.Add(reference);