.NET Based OPC UA Client/Server SDK  3.2.0.519
Lesson 1: Setting up a Basic OPC UA Server Console Application

The first lesson starts with an empty OPC UA server describing the code for integration of an OPC UA server component into a .NET application and the different server configuration options.

Create an Empty Server

The product specific information integration must be registered with the OPC UA server SDK. This is done by deriving a project specific class form the SDK class ServerManager. In the first step this derived class GettingStartedServerManager in the file GettingStartedServerManager.cs is empty.

internal class GettingStartedServerManager : ServerManager
{
}

The main integration code for adding the OPC UA server to the console application can be found in Program.cs.

The license for the server SDK must be loaded first from the embedded resource.

To start the server an instance of the GettingStartedServerManager is created and passed to the start method of the default application instance object.

The configuration of the server is done through the App.config file for the .NET Framework version of the sdk. For the .NET Core and .NET version of the sdk, the configuration is set in code. The configuration options are described later in this lesson.

public static void Main(string[] args)
{
try
{
System.Reflection.Assembly assembly;
#if NETFRAMEWORK
assembly = System.Reflection.Assembly.GetExecutingAssembly();
#else
assembly = PlatformUtils.GetAssembly(typeof(Program));
#endif
// The license file must be loaded from an embedded resource.
ApplicationLicenseManager.AddProcessLicenses(assembly, "License.lic");
// Start the server.
Console.WriteLine("Starting Server.");
GettingStartedServerManager server = new GettingStartedServerManager();
//***********************************************************************
// The following function can be called to configure the server from code
// This will disable the configuration settings from app.config file
//ConfigureOpcUaApplicationFromCode();
//***********************************************************************
ApplicationInstanceBase application;
#if NETFRAMEWORK
application = ApplicationInstance.Default;
// Setting the SecurityProvider is not required to be able to create certificates,
// since WindowsSecurityProvider is created implicitly by ApplicationInstance class.
#else
application = ApplicationInstanceBase.Default;
ConfigureOpcUaApplicationFromCode();
// Setting the SecurityProvider is required to be able to create certificates.
application.SecurityProvider = new BouncyCastleSecurityProvider();
#endif
application.AutoCreateCertificate = true;
application.UntrustedCertificate += Application_UntrustedCertificate;
// Set event handler for modifying the ApplicationSettings after loading.
application.ApplicationSettingsLoaded += Application_ApplicationSettingsLoaded;
application.Start(server, null, server);
// Print endpoints for information.
PrintEndpoints(server);
// Block until the server exits.
Console.WriteLine("Press <enter> to exit the program.");
Console.ReadLine();
// Stop the server.
Console.WriteLine("Stopping Server.");
server.Stop();
}
catch (Exception e)
{
Console.WriteLine("ERROR: {0}", e.Message);
Console.WriteLine("Press <enter> to exit the program.");
Console.ReadLine();
}
}

The server can be started and a client is able to connect. All information in the server address space is defined by the OPC UA specification. The NamespaceArray contains only two namespaces, the OPC UA defined namespace and the local server namespace.

The method PrintEndpoints is used to print the available endpoints to the command line (just as an information for the server developer).

static void PrintEndpoints(GettingStartedServerManager server)
{
// print the endpoints.
Console.WriteLine(string.Empty);
Console.WriteLine("Listening at the following endpoints:");
foreach (EndpointDescription endpoint in server.Application.Endpoints)
{
StatusCode error = server.Application.GetEndpointStatus(endpoint);
Console.WriteLine(" {0}: Status={1}", endpoint, error.ToString(true));
}
Console.WriteLine(string.Empty);
}

Prepare Integration of Product Specific Information

A product specific NodeManager can be loaded by overwriting OnRootNodeManagerStarted in GettingStartedServerManager.cs.

protected override void OnRootNodeManagerStarted(RootNodeManager nodeManager)
{
Console.WriteLine("Creating Node Managers.");
Lesson01NodeManager lession1 = new Lesson01NodeManager(this);
lession1.Startup();
}

The class Lesson1NodeManager is derived from the BaseNodeManager class which forms the Toolkit API. It overloads the methods Startup and Shutdown.

In Lesson1NodeManager.cs, the namespace URI is set in Startup.

