Use concise Machine Vision registration names

Expose AddOpcUaMachineVision and AddOpcUaMachineVisionAMCM for model nodes, encodeables and node-state factories while retaining the generated namespace-derived compatibility entry points. Rename standalone Part 2 hosting to AddMachineVision and update all callers, documentation and registration tests without changing CLR namespaces or wire identities.
This commit is contained in:
Marc
2026-10-11 20:55:05 +02:00
parent ef68d5d0fd
commit 69b1f5db98
18 changed files with 238 additions and 38 deletions
+5 -5
View File
@@ -35,8 +35,8 @@ a model's specification status are separate: the Perception model is provisional
| Model | CLR namespace | Wire namespace | Loader |
| --- | --- | --- | --- |
| Perception model | `Opc.Ua.Vision.Perception` | `http://opcfoundation.org/UA/Vision/` | `AddOpcUaVisionPerception` |
| OPC 40100-1, specification 1.0 | `Opc.Ua.Vision.MachineVision` | `http://opcfoundation.org/UA/MachineVision` | `AddOpcUaVisionMachineVision` |
| OPC 40100-2, specification 1.00.0 | `Opc.Ua.Vision.MachineVision.AMCM` | `http://opcfoundation.org/UA/MachineVision/AMCM/` | `AddOpcUaVisionMachineVisionAMCM` |
| OPC 40100-1, specification 1.0 | `Opc.Ua.Vision.MachineVision` | `http://opcfoundation.org/UA/MachineVision` | `AddOpcUaMachineVision` |
| OPC 40100-2, specification 1.00.0 | `Opc.Ua.Vision.MachineVision.AMCM` | `http://opcfoundation.org/UA/MachineVision/AMCM/` | `AddOpcUaMachineVisionAMCM` |
Namespace URIs are exact identifiers: Part 1 has **no trailing slash**. Part 2 has one.
Part 1 depends on UA core only. Part 2 depends on UA core, Device Integration,
@@ -64,7 +64,7 @@ come from those model producers rather than hand-maintained copies.
`VisionServerOptions.MachineVisionParts` defaults to `None`. Select `Control`,
`Assets`, or `All` independently. Part 2 builders require
`Opc.Ua.MachineVision.Server` and the `AddVisionMachinery()` registration for a
`Opc.Ua.MachineVision.Server` and the `AddMachineVision()` registration for a
standalone manager. This example assumes an application implementation of
`IMachineVisionOperationsProvider` has been registered with DI:
@@ -79,7 +79,7 @@ builder.Services.AddSingleton<IMachineVisionOperationsProvider, CameraOperations
builder.Services.AddOpcUa()
.AddServer(options => options.EndpointUrls.Add("opc.tcp://localhost:62550/MachineVision"))
.AddVision(options => options.MachineVisionParts = MachineVisionParts.All)
.AddVisionMachinery()
.AddMachineVision()
.ConfigureVision(async (context, ct) =>
{
MachineVisionSystemHandle system = await context.AddMachineVisionSystemAsync(
@@ -832,7 +832,7 @@ hosting surface:
| `ConfigureVision(Func<IVisionBuildContext, CancellationToken, ValueTask>)` | Async configurator, run on server start against the standalone `VisionNodeManager` |
| `ConfigureVision(Action<IVisionBuildContext>)` | Sync configurator overload |
| `ConfigureVisionFor<TNodeManager>(...)` | Configurator targeting `VisionNodeManager` or the stock `MachineryNodeManager` |
| `AddVisionMachinery()` | Registers Part 2 models/builders with a standalone Vision owner |
| `AddMachineVision()` | Registers Part 2 models/builders with a standalone Vision owner |
| `AddVisionToMachinery(...)` | Loads the selected Vision models into a Machinery owner; selects both released parts by default |
| `ConfigureVisionOnMachinery<TNodeManager>(...)` | Configures an application-owned DI/Machinery manager that loads the Vision models |
@@ -211,8 +211,8 @@ namespace Opc.Ua.Vision.Client
{
if (!factory.ContainsEncodeableType(Amcm.DataTypeIds.SEMI_E10SystemStateInfoDataType))
{
Amcm.OpcUaVisionMachineVisionAMCMExtensions
.AddOpcUaVisionMachineVisionAMCM(factory.Builder).Commit();
Amcm.OpcUaMachineVisionAMCMExtensions
.AddOpcUaMachineVisionAMCM(factory.Builder).Commit();
}
}
}
@@ -53,7 +53,7 @@ namespace Microsoft.Extensions.DependencyInjection
/// </summary>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <c>null</c>.</exception>
/// <exception cref="InvalidOperationException"></exception>
public static IOpcUaServerBuilder AddVisionMachinery(this IOpcUaServerBuilder builder)
public static IOpcUaServerBuilder AddMachineVision(this IOpcUaServerBuilder builder)
{
if (builder == null)
{
@@ -77,7 +77,7 @@ namespace Microsoft.Extensions.DependencyInjection
}
if (!visionRegistered)
{
throw new InvalidOperationException("Register AddVision before AddVisionMachinery.");
throw new InvalidOperationException("Register AddVision before AddMachineVision.");
}
if (!ownsDi)
{
@@ -142,8 +142,8 @@ namespace Opc.Ua.Vision.Server
nodes.AddOpcUaDi(context);
nodes.AddOpcUaIA(context);
nodes.AddOpcUaMachinery(context);
nodes.AddOpcUaVisionMachineVisionAMCM(context);
context.EncodeableFactory.Builder.AddOpcUaVisionMachineVisionAMCM().Commit();
nodes.AddOpcUaMachineVisionAMCM(context);
context.EncodeableFactory.Builder.AddOpcUaMachineVisionAMCM().Commit();
}
}
@@ -194,8 +194,8 @@ namespace Opc.Ua.Vision.Server
throw new ServiceResultException(StatusCodes.BadConfigurationError,
"Part 2 assets require MachineryParts.BuildingBlocks on the owning manager.");
}
nodes.AddOpcUaVisionMachineVisionAMCM(context);
context.EncodeableFactory.Builder.AddOpcUaVisionMachineVisionAMCM().Commit();
nodes.AddOpcUaMachineVisionAMCM(context);
context.EncodeableFactory.Builder.AddOpcUaMachineVisionAMCM().Commit();
}
private readonly VisionServerOptions m_options;
@@ -4,7 +4,7 @@ OPC 40100-2 asset builders, identification, maintenance/health information,
condition-monitoring updates, and optical/data relationships. Builders live
in `Opc.Ua.Vision.Server` and reuse its shared context and resource ownership.
For a standalone Vision manager, call `AddVision(...).AddVisionMachinery()`.
For a standalone Vision manager, call `AddVision(...).AddMachineVision()`.
For an existing Machinery owner, call `AddVisionToMachinery()` and configure
instances through `ConfigureVisionFor<MachineryNodeManager>`.
Add multiple uniquely named systems/assets within that owner rather than
@@ -0,0 +1,69 @@
/* ========================================================================
* 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;
namespace Opc.Ua.Vision.MachineVision.AMCM
{
/// <summary>
/// Registers the OPC 40100-2 asset-management model and its generated factories.
/// </summary>
public static class OpcUaMachineVisionAMCMExtensions
{
/// <summary>
/// Adds the generated asset-management model nodes to a collection.
/// </summary>
public static NodeStateCollection AddOpcUaMachineVisionAMCM(
this NodeStateCollection nodes,
ISystemContext context)
{
return OpcUaVisionMachineVisionAMCMExtensions.AddOpcUaVisionMachineVisionAMCM(
nodes ?? throw new ArgumentNullException(nameof(nodes)),
context ?? throw new ArgumentNullException(nameof(context)));
}
/// <summary>
/// Registers the generated asset-management encodeable factories.
/// </summary>
public static IEncodeableFactoryBuilder AddOpcUaMachineVisionAMCM(this IEncodeableFactoryBuilder builder)
{
return OpcUaVisionMachineVisionAMCMExtensions.AddOpcUaVisionMachineVisionAMCM(
builder ?? throw new ArgumentNullException(nameof(builder)));
}
/// <summary>
/// Registers the generated asset-management node state factories.
/// </summary>
public static INodeStateFactoryBuilder AddOpcUaMachineVisionAMCM(this INodeStateFactoryBuilder builder)
{
return OpcUaVisionMachineVisionAMCMExtensions.AddOpcUaVisionMachineVisionAMCM(
builder ?? throw new ArgumentNullException(nameof(builder)));
}
}
}
@@ -424,8 +424,8 @@ namespace Opc.Ua.Vision.Client
}
if (!factory.ContainsEncodeableType(MachineVision.DataTypeIds.ResultDataType))
{
MachineVision.OpcUaVisionMachineVisionExtensions
.AddOpcUaVisionMachineVision(factory.Builder).Commit();
MachineVision.OpcUaMachineVisionExtensions
.AddOpcUaMachineVision(factory.Builder).Commit();
}
}
}
@@ -212,8 +212,8 @@ namespace Opc.Ua.Vision.Server
context.EncodeableFactory.Builder.AddOpcUaVisionPerception().Commit();
if ((m_options.MachineVisionParts & MachineVisionParts.Control) != 0)
{
nodes.AddOpcUaVisionMachineVision(context);
context.EncodeableFactory.Builder.AddOpcUaVisionMachineVision().Commit();
nodes.AddOpcUaMachineVision(context);
context.EncodeableFactory.Builder.AddOpcUaMachineVision().Commit();
}
}
@@ -126,7 +126,7 @@ namespace Opc.Ua.Vision.Server
nodes.AddOpcUaVisionPerception(context);
if ((Parts & MachineVisionParts.Control) != 0)
{
nodes.AddOpcUaVisionMachineVision(context);
nodes.AddOpcUaMachineVision(context);
}
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ particular camera, scene or model.
- `AddVision()` — registers the stock `VisionNodeManager` and factory in the
Generic Host pipeline. Its default wire model requires only UA core;
Part 2 hosts additionally call `AddVisionMachinery()` from the Machinery server package.
Part 2 hosts additionally call `AddMachineVision()` from `Opc.Ua.MachineVision.Server`.
- `ConfigureVision(...)` / `ConfigureVisionFor<TNodeManager>(...)` — fluent
configurator that runs on server start.
- `VisionNodeManager` / `VisionNodeManagerFactory` — standalone node
@@ -103,7 +103,7 @@ namespace Opc.Ua.Vision.Server
SystemContext.NamespaceUris.GetIndex(MachineVisionModels.AssetNamespaceUri) < 0)
{
throw ServiceResultException.Create(StatusCodes.BadConfigurationError,
"Part 2 requires the Opc.Ua.MachineVision.Server package and AddVisionMachinery().");
"Part 2 requires the Opc.Ua.MachineVision.Server package and AddMachineVision().");
}
}
@@ -368,8 +368,8 @@ namespace Opc.Ua.Vision.Server
}
if ((m_options.MachineVisionParts & MachineVisionParts.Control) != 0)
{
MachineVision.OpcUaVisionMachineVisionExtensions
.AddOpcUaVisionMachineVision(context.EncodeableFactory.Builder).Commit();
MachineVision.OpcUaMachineVisionExtensions
.AddOpcUaMachineVision(context.EncodeableFactory.Builder).Commit();
}
}
@@ -0,0 +1,69 @@
/* ========================================================================
* 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;
namespace Opc.Ua.Vision.MachineVision
{
/// <summary>
/// Registers the OPC 40100-1 Machine Vision model and its generated factories.
/// </summary>
public static class OpcUaMachineVisionExtensions
{
/// <summary>
/// Adds the generated Machine Vision model nodes to a collection.
/// </summary>
public static NodeStateCollection AddOpcUaMachineVision(
this NodeStateCollection nodes,
ISystemContext context)
{
return OpcUaVisionMachineVisionExtensions.AddOpcUaVisionMachineVision(
nodes ?? throw new ArgumentNullException(nameof(nodes)),
context ?? throw new ArgumentNullException(nameof(context)));
}
/// <summary>
/// Registers the generated Machine Vision encodeable factories.
/// </summary>
public static IEncodeableFactoryBuilder AddOpcUaMachineVision(this IEncodeableFactoryBuilder builder)
{
return OpcUaVisionMachineVisionExtensions.AddOpcUaVisionMachineVision(
builder ?? throw new ArgumentNullException(nameof(builder)));
}
/// <summary>
/// Registers the generated Machine Vision node state factories.
/// </summary>
public static INodeStateFactoryBuilder AddOpcUaMachineVision(this INodeStateFactoryBuilder builder)
{
return OpcUaVisionMachineVisionExtensions.AddOpcUaVisionMachineVision(
builder ?? throw new ArgumentNullException(nameof(builder)));
}
}
}
+2 -2
View File
@@ -11,9 +11,9 @@ the `AddOpcUaVisionPerception` model loader. The generated `ObjectTypeIds`,
`ReferenceTypeIds` and `DataTypeIds` classes are the source of truth for the
model.
Part 1 types use `Opc.Ua.Vision.MachineVision` and `AddOpcUaVisionMachineVision`.
Part 1 types use `Opc.Ua.Vision.MachineVision` and `AddOpcUaMachineVision`.
Part 2 types use `Opc.Ua.Vision.MachineVision.AMCM` and
`AddOpcUaVisionMachineVisionAMCM` from the Machinery package. Both models retain
`AddOpcUaMachineVisionAMCM` from `Opc.Ua.MachineVision`. Both models retain
their official wire identities. Part 1 requires UA core; Part 2 requires
DI/Machinery without a model dependency on Part 1.
@@ -75,7 +75,7 @@ namespace Opc.Ua.Aot.Tests
logging => logging.SetMinimumLevel(LogLevel.Warning));
var context = ServiceMessageContext.Create(telemetry);
context.NamespaceUris.GetIndexOrAppend(MachineVisionModels.ControlNamespaceUri);
context.Factory.Builder.AddOpcUaVisionMachineVision().Commit();
context.Factory.Builder.AddOpcUaMachineVision().Commit();
using var encoder = new JsonEncoder(context);
encoder.WriteVariant("Result", Variant.FromStructure(new ResultDataType
{
@@ -59,7 +59,7 @@ namespace Opc.Ua.Aot.Tests
{
message.NamespaceUris.GetIndexOrAppend(uri);
}
message.Factory.Builder.AddOpcUaVisionMachineVision().AddOpcUaVisionMachineVisionAMCM().Commit();
message.Factory.Builder.AddOpcUaMachineVision().AddOpcUaMachineVisionAMCM().Commit();
var context = new SystemContext(telemetry)
{
NamespaceUris = message.NamespaceUris,
@@ -69,8 +69,8 @@ namespace Opc.Ua.Aot.Tests
nodes.AddOpcUaDi(context);
nodes.AddOpcUaIA(context);
nodes.AddOpcUaMachinery(context);
nodes.AddOpcUaVisionMachineVision(context);
nodes.AddOpcUaVisionMachineVisionAMCM(context);
nodes.AddOpcUaMachineVision(context);
nodes.AddOpcUaMachineVisionAMCM(context);
VisionSystemState control = context.CreateInstanceOfVisionSystemType();
VisionSystemAssetState asset = context.CreateInstanceOfVisionSystemAssetType();
await Assert.That(control.VisionStateMachine).IsNotNull();
@@ -118,7 +118,7 @@ namespace Opc.Ua.Aot.Tests
var services = new ServiceCollection();
services.AddOpcUa().AddServer(options => options.ApplicationName = "MachineVisionAotServer")
.AddVision(options => options.MachineVisionParts = MachineVisionParts.All)
.AddVisionMachinery();
.AddMachineVision();
services.AddOpcUa().AddClient(options => options.ApplicationName = "MachineVisionAotClient")
.AddVisionClient();
using ServiceProvider provider = services.BuildServiceProvider();
@@ -785,7 +785,7 @@ namespace Opc.Ua.Tools.Tests.Mcp
Provider = Services(McpToolProfile.Vision).BuildServiceProvider();
Manager = Provider.GetRequiredService<OpcUaSessionManager>();
Context = ServiceMessageContext.Create(Manager.Telemetry);
Context.Factory.Builder.AddOpcUaVisionMachineVision().Commit();
Context.Factory.Builder.AddOpcUaMachineVision().Commit();
if (control)
{
Context.NamespaceUris.GetIndexOrAppend(MachineVisionModels.ControlNamespaceUri);
@@ -112,8 +112,8 @@ namespace Opc.Ua.Vision.Tests
nodes.AddOpcUaIA(context);
nodes.AddOpcUaMachinery(context);
nodes.AddOpcUaVisionPerception(context);
nodes.AddOpcUaVisionMachineVision(context);
nodes.AddOpcUaVisionMachineVisionAMCM(context);
Assert.That(nodes.AddOpcUaMachineVision(context), Is.SameAs(nodes));
Assert.That(nodes.AddOpcUaMachineVisionAMCM(context), Is.SameAs(nodes));
VisionSystemState control = context.CreateInstanceOfVisionSystemType();
VisionSystemAssetState asset = context.CreateInstanceOfVisionSystemAssetType();
@@ -129,6 +129,68 @@ namespace Opc.Ua.Vision.Tests
});
}
/// <summary>
/// The short loader names register both encodeable and node state factories without changing CLR namespaces.
/// </summary>
[Test]
public void ModelLoaderNamesRegisterTypedFactories()
{
ITelemetryContext telemetry = NUnitTelemetryContext.Create();
var message = ServiceMessageContext.CreateEmpty(telemetry);
IEncodeableFactoryBuilder encodeables = message.Factory.Builder;
Assert.That(encodeables.AddOpcUaMachineVision(), Is.SameAs(encodeables));
Assert.That(encodeables.AddOpcUaMachineVisionAMCM(), Is.SameAs(encodeables));
encodeables.Commit();
Assert.That(message.Factory.ContainsEncodeableType(MachineVision.DataTypeIds.ResultDataType), Is.True);
Assert.That(message.Factory.ContainsEncodeableType(
MachineVision.AMCM.DataTypeIds.SEMI_E10SystemStateInfoDataType), Is.True);
var context = new SystemContext(telemetry) { NamespaceUris = message.NamespaceUris };
context.NamespaceUris.GetIndexOrAppend(MachineVisionModels.ControlNamespaceUri);
context.NamespaceUris.GetIndexOrAppend(MachineVisionModels.AssetNamespaceUri);
var factory = new NodeStateFactory();
Assert.That(factory.AddOpcUaMachineVision(), Is.SameAs(factory));
Assert.That(factory.AddOpcUaMachineVisionAMCM(), Is.SameAs(factory));
Assert.That(factory.CreateInstance(
context, null, NodeClass.Object, new QualifiedName("Control"), ReferenceTypeIds.HasComponent,
ExpandedNodeId.ToNodeId(MachineVision.ObjectTypeIds.VisionSystemType, context.NamespaceUris)),
Is.TypeOf<VisionSystemState>());
Assert.That(factory.CreateInstance(
context, null, NodeClass.Object, new QualifiedName("Asset"), ReferenceTypeIds.HasComponent,
ExpandedNodeId.ToNodeId(
MachineVision.AMCM.ObjectTypeIds.VisionSystemAssetType, context.NamespaceUris)),
Is.TypeOf<VisionSystemAssetState>());
}
/// <summary>
/// Both models reject missing loader arguments at their public entry points.
/// </summary>
[Test]
public void ModelLoaderNamesRejectNullArguments()
{
var context = new SystemContext(NUnitTelemetryContext.Create());
var nodes = new NodeStateCollection();
Assert.Multiple(() =>
{
Assert.That(Assert.Throws<ArgumentNullException>(() =>
((NodeStateCollection)null!).AddOpcUaMachineVision(context))!.ParamName, Is.EqualTo("nodes"));
Assert.That(Assert.Throws<ArgumentNullException>(() =>
((NodeStateCollection)null!).AddOpcUaMachineVisionAMCM(context))!.ParamName, Is.EqualTo("nodes"));
Assert.That(Assert.Throws<ArgumentNullException>(() =>
nodes.AddOpcUaMachineVision(null!))!.ParamName, Is.EqualTo("context"));
Assert.That(Assert.Throws<ArgumentNullException>(() =>
nodes.AddOpcUaMachineVisionAMCM(null!))!.ParamName, Is.EqualTo("context"));
Assert.That(Assert.Throws<ArgumentNullException>(() =>
((IEncodeableFactoryBuilder)null!).AddOpcUaMachineVision())!.ParamName, Is.EqualTo("builder"));
Assert.That(Assert.Throws<ArgumentNullException>(() =>
((IEncodeableFactoryBuilder)null!).AddOpcUaMachineVisionAMCM())!.ParamName, Is.EqualTo("builder"));
Assert.That(Assert.Throws<ArgumentNullException>(() =>
((INodeStateFactoryBuilder)null!).AddOpcUaMachineVision())!.ParamName, Is.EqualTo("builder"));
Assert.That(Assert.Throws<ArgumentNullException>(() =>
((INodeStateFactoryBuilder)null!).AddOpcUaMachineVisionAMCM())!.ParamName, Is.EqualTo("builder"));
});
}
/// <summary>
/// Existing hosting remains opt-in and invalid model selections are rejected.
/// </summary>
@@ -70,7 +70,7 @@ namespace Opc.Ua.Vision.Tests
.AddServer(options => options.ApplicationName = "MachineVisionRegistration")
.AddMachinery(options => options.Parts = MachineryParts.BuildingBlocks);
Assert.Throws<InvalidOperationException>(() => builder.AddVision(
options => options.MachineVisionParts = MachineVisionParts.Assets).AddVisionMachinery());
options => options.MachineVisionParts = MachineVisionParts.Assets).AddMachineVision());
}
[Test]
@@ -50,7 +50,7 @@ namespace Opc.Ua.Vision.Tests
var services = new ServiceCollection();
IOpcUaServerBuilder builder = services.AddOpcUa().AddServer(
options => options.ApplicationName = "VisionModuleTests").AddVision();
builder.AddVisionMachinery().AddVisionMachinery();
builder.AddMachineVision().AddMachineVision();
using ServiceProvider provider = services.BuildServiceProvider();
Assert.That(provider.GetRequiredService<IOptions<VisionServerOptions>>().Value.MachineVisionParts,
Is.EqualTo(MachineVisionParts.Assets));
@@ -67,12 +67,12 @@ namespace Opc.Ua.Vision.Tests
var services = new ServiceCollection();
IOpcUaServerBuilder builder = services.AddOpcUa().AddServer(
options => options.ApplicationName = "VisionModuleTests");
Assert.Throws<System.InvalidOperationException>(() => builder.AddVisionMachinery());
Assert.Throws<System.InvalidOperationException>(() => builder.AddMachineVision());
builder.AddVision();
services.AddSingleton(new DiAddressSpaceOwnership("OtherOwner"));
Assert.Throws<System.InvalidOperationException>(() => builder.AddVisionMachinery());
Assert.Throws<System.InvalidOperationException>(() => builder.AddMachineVision());
Assert.Throws<System.ArgumentNullException>(() =>
OpcUaServerVisionMachineryExtensions.AddVisionMachinery(null!));
OpcUaServerVisionMachineryExtensions.AddMachineVision(null!));
Assert.Throws<System.ArgumentNullException>(() =>
OpcUaServerVisionMachineryExtensions.AddVisionToMachinery(null!));
}