Skip to content
48 changes: 48 additions & 0 deletions Applications/ConsoleReferenceServer/IUAServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Opc.Ua;
using Opc.Ua.Configuration;
using Opc.Ua.Server;

namespace Quickstarts
{
public interface IUAServer<T> where T : IStandardServer
{
IApplicationInstance Application { get; }

ApplicationConfiguration Configuration { get; }

bool AutoAccept { get; set; }

string Password { get; set; }

ExitCode ExitCode { get; }

T Server { get; }

/// <summary>
/// Load the application configuration.
/// </summary>
Task LoadAsync(string applicationName, string configSectionName);

/// <summary>
/// Load the application configuration.
/// </summary>
Task CheckCertificateAsync(bool renewCertificate);

/// <summary>
/// Create server instance and add node managers.
/// </summary>
void Create(IList<INodeManagerFactory> nodeManagerFactories);

/// <summary>
/// Start the server.
/// </summary>
Task StartAsync();

/// <summary>
/// Stops the server.
/// </summary>
Task StopAsync();
}
}
25 changes: 18 additions & 7 deletions Applications/ConsoleReferenceServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2005-2020 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
Expand All @@ -11,7 +11,7 @@
* 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,
Expand All @@ -32,8 +32,11 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Configuration;
using Opc.Ua.Server;

namespace Quickstarts.ReferenceServer
{
Expand Down Expand Up @@ -81,21 +84,29 @@ public static async Task<int> Main(string[] args)
{ "ctt", "CTT mode, use to preset alarms for CTT testing.", c => cttMode = c != null },
};

IServiceCollection services = new ServiceCollection()
.AddConfigurationServices()
.AddServerServices()
.AddScoped<IReferenceServer, ReferenceServer>()
.AddScoped(sp => new LogWriter())
.AddScoped<IUAServer<IReferenceServer>, UAServer<IReferenceServer>>();

IServiceProvider serviceProvider = services.BuildServiceProvider();

