feat(commons): batched WriteValues on the address-space sink

This commit is contained in:
Joseph Doherty
2026-08-15 12:09:27 -04:00
parent 1320f3fa08
commit bb3ccf264b
4 changed files with 155 additions and 1 deletions
@@ -86,12 +86,28 @@ public class DeferredSinkForwardingReflectionTests
{
if (method.Name == nameof(IOpcUaAddressSpaceSink.RebuildAddressSpace)) continue;
method.GetParameters().Any(p => p.ParameterType == typeof(AddressSpaceRealm)).ShouldBeTrue(
method.GetParameters().Any(p => CarriesRealm(p.ParameterType)).ShouldBeTrue(
$"{method.DeclaringType!.Name}.{method.Name} names a node but has no AddressSpaceRealm parameter — " +
"a bare node id is ambiguous across the Raw and UNS namespaces (B4-WP2). Add the realm discriminator.");
}
}
/// <summary>Whether a parameter type supplies the B4-WP2 realm discriminator — either directly, or
/// carried per element by a batch parameter (<c>WriteValues(IReadOnlyList&lt;ValueWrite&gt;)</c> is
/// realm-qualified per item, since one batch legitimately spans both namespaces). Looking THROUGH the
/// element type rather than exempting the method by name keeps the guard mechanical: a future batch
/// item struct that forgets its realm still trips it.</summary>
private static bool CarriesRealm(Type t)
{
if (t == typeof(AddressSpaceRealm)) return true;
if (t.IsGenericType && t.GetGenericArguments().Any(CarriesRealm)) return true;
return t.Namespace == typeof(IOpcUaAddressSpaceSink).Namespace
&& (t.GetProperties().Any(p => p.PropertyType == typeof(AddressSpaceRealm))
|| t.GetFields().Any(f => f.FieldType == typeof(AddressSpaceRealm)));
}
[Fact]
public void Every_IServiceLevelPublisher_method_reaches_the_inner_publisher()
{