mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-18 14:43:08 +08:00
0de25de89a
This pull request updates the Device Integration documentation and the `PumpDeviceIntegrationServer` sample to introduce and demonstrate the new `ITopologyElementBuilder<TElement>` API for working with OPC UA Device Integration topology elements. The changes clarify the distinction between device and topology-element builders, update sample code to use the new builder, and improve the overall clarity and accuracy of both the documentation and the sample implementation. **API and Documentation Updates:** * Introduced `ITopologyElementBuilder<TElement>` as the unified builder for topology elements, exposing common operations such as functional groups and topology references, and updated all references and examples to use this builder where appropriate. [[1]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaR164-R173) [[2]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaR219-R228) [[3]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL897-R926) [[4]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL912-R943) [[5]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL926-R958) * Clarified the distinction between device builders (`IDeviceBuilder<TDevice>`) and topology-element builders in both the documentation and sample code, and documented the available builder methods and their intended use. [[1]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL33-R38) [[2]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL141-R146) [[3]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaR495-R500) **Sample Code Modernization:** * Refactored `samples/PumpDeviceIntegrationServer/Program.cs` to use the new `ITopologyElementBuilder<TElement>` API for creating and configuring the second pump, including setting identification properties and functional groups, and removed legacy code for device health and software update facets. [[1]](diffhunk://#diff-7e065631de4a55cb923ba4fa9a32fffcff804ce5ef3e09a70a2eead76425b18dL74-R98) [[2]](diffhunk://#diff-7e065631de4a55cb923ba4fa9a32fffcff804ce5ef3e09a70a2eead76425b18dL133-L139) [[3]](diffhunk://#diff-7e065631de4a55cb923ba4fa9a32fffcff804ce5ef3e09a70a2eead76425b18dL53-L57) [[4]](diffhunk://#diff-7e065631de4a55cb923ba4fa9a32fffcff804ce5ef3e09a70a2eead76425b18dL37-R37) * Cleaned up and modernized the sample project description and removed unused or outdated fields and comments. [[1]](diffhunk://#diff-e3976eed0bf7d7b96d6762d58e8101a0670b2ef60aa04609e8d5ff4be30f64beL8-R8) [[2]](diffhunk://#diff-70693dd3c39308e8e25d4e009281950f860a914afcf92b2e100a0710b5bb8db1L58-L76) [[3]](diffhunk://#diff-70693dd3c39308e8e25d4e009281950f860a914afcf92b2e100a0710b5bb8db1L110-R91) [[4]](diffhunk://#diff-70693dd3c39308e8e25d4e009281950f860a914afcf92b2e100a0710b5bb8db1L129-R110) **Software Update Example Improvements:** * Updated the software update walkthrough in the documentation to use the new builder APIs and improved the clarity of the example, including more accurate references to the simulated device and software update facet. [[1]](diffhunk://#diff-5342d39e79e59ca4fc77a9d6277377841c321d17476979f612c0487c53342238L300-R303) [[2]](diffhunk://#diff-5342d39e79e59ca4fc77a9d6277377841c321d17476979f612c0487c53342238L313-R337) [[3]](diffhunk://#diff-5342d39e79e59ca4fc77a9d6277377841c321d17476979f612c0487c53342238L378-L379) These changes make it easier to work with OPC UA Device Integration topology elements, provide clearer guidance in the documentation, and ensure the sample code reflects current best practices.# Description _Describe the changes here to communicate to the maintainers why they should accept this pull request. By default - this will become the Commit message after merging and thus define history._ ## Related Issues _Reference all GitHub issues this PR addresses. If there is no issue yet, open one and link it here._ _If this is a relatively large or complex change, a design must have been discussed in the related tracking issue and signed off (which becomes the Architectural Decision Record (ADR))._ - Fixes #github-issue-number, ... ## Checklist _Put an `x` in the boxes that apply. You can complete these step by step after opening the PR._ - [x] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [ ] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. --------- Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@hotmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
120 lines
5.4 KiB
C#
120 lines
5.4 KiB
C#
/* ========================================================================
|
|
* Copyright (c) 2005-2025 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;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
using Opc.Ua;
|
|
using Opc.Ua.Di;
|
|
using Opc.Ua.Di.Server.Builders;
|
|
using Opc.Ua.Pumps;
|
|
using Opc.Ua.Server.Fluent;
|
|
using Pumps;
|
|
|
|
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
|
|
|
|
builder.Logging.ClearProviders();
|
|
builder.Logging.AddConsole();
|
|
|
|
int port = int.TryParse(builder.Configuration["port"], out int p) ? p : 62542;
|
|
|
|
// Bind host for the OPC UA endpoint. Defaults to 0.0.0.0 so the server is
|
|
// reachable from outside a container; override with --host / host env var
|
|
// (e.g. "localhost" for local-only development).
|
|
string host = builder.Configuration["host"] is { Length: > 0 } h ? h : "0.0.0.0";
|
|
|
|
builder.Services
|
|
.AddOpcUa()
|
|
.AddServer(o =>
|
|
{
|
|
o.ApplicationName = "PumpDeviceIntegrationServer";
|
|
o.ApplicationUri = "urn:localhost:OPCFoundation:PumpDeviceIntegrationServer";
|
|
o.ProductUri = "uri:opcfoundation.org:PumpDeviceIntegrationServer";
|
|
o.AutoAcceptUntrustedCertificates = true;
|
|
o.EndpointUrls.Add($"opc.tcp://{host}:{port}/PumpDeviceIntegrationServer");
|
|
})
|
|
.AddNodeManager<PumpNodeManagerFactory>()
|
|
// Materialise a second pump declaratively at server startup. The
|
|
// runner runs the delegate after the pump address space and
|
|
// fluent wiring are complete.
|
|
.ConfigureDevicesFor<PumpNodeManager>(async ctx =>
|
|
{
|
|
var manager = (PumpNodeManager)ctx.Manager;
|
|
ushort pumpsNamespaceIndex = (ushort)manager.Server.NamespaceUris.GetIndex(
|
|
Opc.Ua.Pumps.Namespaces.Pumps);
|
|
PumpState pumpState = await manager.CreatePumpAsync(
|
|
new QualifiedName("Pump #2", pumpsNamespaceIndex),
|
|
ctx.CancellationToken)
|
|
.ConfigureAwait(false);
|
|
ITopologyElementBuilder<PumpState> pump =
|
|
ctx.TopologyElement<PumpState>(pumpState.NodeId);
|
|
|
|
ushort diNamespaceIndex = ctx.Manager.DiNamespaceIndex;
|
|
pump.WithIdentificationGroup(id => id.Configure(node =>
|
|
node.WithProperty(
|
|
new QualifiedName("Manufacturer", diNamespaceIndex),
|
|
Variant.From(new LocalizedText("Acme Pumps Inc.")))
|
|
.WithProperty(
|
|
new QualifiedName("Model", diNamespaceIndex),
|
|
Variant.From(new LocalizedText("PumpX-2000 (declarative)")))
|
|
.WithProperty(
|
|
new QualifiedName("SerialNumber", diNamespaceIndex),
|
|
Variant.From("SN-DI-2"))
|
|
.WithProperty(
|
|
new QualifiedName("DeviceClass", diNamespaceIndex),
|
|
Variant.From("Pump"))
|
|
.WithProperty(
|
|
new QualifiedName("HardwareRevision", diNamespaceIndex),
|
|
Variant.From("1.0"))
|
|
.WithProperty(
|
|
new QualifiedName("SoftwareRevision", diNamespaceIndex),
|
|
Variant.From("2.5.3"))));
|
|
|
|
// Demonstrate the non-typed WithFunctionalGroup(QualifiedName)
|
|
// builder for ad-hoc groups not covered by the 8 well-known
|
|
// DI typed extensions (WithMaintenanceGroup, WithOperationalGroup,
|
|
// ...). Pump #2 exposes a custom "Diagnostics" group that
|
|
// surfaces a few operational signals as plain properties so
|
|
// clients get a single browsable folder without having to chase
|
|
// the supervision alarm tree.
|
|
//
|
|
// WithProperty creates each property on the freshly built group
|
|
// (read-only by default); LastError is made writable via the
|
|
// fluent Writable() helper.
|
|
pump.WithFunctionalGroup(
|
|
new QualifiedName("Diagnostics", ctx.Manager.DiNamespaceIndex),
|
|
fg => fg.Configure(node =>
|
|
node.WithProperty("LastError", Variant.From(string.Empty), p => p.Writable())
|
|
.WithProperty("ErrorCount", 0)
|
|
.WithProperty("LastSelfTest", (DateTimeUtc)DateTime.UtcNow)));
|
|
});
|
|
|
|
await builder.Build().RunAsync().ConfigureAwait(false);
|