try
{
// parse command line and set options
ConsoleUtils.ProcessCommandLine(output, args, options, ref showHelp, "REFSERVER");

if (logConsole && appLog)
{
output = new LogWriter();
output = serviceProvider.GetRequiredService<LogWriter>();
}

// create the UA server
var server = new UAServer<ReferenceServer>(output) {
AutoAccept = autoAccept,
Password = password
};
IUAServer<IReferenceServer> server = serviceProvider.GetRequiredService<IUAServer<IReferenceServer>>();
server.AutoAccept = autoAccept;
server.Password = password;

// load the server configuration, validate certificates
output.WriteLine("Loading configuration from {0}.", configSectionName);
Expand Down
56 changes: 28 additions & 28 deletions Applications/ConsoleReferenceServer/UAServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2005-2020 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
Expand All @@ -11,7 +11,7 @@
* 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,
Expand Down Expand Up @@ -39,9 +39,25 @@

namespace Quickstarts
{
public class UAServer<T> where T : StandardServer, new()
public class UAServer<T> : IUAServer<T> where T : IStandardServer
{
public ApplicationInstance Application => m_application;
/// <summary>
/// Constructor of the server.
/// </summary>
/// <param name="applicationInstance">Application instance.</param>
/// <param name="server">Server of the specified generic type.</param>
/// <param name="writer">The text output.</param>
public UAServer(
IApplicationInstance applicationInstance,
T server,
LogWriter writer)
{
m_application = applicationInstance;
m_server = server;
m_output = writer;
}

public IApplicationInstance Application => m_application;
public ApplicationConfiguration Configuration => m_application.ApplicationConfiguration;

public bool AutoAccept { get; set; }
Expand All @@ -50,15 +66,6 @@ namespace Quickstarts
public ExitCode ExitCode { get; private set; }
public T Server => m_server;

/// <summary>
/// Ctor of the server.
/// </summary>
/// <param name="writer">The text output.</param>
public UAServer(TextWriter writer)
{
m_output = writer;
}

/// <summary>
/// Load the application configuration.
/// </summary>
Expand All @@ -69,13 +76,12 @@ public async Task LoadAsync(string applicationName, string configSectionName)
ExitCode = ExitCode.ErrorNotStarted;

ApplicationInstance.MessageDlg = new ApplicationMessageDlg(m_output);
CertificatePasswordProvider PasswordProvider = new CertificatePasswordProvider(Password);
m_application = new ApplicationInstance {
ApplicationName = applicationName,
ApplicationType = ApplicationType.Server,
ConfigSectionName = configSectionName,
CertificatePasswordProvider = PasswordProvider
};
var passwordProvider = new CertificatePasswordProvider(Password);
Copy link
Contributor

Choose a reason for hiding this comment

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

previously LoadAsync created a new application instance, now it doesnt.

I need to check...

Copy link
Author

Choose a reason for hiding this comment

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

Yes, now this object comes from the DI. This was the intension.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah but if you call load twice previously a new instance was created now the Same is reused. I need to Check If this could cause unwanted behaviour changes

Copy link
Author

@wbtoms wbtoms Mar 14, 2025

Choose a reason for hiding this comment

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

I see, for this shadow config use case, right? I think this works becauce LoadApplicationConfiguration sets the application configuration to null before relaoding it so it is always a complete new instance of the configuration.

But what is the shadow configuration use case for excatly? do you know?


m_application.ApplicationName = applicationName;
m_application.ApplicationType = ApplicationType.Server;
m_application.ConfigSectionName = configSectionName;
m_application.CertificatePasswordProvider = passwordProvider;

// load the application configuration.
await m_application.LoadApplicationConfiguration(false).ConfigureAwait(false);
Expand Down Expand Up @@ -125,8 +131,6 @@ public void Create(IList<INodeManagerFactory> nodeManagerFactories)
{
try
{
// create the server.
m_server = new T();
if (nodeManagerFactories != null)
{
foreach (var factory in nodeManagerFactories)
Expand All @@ -148,9 +152,6 @@ public async Task StartAsync()
{
try
{
// create the server.
m_server = m_server ?? new T();

// start the server
await m_application.Start(m_server).ConfigureAwait(false);

Expand Down Expand Up @@ -190,7 +191,6 @@ public async Task StopAsync()
using (T server = m_server)
{
// Stop status thread
m_server = null;
await m_status.ConfigureAwait(false);

// Stop server and dispose
Expand Down Expand Up @@ -281,8 +281,8 @@ private async Task StatusThreadAsync()

#region Private Members
private readonly TextWriter m_output;
private ApplicationInstance m_application;
private T m_server;
private readonly T m_server;
private readonly IApplicationInstance m_application;
private Task m_status;
private DateTime m_lastEventTime;
#endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* ========================================================================
* Copyright (c) 2005-2020 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 Opc.Ua;
using Opc.Ua.Server;

namespace Quickstarts.ReferenceServer
{
public interface IReferenceServer : IReverseConnectServer
{
ITokenValidator TokenValidator { get; set; }

/// <summary>
/// Creates an instance of the service host.
/// </summary>
ServiceHost CreateServiceHost(ServerBase server, params Uri[] addresses);
}
}
43 changes: 30 additions & 13 deletions Applications/Quickstarts.Servers/ReferenceServer/ReferenceServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2005-2020 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
Expand All @@ -11,7 +11,7 @@
* 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,
Expand All @@ -32,6 +32,7 @@
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Opc.Ua;
using Opc.Ua.Configuration;
using Opc.Ua.Server;

namespace Quickstarts.ReferenceServer
Expand All @@ -43,12 +44,23 @@ namespace Quickstarts.ReferenceServer
/// Each server instance must have one instance of a StandardServer object which is
/// responsible for reading the configuration file, creating the endpoints and dispatching
/// incoming requests to the appropriate handler.
///
///
/// This sub-class specifies non-configurable metadata such as Product Name and initializes
/// the EmptyNodeManager which provides access to the data exposed by the Server.
/// </remarks>
public partial class ReferenceServer : ReverseConnectServer
public partial class ReferenceServer : ReverseConnectServer, IReferenceServer
{
public ReferenceServer(
IApplicationInstance applicationInstance,
IServerInternal serverInternal,
IMainNodeManagerFactory mainNodeManagerFactory)
: base(applicationInstance, serverInternal, mainNodeManagerFactory)
{
m_applicationInstance = applicationInstance;
m_serverInternal = serverInternal;
m_mainNodeManagerFactory = mainNodeManagerFactory;
}

#region Properties
public ITokenValidator TokenValidator { get; set; }

Expand All @@ -63,7 +75,9 @@ public partial class ReferenceServer : ReverseConnectServer
/// always creates a CoreNodeManager which handles the built-in nodes defined by the specification.
/// Any additional NodeManagers are expected to handle application specific nodes.
/// </remarks>
protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
protected override IMasterNodeManager CreateMasterNodeManager(
IServerInternal server,
ApplicationConfiguration configuration)
{
Utils.LogInfo(Utils.TraceMasks.StartStop, "Creating the Reference Server Node Manager.");

Expand All @@ -78,7 +92,7 @@ protected override MasterNodeManager CreateMasterNodeManager(IServerInternal ser
}

// create master node manager.
return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray());
return m_mainNodeManagerFactory.CreateMasterNodeManager(null, nodeManagers.ToArray());
}

protected override IMonitoredItemQueueFactory CreateMonitoredItemQueueFactory(IServerInternal server, ApplicationConfiguration configuration)
Expand Down Expand Up @@ -136,7 +150,7 @@ protected override ResourceManager CreateResourceManager(IServerInternal server,
/// Initializes the server before it starts up.
/// </summary>
/// <remarks>
/// This method is called before any startup processing occurs. The sub-class may update the
/// This method is called before any startup processing occurs. The sub-class may update the
/// configuration object or do any other application specific startup tasks.
/// </remarks>
protected override void OnServerStarting(ApplicationConfiguration configuration)
Expand All @@ -162,11 +176,10 @@ protected override void OnServerStarted(IServerInternal server)

try
{
lock (ServerInternal.Status.Lock)
{
// allow a faster sampling interval for CurrentTime node.
ServerInternal.Status.Variable.CurrentTime.MinimumSamplingInterval = 250;
}
ServerInternal.UpdateServerStatus(
(serverStatus) => {
serverStatus.Variable.CurrentTime.MinimumSamplingInterval = 250;
});
}
catch
{ }
Expand Down Expand Up @@ -416,7 +429,7 @@ private IUserIdentity VerifyIssuedToken(IssuedIdentityToken issuedToken)
info = new TranslationInfo("IssuedTokenInvalid", "en-US", "token is an invalid issued token.");
result = StatusCodes.BadIdentityTokenInvalid;
}
else // Rejected
else // Rejected
{
// construct translation object with default text.
info = new TranslationInfo("IssuedTokenRejected", "en-US", "token is rejected.");
Expand All @@ -434,6 +447,10 @@ private IUserIdentity VerifyIssuedToken(IssuedIdentityToken issuedToken)

#region Private Fields
private ICertificateValidator m_userCertificateValidator;
private readonly IMainNodeManagerFactory m_mainNodeManagerFactory;
private readonly IServerInternal m_serverInternal;
private readonly IApplicationInstance m_applicationInstance;

#endregion
}
}
Loading
Loading