public override void Startup()
{
try
{
Console.WriteLine("Starting Lesson01NodeManager.");
base.Startup();
AddNamespaceUri("http://yourorganisation.com/lesson01/");
// TBD
}

The server NamespaceArray does now contain a third namespace.

Options for Loading Configuration Settings

The configuration settings are managed by the class ApplicationInstanceBase. This class provides options to load the settings from XML in addition. The settings have to be set in code.

The Base Library Overview provides more details regarding configuration with Application Instance Base or Application Instance, the Configuration Schema for the configuration options and the Installation Process.

The file Program.cs of this lesson contains the following function ConfigureOpcUaApplicationFromCode with sample code to set the configuration settings from code before starting the application instance. The default option used in the server getting started lessons for .NET framework is the App.config file. For .Net Core and .Net ConfigureOpcUaApplicationFromCode() can be called instead before starting the application instance.

The standard configuration options can be set directly on the class SecuredApplication.

Additional configuration options like Trace Settings, Server Settings, User Identity Settings, Session Settings and Subscription Settings can be set as extension to the SecuredApplication.

This code requires an assembly reference to System.Runtime.Serialization.

static void ConfigureOpcUaApplicationFromCode()
{
// fill in the application settings in code
// The configuration settings are typically provided by another module
// of the application or loaded from a data base. In this example the
// settings are hardcoded
var application = new ConfigurationInMemory();
string enviromentPath = "%CommonApplicationData%";
// override path for net core on non windows platform
if (!PlatformUtils.IsWindows()) enviromentPath = "%LocalApplicationData%";
// ***********************************************************************
// standard configuration options
// general application identification settings
application.ApplicationName = "UnifiedAutomation GettingStartedServer";
application.ApplicationUri = "urn:localhost:UnifiedAutomation:GettingStartedServer";
application.ApplicationType = UnifiedAutomation.UaSchema.ApplicationType.Server_0;
application.ProductName = "UnifiedAutomation GettingStartedServer";
// configure application certificate and paths to the certificate stores
application.SetSecurity(PlatformUtils.CombinePath(enviromentPath, "UnifiedAutomation", "pki"), "CN=GettingStartedServer/O=UnifiedAutomation/DC=localhost");
// configure endpoints
application.BaseAddresses = new UnifiedAutomation.UaSchema.ListOfBaseAddresses();
application.BaseAddresses.Add("opc.tcp://localhost:48030");
application.SecurityProfiles = new ListOfSecurityProfiles();
application.SecurityProfiles.Add(new SecurityProfile() { ProfileUri = SecurityProfiles.Basic256Sha256, Enabled = true });
application.SecurityProfiles.Add(new SecurityProfile() { ProfileUri = SecurityProfiles.Aes128Sha256RsaOaep, Enabled = true });
application.SecurityProfiles.Add(new SecurityProfile() { ProfileUri = SecurityProfiles.Aes256Sha256RsaPss, Enabled = true });
// This SecurityProfile is enabled for testing purposes. It shall NOT be enabled in end user products.
application.SecurityProfiles.Add(new SecurityProfile() { ProfileUri = SecurityProfiles.None, Enabled = true });
// ***********************************************************************
// ***********************************************************************
// extended configuration options
// trace settings
TraceSettings trace = new TraceSettings();
trace.MasterTraceEnabled = false;
trace.DefaultTraceLevel = UnifiedAutomation.UaSchema.TraceLevel.Info;
trace.TraceFile = PlatformUtils.CombinePath(enviromentPath, "UnifiedAutomation", "Logs", FilePathUtils.MakeValidFileName(application.ApplicationName) + ".log.txt");
trace.MaxLogFileBackups = 3;
trace.ModuleSettings = new ModuleTraceSettings[]
{
new ModuleTraceSettings() { ModuleName = "UnifiedAutomation.Stack", TraceEnabled = true },
new ModuleTraceSettings() { ModuleName = "UnifiedAutomation.Server", TraceEnabled = true },
};
application.Set<TraceSettings>(trace);
// Installation settings
InstallationSettings installation = new InstallationSettings();
installation.GenerateCertificateIfNone = true;
installation.DeleteCertificateOnUninstall = true;
application.Set<InstallationSettings>(installation);
application.ServerSettings = new ServerSettings()
{
ProductName = "UnifiedAutomation GettingStartedServer",
DiscoveryRegistration = new DiscoveryRegistrationSettings()
{
Enabled = false
}
};
// ***********************************************************************
// set the configuration for the application (must be called before start to have any effect).
// these settings are discarded if the /configFile flag is specified on the command line.
ApplicationInstanceBase.Default.SetApplicationSettings(application);
}