Update da GitHub del progetto UA-NetStd

This commit is contained in:
Samuele Locatelli
2022-03-30 11:30:34 +02:00
parent 3890b7a311
commit bbc5cc04ad
588 changed files with 295880 additions and 100006 deletions
+3
View File
@@ -327,3 +327,6 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
.fake
.ionide
+48 -2
View File
@@ -46,7 +46,7 @@ csharp_style_conditional_delegate_call =
# Modifier preferences
# require braces to be on a new line for control_blocks, types, and methods (also known as "Allman" style)
csharp_new_line_before_open_brace = control_blocks, types, methods, properties, accessors
csharp_new_line_before_open_brace = control_blocks, types, methods, properties, accessors, object_collection
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async : suggestion
csharp_new_line_before_catch = true
csharp_new_line_before_else = true : suggestion
@@ -111,13 +111,16 @@ csharp_style_expression_bodied_accessors =
csharp_style_expression_bodied_constructors = false : suggestion
csharp_style_expression_bodied_methods = false : suggestion
csharp_style_expression_bodied_properties = true : silent
csharp_style_expression_bodied_lambdas = true : silent
csharp_style_expression_bodied_local_functions = false : silent
#Style - expression level options
csharp_prefer_braces = true : silent
csharp_style_deconstructed_variable_declaration = true : suggestion
csharp_prefer_simple_default_expression = true : suggestion
csharp_style_inlined_variable_declaration = false : suggestion
dotnet_style_predefined_type_for_member_access = false : suggestion
csharp_prefer_simple_using_statement = false : suggestion
#Style - implicit and explicit types
csharp_style_var_for_built_in_types = false : silent
@@ -129,6 +132,7 @@ csharp_style_var_elsewhere =
#prefer the language keyword for local variables, method parameters, and class members,
#instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_locals_parameters_members = true : suggestion
dotnet_style_predefined_type_for_member_access = true : suggestion
#Style - qualification options
dotnet_style_qualification_for_event = false : suggestion
@@ -159,6 +163,10 @@ dotnet_style_prefer_auto_properties =
dotnet_style_prefer_conditional_expression_over_assignment = true : suggestion
dotnet_style_prefer_conditional_expression_over_return = true : suggestion
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace : silent
csharp_style_namespace_declarations = block_scoped : silent
# Naming rules
# Private Constants are PascalCase and start with k
@@ -229,6 +237,17 @@ dotnet_naming_symbols.instance_fields.applicable_accessibilities =
dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = m_
# Private event fields are PascalCase and start with m_
dotnet_naming_rule.instance_events_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.instance_events_should_be_pascal_case.symbols = instance_events
dotnet_naming_rule.instance_events_should_be_pascal_case.style = instance_events_style
dotnet_naming_symbols.instance_events.applicable_kinds = event
dotnet_naming_symbols.instance_events.applicable_accessibilities = private, protected, private_protected, protected_friend
dotnet_naming_style.instance_events_style.capitalization = pascal_case
dotnet_naming_style.instance_events_style.required_prefix = m_
# Locals and parameters are camelCase
dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
@@ -256,3 +275,30 @@ dotnet_naming_symbols.all_members.applicable_kinds =
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# Diagnostic settings (windows only)
dotnet_analyzer_diagnostic.category-style.severity = suggestion
dotnet_analyzer_diagnostic.category-globalization.severity = silent
dotnet_analyzer_diagnostic.category-design.severity = suggestion
dotnet_analyzer_diagnostic.category-reliability.severity = warning
dotnet_analyzer_diagnostic.category-performance.severity = warning
dotnet_analyzer_diagnostic.category-security.severity = warning
# Suggest only non breaking warnings
# CA1805: Do not initialize unnecessarily.
dotnet_diagnostic.CA1805.severity = silent
dotnet_code_quality.CA1805.api_surface = private, internal
# CA1822: Mark members as static.
dotnet_diagnostic.CA1822.severity = suggestion
dotnet_code_quality.CA1822.api_surface = private, internal
# CA3075: Insecure DTD processing in XML
dotnet_diagnostic.CA3075.severity = error
dotnet_diagnostic.CA3077.severity = error
# IDE0049: Simplify Names
dotnet_diagnostic.IDE0049.severity = silent
# CA1507: Use nameof in place of string
dotnet_diagnostic.CA1507.severity = warning
+12
View File
@@ -26,6 +26,9 @@ bld/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Rider cache/options directory
.idea/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
@@ -67,6 +70,7 @@ artifacts/
*.pidb
*.svclog
*.scc
*.nettrace
# Chutzpah Test files
@@ -122,6 +126,10 @@ AutoTest.Net/
# Installshield output folder
[Ee]xpress/
# CodeCoverage
CodeCoverage/
TestResults/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
@@ -234,6 +242,10 @@ _Pvt_Extensions
# FAKE - F# Make
.fake/
# BenchmarkDotNet Artifacts
BenchmarkDotNet.Artifacts
# OPC UA Sample app certificate stores
OPC\ Foundation/
*.der
@@ -443,7 +443,7 @@ namespace Opc.Ua.Client.Controls
}
catch (Exception exception)
{
Utils.Trace(exception, "Error loading image.");
Utils.LogError(exception, "Error loading image.");
}
}
@@ -60,10 +60,6 @@ namespace Opc.Ua.Client.Controls
this.ConnectBTN = new System.Windows.Forms.Button();
this.UseSecurityCK = new System.Windows.Forms.CheckBox();
this.UrlCB = new System.Windows.Forms.ComboBox();
this.userName = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.passwd = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// ConnectBTN
@@ -80,11 +76,12 @@ namespace Opc.Ua.Client.Controls
// UseSecurityCK
//
this.UseSecurityCK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.UseSecurityCK.AutoSize = true;
this.UseSecurityCK.Checked = true;
this.UseSecurityCK.CheckState = System.Windows.Forms.CheckState.Checked;
this.UseSecurityCK.Location = new System.Drawing.Point(346, 3);
this.UseSecurityCK.Location = new System.Drawing.Point(335, 3);
this.UseSecurityCK.Name = "UseSecurityCK";
this.UseSecurityCK.Size = new System.Drawing.Size(91, 17);
this.UseSecurityCK.Size = new System.Drawing.Size(86, 17);
this.UseSecurityCK.TabIndex = 1;
this.UseSecurityCK.Text = "Use Security";
this.UseSecurityCK.UseVisualStyleBackColor = true;
@@ -92,57 +89,17 @@ namespace Opc.Ua.Client.Controls
// UrlCB
//
this.UrlCB.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
| System.Windows.Forms.AnchorStyles.Right)));
this.UrlCB.FormattingEnabled = true;
this.UrlCB.Location = new System.Drawing.Point(0, 1);
this.UrlCB.Name = "UrlCB";
this.UrlCB.Size = new System.Drawing.Size(168, 21);
this.UrlCB.Size = new System.Drawing.Size(327, 21);
this.UrlCB.TabIndex = 0;
//
// userName
//
this.userName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.userName.Location = new System.Drawing.Point(187, 2);
this.userName.Name = "userName";
this.userName.Size = new System.Drawing.Size(71, 20);
this.userName.TabIndex = 3;
//
// label1
//
this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(171, 5);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(15, 13);
this.label1.TabIndex = 4;
this.label1.Text = "U";
//
// label2
//
this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(262, 5);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(14, 13);
this.label2.TabIndex = 6;
this.label2.Text = "P";
//
// passwd
//
this.passwd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.passwd.Location = new System.Drawing.Point(278, 2);
this.passwd.Name = "passwd";
this.passwd.Size = new System.Drawing.Size(62, 20);
this.passwd.TabIndex = 5;
//
// ConnectServerCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.label2);
this.Controls.Add(this.passwd);
this.Controls.Add(this.label1);
this.Controls.Add(this.userName);
this.Controls.Add(this.ConnectBTN);
this.Controls.Add(this.UseSecurityCK);
this.Controls.Add(this.UrlCB);
@@ -160,9 +117,5 @@ namespace Opc.Ua.Client.Controls
private System.Windows.Forms.Button ConnectBTN;
private System.Windows.Forms.CheckBox UseSecurityCK;
private System.Windows.Forms.ComboBox UrlCB;
private System.Windows.Forms.TextBox userName;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox passwd;
}
}
@@ -333,7 +333,7 @@ namespace Opc.Ua.Client.Controls
catch (Exception e)
{
UpdateStatus(true, DateTime.Now, "Connected, failed to load complex type system.");
Utils.Trace(e, "Failed to load complex type system.");
Utils.LogWarning(e, "Failed to load complex type system.");
}
// return the new session.
@@ -369,7 +369,7 @@ namespace Opc.Ua.Client.Controls
InternalDisconnect();
// select the best endpoint.
var endpointDescription = CoreClientUtils.SelectEndpoint(serverUrl, useSecurity, DiscoverTimeout);
var endpointDescription = CoreClientUtils.SelectEndpoint(m_configuration, serverUrl, useSecurity, DiscoverTimeout);
var endpointConfiguration = EndpointConfiguration.Create(m_configuration);
var endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
@@ -398,7 +398,7 @@ namespace Opc.Ua.Client.Controls
catch (Exception e)
{
UpdateStatus(true, DateTime.Now, "Connected, failed to load complex type system.");
Utils.Trace(e, "Failed to load complex type system.");
Utils.LogError(e, "Failed to load complex type system.");
}
// return the new session.
@@ -565,7 +565,7 @@ namespace Opc.Ua.Client.Controls
}
// return the selected endpoint.
return CoreClientUtils.SelectEndpoint(discoveryUrl, UseSecurityCK.Checked, DiscoverTimeout);
return CoreClientUtils.SelectEndpoint(m_configuration, discoveryUrl, UseSecurityCK.Checked, DiscoverTimeout);
}
finally
{
@@ -668,11 +668,6 @@ namespace Opc.Ua.Client.Controls
/// </summary>
private async void Server_ConnectMI_Click(object sender, EventArgs e)
{
// se ho user/pwd uso quelli...
if (!string.IsNullOrEmpty(userName.Text) || !string.IsNullOrEmpty(passwd.Text))
{
UserIdentity = new UserIdentity(userName.Text, passwd.Text);
}
try
{
await ConnectAsync();
@@ -112,9 +112,9 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -217,7 +217,7 @@ namespace Opc.Ua.Client.Controls
}
catch (Exception e)
{
Utils.Trace(e, "Unexpected error discovering servers.");
Utils.LogError(e, "Unexpected error discovering servers.");
}
}
@@ -254,7 +254,7 @@ namespace Opc.Ua.Client.Controls
}
catch (Exception e)
{
Utils.Trace("Error retrieving FindServersOnNetwork paramters. Error=({1}){0}", e.Message, e.GetType());
Utils.LogError("Error retrieving FindServersOnNetwork parameters. Error=({1}){0}", e.Message, e.GetType());
return false;
}
@@ -265,7 +265,7 @@ namespace Opc.Ua.Client.Controls
}
catch (Exception e)
{
Utils.Trace("DISCOVERY ERROR - Could not fetch servers from url: {0}. Error=({2}){1}", discoveryUrl, e.Message, e.GetType());
Utils.LogError("DISCOVERY ERROR - Could not fetch servers from url: {0}. Error=({2}){1}", discoveryUrl, e.Message, e.GetType());
return false;
}
finally
@@ -119,7 +119,7 @@ namespace Opc.Ua.Client.Controls
}
catch (Exception e)
{
Utils.Trace(e, "Could not get ip addresses for host: {0}", hostname);
Utils.LogError(e, "Could not get ip addresses for host: {0}", hostname);
ThreadPool.QueueUserWorkItem(new WaitCallback(OnUpdateAddress), new object[] { listItem, e.Message });
}
}
@@ -198,7 +198,7 @@ namespace Opc.Ua.Client.Controls
foreach (string item in StorePathCB.Items)
{
if (String.Compare(storePath, item, StringComparison.OrdinalIgnoreCase) == 0)
if (String.Equals(storePath, item, StringComparison.OrdinalIgnoreCase))
{
found = true;
break;
@@ -265,7 +265,7 @@ namespace Opc.Ua.Client.Controls
for (int ii = 0; ii < StorePathCB.Items.Count; ii++)
{
if (String.Compare(storePath, StorePathCB.Items[ii] as string, StringComparison.OrdinalIgnoreCase) == 0)
if (String.Equals(storePath, StorePathCB.Items[ii] as string, StringComparison.OrdinalIgnoreCase))
{
StorePathCB.SelectedIndex = ii;
found = true;
@@ -1,26 +1,37 @@
using System;
using System;
using System.Windows.Forms;
namespace Opc.Ua.Client.Controls
{
public partial class HeaderBranding : UserControl
{
#region Public Constructors
public HeaderBranding()
{
InitializeComponent();
}
#endregion Public Constructors
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
try
{
System.Diagnostics.Process.Start(linkLabel1.Text);
}
catch
{
}
}
#region Private Methods
private void linkLabel1_Click(object sender, EventArgs e)
{
linkLabel1_LinkClicked(sender, null);
}
private void pictureBox2_Click(object sender, EventArgs e)
{
try
{
System.Diagnostics.Process.Start("http://www.steamware.net");
// System.Diagnostics.Process.Start("http://www.opcfoundation.org/certification");
}
catch
{
@@ -29,9 +40,7 @@ namespace Opc.Ua.Client.Controls
private void ServerHeaderBranding_Load(object sender, EventArgs e)
{
labelVersion.Text = this.Parent.Text;
appName.Text = this.Parent.Text;
}
#endregion Private Methods
}
}
@@ -31,10 +31,11 @@
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(HeaderBranding));
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.linkLabel1 = new System.Windows.Forms.LinkLabel();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.appName = new System.Windows.Forms.Label();
this.labelVersion = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
@@ -49,55 +50,72 @@
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
this.toolTip1.SetToolTip(this.pictureBox1, "Visit www.opcfoundation.org");
this.pictureBox1.Click += new System.EventHandler(this.pictureBox2_Click);
//
// linkLabel1
//
this.linkLabel1.AutoSize = true;
this.linkLabel1.BackColor = System.Drawing.Color.White;
this.linkLabel1.Location = new System.Drawing.Point(200, 22);
this.linkLabel1.Name = "linkLabel1";
this.linkLabel1.Size = new System.Drawing.Size(236, 25);
this.linkLabel1.TabIndex = 2;
this.linkLabel1.TabStop = true;
// this.linkLabel1.Text = "www.opcfoundation.org";
this.linkLabel1.Text = "www.steamware.net";
this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
this.linkLabel1.Click += new System.EventHandler(this.linkLabel1_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.BackColor = System.Drawing.Color.White;
this.label1.Font = new System.Drawing.Font("Arial", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.Location = new System.Drawing.Point(296, 37);
this.label1.Font = new System.Drawing.Font("Arial", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.Location = new System.Drawing.Point(200, 6);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(192, 15);
this.label1.Size = new System.Drawing.Size(389, 32);
this.label1.TabIndex = 3;
this.label1.Text = "OPC UA Technology Based Client";
// this.label1.Text = "OPC UA Technology Sample";
//
// label2
//
this.label2.BackColor = System.Drawing.Color.White;
this.label2.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.Location = new System.Drawing.Point(3, 74);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(230, 16);
this.label2.TabIndex = 4;
this.label2.Text = "Unified Architecture testing app";
// this.label2.Text = "Unified Architecture demonstration app";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// appName
//
this.appName.BackColor = System.Drawing.Color.White;
this.appName.Font = new System.Drawing.Font("Arial", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.appName.Location = new System.Drawing.Point(201, 7);
this.appName.Font = new System.Drawing.Font("Arial", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.appName.Location = new System.Drawing.Point(203, 44);
this.appName.Name = "appName";
this.appName.Size = new System.Drawing.Size(382, 27);
this.appName.Size = new System.Drawing.Size(306, 19);
this.appName.TabIndex = 8;
this.appName.Text = "OPC-UA Client Browser";
// this.appName.Text = "Sample Application";
this.appName.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// labelVersion
//
this.labelVersion.AutoSize = true;
this.labelVersion.BackColor = System.Drawing.Color.White;
this.labelVersion.Font = new System.Drawing.Font("Arial", 7F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.labelVersion.Location = new System.Drawing.Point(404, 60);
this.labelVersion.Name = "labelVersion";
this.labelVersion.Size = new System.Drawing.Size(166, 13);
this.labelVersion.TabIndex = 9;
this.labelVersion.Text = "OPC UA Technology Based Client";
//
// HeaderBranding
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.BackColor = System.Drawing.Color.White;
this.Controls.Add(this.labelVersion);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.linkLabel1);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.appName);
this.MaximumSize = new System.Drawing.Size(0, 100);
this.MinimumSize = new System.Drawing.Size(500, 80);
this.MinimumSize = new System.Drawing.Size(500, 90);
this.Name = "HeaderBranding";
this.Padding = new System.Windows.Forms.Padding(3);
this.Size = new System.Drawing.Size(591, 80);
this.Size = new System.Drawing.Size(591, 90);
this.Load += new System.EventHandler(this.ServerHeaderBranding_Load);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
@@ -108,9 +126,10 @@
#endregion
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.LinkLabel linkLabel1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.Label appName;
private System.Windows.Forms.Label labelVersion;
}
}
File diff suppressed because it is too large Load Diff
@@ -7,15 +7,29 @@
<PackageId>ConsoleReferenceClient</PackageId>
<Company>OPC Foundation</Company>
<Description>.NET Console Reference Client</Description>
<Copyright>Copyright © 2004-2020 OPC Foundation, Inc</Copyright>
<Copyright>Copyright © 2004-2022 OPC Foundation, Inc</Copyright>
<RootNamespace>Quickstarts.ConsoleReferenceClient</RootNamespace>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup Condition=" '$(NoHttps)' != 'true' ">
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\ConsoleReferenceServer\ConsoleUtils.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
@@ -23,8 +37,9 @@
</ItemGroup>
<ItemGroup>
<None Update="ConsoleReferenceClient.Config.xml">
<None Update="Quickstarts.ReferenceClient.Config.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Quickstarts.ConsoleReferenceClient
{
public interface IOutput
{
void WriteLine(object obj);
void WriteLine(string msg);
void WriteLine(string msg, params object[] parameters);
}
public class ConsoleOutput : IOutput
{
public void WriteLine(object obj) => Console.WriteLine(obj);
public void WriteLine(string msg) => Console.WriteLine(msg);
public void WriteLine(string msg, params object[] parameters) => Console.WriteLine(msg, parameters);
}
}
@@ -1,5 +1,5 @@
/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
@@ -28,112 +28,159 @@
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Configuration;
namespace Quickstarts.ConsoleReferenceClient
{
/// <summary>
/// The program.
/// </summary>
public static class Program
{
#region Private Fields
/// <summary>
/// Elenco degli items da monitorare come risultato del browse iniziale
/// Main entry point.
/// </summary>
private static Dictionary<string, string> selectedItemList = new Dictionary<string, string>();
#endregion Private Fields
#region Public Methods
public static async Task Main(string[] args)
{
IOutput console = new ConsoleOutput();
console.WriteLine("OPC UA Console Reference Client");
TextWriter output = Console.Out;
output.WriteLine("OPC UA Console Reference Client");
output.WriteLine("OPC UA library: {0} @ {1} -- {2}",
Utils.GetAssemblyBuildNumber(),
Utils.GetAssemblyTimestamp().ToString("G", CultureInfo.InvariantCulture),
Utils.GetAssemblySoftwareVersion());
// The application name and config file names
var applicationName = "ConsoleReferenceClient";
var configSectionName = "Quickstarts.ReferenceClient";
var usage = $"Usage: dotnet {applicationName}.dll [OPTIONS]";
// command line options
bool showHelp = false;
bool autoAccept = false;
bool logConsole = false;
bool appLog = false;
bool renewCertificate = false;
string password = null;
int timeout = Timeout.Infinite;
Mono.Options.OptionSet options = new Mono.Options.OptionSet {
usage,
{ "h|help", "show this message and exit", h => showHelp = h != null },
{ "a|autoaccept", "auto accept certificates (for testing only)", a => autoAccept = a != null },
{ "c|console", "log to console", c => logConsole = c != null },
{ "l|log", "log app output", c => appLog = c != null },
{ "p|password=", "optional password for private key", (string p) => password = p },
{ "r|renew", "renew application certificate", r => renewCertificate = r != null },
{ "t|timeout=", "timeout in seconds to exit application", (int t) => timeout = t * 1000 },
};
try
{
// parse command line and set options
var extraArg = ConsoleUtils.ProcessCommandLine(output, args, options, ref showHelp, false);
// connect Url?
Uri serverUrl = new Uri("opc.tcp://localhost:62541/Quickstarts/ReferenceServer");
if (!string.IsNullOrEmpty(extraArg))
{
serverUrl = new Uri(extraArg);
}
// log console output to logger
if (logConsole && appLog)
{
output = new LogWriter();
}
// Define the UA Client application
ApplicationInstance application = new ApplicationInstance();
application.ApplicationName = "Quickstart Console Reference Client";
application.ApplicationType = ApplicationType.Client;
ApplicationInstance.MessageDlg = new ApplicationMessageDlg(output);
CertificatePasswordProvider PasswordProvider = new CertificatePasswordProvider(password);
ApplicationInstance application = new ApplicationInstance {
ApplicationName = applicationName,
ApplicationType = ApplicationType.Client,
ConfigSectionName = configSectionName,
CertificatePasswordProvider = PasswordProvider
};
// load the application configuration.
await application.LoadApplicationConfiguration("ConsoleReferenceClient.Config.xml", silent: false);
// check the application certificate.
await application.CheckApplicationInstanceCertificate(silent: false, minimumKeySize: 0);
var config = await application.LoadApplicationConfiguration(silent: false);
// create the UA Client object and connect to configured server.
UAClient uaClient = new UAClient(application.ApplicationConfiguration, console, ClientBase.ValidateResponse);
// setup the logging
ConsoleUtils.ConfigureLogging(config, applicationName, logConsole, LogLevel.Information);
console.WriteLine("Connection Format: opc.tcp://{srvUrl}:{port}");
console.WriteLine("Please enter server url (Enter for default):");
string srvUrl = Console.ReadLine();
srvUrl = string.IsNullOrEmpty(srvUrl) ? "192.168.250.53" : srvUrl;
console.WriteLine("Please enter server port [4840]:");
string srvPort = Console.ReadLine();
srvPort = string.IsNullOrEmpty(srvPort) ? "4840" : srvPort;
uaClient.ServerUrl = $"opc.tcp://{srvUrl}:{srvPort}";
bool connected = await uaClient.ConnectAsync();
if (connected)
// delete old certificate
if (renewCertificate)
{
// Run tests for available methods.
uaClient.ReadNodes();
uaClient.WriteNodes();
await application.DeleteApplicationInstanceCertificate().ConfigureAwait(false);
}
console.WriteLine("Please enter NodeId Value (enter for next option) (ex: ns=4,i=5001)");
string fullVal = Console.ReadLine();
if (!string.IsNullOrEmpty(fullVal))
// check the application certificate.
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, minimumKeySize: 0).ConfigureAwait(false);
if (!haveAppCertificate)
{
throw new ErrorExitException("Application instance certificate invalid!", ExitCode.ErrorCertificate);
}
// wait for timeout or Ctrl-C
var quitEvent = ConsoleUtils.CtrlCHandler();
// connect to a server until application stopped
bool quit = false;
DateTime start = DateTime.UtcNow;
int waitTime = int.MaxValue;
do
{
if (timeout > 0)
{
selectedItemList = uaClient.Browse(fullVal);
waitTime = timeout - (int)DateTime.UtcNow.Subtract(start).TotalMilliseconds;
if (waitTime <= 0)
{
break;
}
}
// create the UA Client object and connect to configured server.
UAClient uaClient = new UAClient(application.ApplicationConfiguration, output, ClientBase.ValidateResponse) {
AutoAccept = autoAccept
};
bool connected = await uaClient.ConnectAsync(serverUrl.ToString());
if (connected)
{
// Run tests for available methods.
uaClient.ReadNodes();
uaClient.WriteNodes();
uaClient.Browse();
uaClient.CallMethod();
uaClient.SubscribeToDataChanges();
// Wait for some DataChange notifications from MonitoredItems
quit = quitEvent.WaitOne(Math.Min(30_000, waitTime));
uaClient.Disconnect();
}
else
{
console.WriteLine("Please enter INT Value (ex: 5001 --> i=5001)");
string valIn01 = Console.ReadLine();
console.WriteLine("Please enter Namespace index (ex: 4 --> ns=4)");
string valIn02 = Console.ReadLine();
uint iValue = 5001;
ushort nsVal = 4;
if (!string.IsNullOrEmpty(valIn01))
{
uint.TryParse(valIn01, out iValue);
}
if (!string.IsNullOrEmpty(valIn02))
{
ushort.TryParse(valIn02, out nsVal);
}
selectedItemList = uaClient.Browse(nsVal, iValue);
output.WriteLine("Could not connect to server! Retry in 10 seconds or Ctrl-C to quit.");
quit = quitEvent.WaitOne(Math.Min(10_000, waitTime));
}
console.WriteLine("Press ENTER to continue...");
Console.ReadLine();
//uaClient.CallMethod();
List<Opc.Ua.Client.MonitoredItem> subscribedItems = uaClient.SubscribeToDataChanges(selectedItemList);
} while (!quit);
// Wait for some DataChange notifications from MonitoredItems
await Task.Delay(20_000 * 3 * 15);
uaClient.Disconnect();
}
else
{
console.WriteLine("Could not connect to server!");
}
console.WriteLine("\nProgram ended.");
console.WriteLine("Press any key to finish...");
Console.ReadKey();
output.WriteLine("\nClient stopped.");
}
catch (Exception ex)
{
console.WriteLine(ex.Message);
output.WriteLine(ex.Message);
}
}
#endregion Public Methods
}
}
@@ -5,8 +5,8 @@
xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"
>
<ApplicationName>Quickstart Console Reference Client</ApplicationName>
<ApplicationUri>urn:localhost:Quickstarts:Console ReferenceClient</ApplicationUri>
<ProductUri>uri:opcfoundation.org:Quickstarts:Console ReferenceClient</ProductUri>
<ApplicationUri>urn:localhost:UA:Quickstarts:ReferenceClient</ApplicationUri>
<ProductUri>uri:opcfoundation.org:Quickstarts:ReferenceClient</ProductUri>
<ApplicationType>Client_1</ApplicationType>
<SecurityConfiguration>
@@ -14,26 +14,26 @@
<!-- Where the application instance certificate is stored (MachineDefault) -->
<ApplicationCertificate>
<StoreType>Directory</StoreType>
<StorePath>%CommonApplicationData%\OPC Foundation\pki\own</StorePath>
<SubjectName>CN=Quickstart Console Reference Client, C=US, S=Arizona, O=OPC Foundation, DC=localhost</SubjectName>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/own</StorePath>
<SubjectName>CN=Console Reference Client, C=US, S=Arizona, O=OPC Foundation, DC=localhost</SubjectName>
</ApplicationCertificate>
<!-- Where the issuer certificate are stored (certificate authorities) -->
<TrustedIssuerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%CommonApplicationData%\OPC Foundation\pki\issuer</StorePath>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/issuer</StorePath>
</TrustedIssuerCertificates>
<!-- Where the trust list is stored -->
<TrustedPeerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%CommonApplicationData%\OPC Foundation\pki\trusted</StorePath>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/trusted</StorePath>
</TrustedPeerCertificates>
<!-- The directory used to store invalid certficates for later review by the administrator. -->
<RejectedCertificateStore>
<StoreType>Directory</StoreType>
<StorePath>%CommonApplicationData%\OPC Foundation\pki\rejected</StorePath>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/rejected</StorePath>
</RejectedCertificateStore>
<!-- WARNING: The following setting (to automatically accept untrusted certificates) should be used
@@ -70,7 +70,7 @@
</Extensions>
<TraceConfiguration>
<OutputFilePath>%CommonApplicationData%\OPC Foundation\Logs\Quickstarts.ConsoleReferenceClient.log.txt</OutputFilePath>
<OutputFilePath>%LocalApplicationData%/OPC Foundation/Logs/Quickstarts.ReferenceClient.log.txt</OutputFilePath>
<DeleteOnLoad>true</DeleteOnLoad>
<!-- Show Only Errors -->
<!-- <TraceMasks>1</TraceMasks> -->
@@ -30,271 +30,51 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Collections;
using Opc.Ua;
using Opc.Ua.Client;
namespace Quickstarts.ConsoleReferenceClient
namespace Quickstarts
{
/// <summary>
/// OPC UA Client with examples of basic functionality.
/// </summary>
internal class UAClient
class UAClient
{
#region Private Fields
private readonly IOutput m_output;
private readonly Action<IList, IList> m_validateResponse;
private ApplicationConfiguration m_configuration;
private Session m_session;
#endregion Private Fields
#region Public Constructors
#region Constructors
/// <summary>
/// Initializes a new instance of the UAClient class.
/// </summary>
public UAClient(ApplicationConfiguration configuration, IOutput output, Action<IList, IList> validateResponse)
public UAClient(ApplicationConfiguration configuration, TextWriter writer, Action<IList, IList> validateResponse)
{
m_validateResponse = validateResponse;
m_output = output;
m_output = writer;
m_configuration = configuration;
m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
}
#endregion Public Constructors
#region Public Events
/// <summary>
/// Evento notifica variazione MonitoredItem
/// </summary>
public event EventHandler<opcUaMonitItemChange> eh_MonItChange;
#endregion Public Events
#endregion
#region Public Properties
/// <summary>
/// Gets or sets the server URL.
/// </summary>
public string ServerUrl { get; set; } = "opc.tcp://localhost:62541/Quickstarts/ReferenceServer";
/// <summary>
/// Gets the client session.
/// </summary>
public Session Session => m_session;
#endregion Public Properties
#region Private Methods
/// <summary>
/// Handles the certificate validation event.
/// This event is triggered every time an untrusted certificate is received from the server.
/// Auto accept untrusted certificates.
/// </summary>
private void CertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e)
{
bool certificateAccepted = true;
// ****
// Implement a custom logic to decide if the certificate should be
// accepted or not and set certificateAccepted flag accordingly.
// The certificate can be retrieved from the e.Certificate field
// ***
ServiceResult error = e.Error;
while (error != null)
{
m_output.WriteLine(error);
error = error.InnerResult;
}
if (certificateAccepted)
{
m_output.WriteLine("Untrusted Certificate accepted. SubjectName = {0}", e.Certificate.SubjectName);
}
e.AcceptAll = certificateAccepted;
}
/// <summary>
/// Handle DataChange notifications from Server
/// </summary>
private void OnMonitoredItemNotification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
try
{
// Log MonitoredItem Notification event
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
// sollevo evento notifica vaziazione MonitoredItem
if (eh_MonItChange != null)
{
eh_MonItChange(this, new opcUaMonitItemChange(monitoredItem, notification));
}
m_output.WriteLine("Notification Received for Variable \"{0}\" and Value = {1}.", monitoredItem.DisplayName, notification.Value);
}
catch (Exception ex)
{
m_output.WriteLine("OnMonitoredItemNotification error: {0}", ex.Message);
}
}
#endregion Private Methods
public bool AutoAccept { get; set; } = false;
#endregion
#region Public Methods
/// <summary>
/// Browse Server nodes
/// </summary>
public Dictionary<string, string> Browse(string fullConf)
{
Dictionary<string, string> nodeIdNameList = new Dictionary<string, string>();
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return nodeIdNameList;
}
try
{
// Create a Browser object
Browser browser = new Browser(m_session);
// Set browse parameters
browser.BrowseDirection = BrowseDirection.Forward;
browser.NodeClassMask = (int)NodeClass.Object | (int)NodeClass.Variable;
browser.ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences;
//NodeId nodeToBrowse = ObjectIds.Server;
//NodeId nodeToBrowse = new NodeId("ns=4,i=5001");
NodeId nodeToBrowse = new NodeId(fullConf);
// Call Browse service
m_output.WriteLine("Browsing {0} node...", nodeToBrowse);
ReferenceDescriptionCollection browseResults = browser.Browse(nodeToBrowse);
// Display the results
m_output.WriteLine("Browse returned {0} results:", browseResults.Count);
foreach (ReferenceDescription result in browseResults)
{
m_output.WriteLine($" NodeId = {result.NodeId}, DisplayName = {result.DisplayName.Text}, NodeClass = {result.NodeClass}, Others: {result.BinaryEncodingId} | {result.BrowseName}");
nodeIdNameList.Add(result.NodeId.ToString(), result.DisplayName.Text);
}
}
catch (Exception ex)
{
// Log Error
m_output.WriteLine($"Browse Error : {ex.Message}.");
}
return nodeIdNameList;
}
/// <summary>
/// Browse Server nodes
/// </summary>
public Dictionary<string, string> Browse(ushort startNodeNS, uint startNodeVal)
{
Dictionary<string, string> nodeIdNameList = new Dictionary<string, string>();
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return nodeIdNameList;
}
try
{
// Create a Browser object
Browser browser = new Browser(m_session);
// Set browse parameters
browser.BrowseDirection = BrowseDirection.Forward;
browser.NodeClassMask = (int)NodeClass.Object | (int)NodeClass.Variable;
browser.ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences;
//NodeId nodeToBrowse = ObjectIds.Server;
//NodeId nodeToBrowse = new NodeId("ns=4,i=5001");
NodeId nodeToBrowse = new NodeId(startNodeVal, startNodeNS);
// Call Browse service
m_output.WriteLine("Browsing {0} node...", nodeToBrowse);
ReferenceDescriptionCollection browseResults = browser.Browse(nodeToBrowse);
// Display the results
m_output.WriteLine("Browse returned {0} results:", browseResults.Count);
foreach (ReferenceDescription result in browseResults)
{
m_output.WriteLine($" NodeId = {result.NodeId}, DisplayName = {result.DisplayName.Text}, NodeClass = {result.NodeClass}, Others: {result.BinaryEncodingId} | {result.BrowseName}");
nodeIdNameList.Add(result.NodeId.ToString(), result.DisplayName.Text);
}
}
catch (Exception ex)
{
// Log Error
m_output.WriteLine($"Browse Error : {ex.Message}.");
}
return nodeIdNameList;
}
/// <summary>
/// Call UA method
/// </summary>
public void CallMethod()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Define the UA Method to call
// Parent node - Objects\CTT\Methods
// Method node - Objects\CTT\Methods\Add
NodeId objectId = new NodeId("ns=2;s=Methods");
NodeId methodId = new NodeId("ns=2;s=Methods_Add");
// Define the method parameters
// Input argument requires a Float and an UInt32 value
object[] inputArguments = new object[] { (float)10.5, (uint)10 };
IList<object> outputArguments = null;
// Invoke Call service
m_output.WriteLine("Calling UAMethod for node {0} ...", methodId);
outputArguments = m_session.Call(objectId, methodId, inputArguments);
// Display results
m_output.WriteLine("Method call returned {0} output argument(s):", outputArguments.Count);
foreach (var outputArgument in outputArguments)
{
m_output.WriteLine(" OutputValue = {0}", outputArgument.ToString());
}
}
catch (Exception ex)
{
m_output.WriteLine("Method call error: {0}", ex.Message);
}
}
/// <summary>
/// Creates a session with the UA server
/// </summary>
public async Task<bool> ConnectAsync()
public async Task<bool> ConnectAsync(string serverUrl)
{
if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
try
{
if (m_session != null && m_session.Connected == true)
@@ -303,12 +83,11 @@ namespace Quickstarts.ConsoleReferenceClient
}
else
{
m_output.WriteLine("Connecting...");
m_output.WriteLine("Connecting to... {0}", serverUrl);
// Get the endpoint by connecting to server's discovery endpoint.
// Try to find the first endopint without security.
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(ServerUrl, false);
// Try to find the first endopint with security.
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(m_configuration, serverUrl, true);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
@@ -331,7 +110,7 @@ namespace Quickstarts.ConsoleReferenceClient
}
// Session created successfully.
m_output.WriteLine($"New Session Created with SessionName = {m_session.SessionName}");
m_output.WriteLine("New Session Created with SessionName = {0}", m_session.SessionName);
}
return true;
@@ -339,7 +118,7 @@ namespace Quickstarts.ConsoleReferenceClient
catch (Exception ex)
{
// Log Error
m_output.WriteLine($"Create Session Error : {ex.Message}");
m_output.WriteLine("Create Session Error : {0}", ex.Message);
return false;
}
}
@@ -420,18 +199,15 @@ namespace Quickstarts.ConsoleReferenceClient
{
m_output.WriteLine("Read Value = {0} , StatusCode = {1}", result.Value, result.StatusCode);
}
#endregion Read a node by calling the Read Service
#endregion
#region Read the Value attribute of a node by calling the Session.ReadValue method
// Read Server NamespaceArray
m_output.WriteLine("Reading Value of NamespaceArray node...");
DataValue namespaceArray = m_session.ReadValue(Variables.Server_NamespaceArray);
// Display the result
m_output.WriteLine($"NamespaceArray Value = {namespaceArray}");
#endregion Read the Value attribute of a node by calling the Session.ReadValue method
#endregion
}
catch (Exception ex)
{
@@ -440,137 +216,6 @@ namespace Quickstarts.ConsoleReferenceClient
}
}
/// <summary>
/// Create Subscription and MonitoredItems for DataChanges
/// </summary>
public List<MonitoredItem> SubscribeToDataChanges(Dictionary<string, string> DataList)
{
List<MonitoredItem> monItList = new List<MonitoredItem>();
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return monItList;
}
try
{
// Create a subscription for receiving data change notifications
// Define Subscription parameters
Subscription subscription = new Subscription(m_session.DefaultSubscription);
subscription.DisplayName = "Steamware Console Subscription";
subscription.PublishingEnabled = true;
subscription.PublishingInterval = 1000;
m_session.AddSubscription(subscription);
// Create the subscription on Server side
subscription.Create();
m_output.WriteLine("New Subscription created with SubscriptionId = {0}.", subscription.Id);
// Create MonitoredItems for data changes
foreach (var item in DataList)
{
MonitoredItem currMonIt = new MonitoredItem(subscription.DefaultItem);
// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
currMonIt.StartNodeId = new NodeId(item.Key);
currMonIt.AttributeId = Attributes.Value;
currMonIt.DisplayName = item.Value;
currMonIt.SamplingInterval = 1000;
currMonIt.Notification += OnMonitoredItemNotification;
subscription.AddItem(currMonIt);
monItList.Add(currMonIt);
}
#if false
MonitoredItem IO_120_00_MonitoredItem = new MonitoredItem(subscription.DefaultItem);
// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
IO_120_00_MonitoredItem.StartNodeId = new NodeId("ns=4;s=IO_120.00");
IO_120_00_MonitoredItem.AttributeId = Attributes.Value;
IO_120_00_MonitoredItem.DisplayName = "IO_120 Variable";
IO_120_00_MonitoredItem.SamplingInterval = 1000;
IO_120_00_MonitoredItem.Notification += OnMonitoredItemNotification;
subscription.AddItem(IO_120_00_MonitoredItem);
MonitoredItem IO_120_01_MonitoredItem = new MonitoredItem(subscription.DefaultItem);
// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
IO_120_01_MonitoredItem.StartNodeId = new NodeId("ns=4;s=IO_120.01");
IO_120_01_MonitoredItem.AttributeId = Attributes.Value;
IO_120_01_MonitoredItem.DisplayName = "IO_120_01 Variable";
IO_120_01_MonitoredItem.SamplingInterval = 1000;
IO_120_01_MonitoredItem.Notification += OnMonitoredItemNotification;
subscription.AddItem(IO_120_01_MonitoredItem);
MonitoredItem IO_130_MonitoredItem = new MonitoredItem(subscription.DefaultItem);
// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
IO_130_MonitoredItem.StartNodeId = new NodeId("ns=4;s=IO_130");
IO_130_MonitoredItem.AttributeId = Attributes.Value;
IO_130_MonitoredItem.DisplayName = "IO_130 Variable";
IO_130_MonitoredItem.SamplingInterval = 1000;
IO_130_MonitoredItem.Notification += OnMonitoredItemNotification;
subscription.AddItem(IO_130_MonitoredItem);
MonitoredItem IO_135_MonitoredItem = new MonitoredItem(subscription.DefaultItem);
// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
IO_135_MonitoredItem.StartNodeId = new NodeId("ns=4;s=IO_135");
IO_135_MonitoredItem.AttributeId = Attributes.Value;
IO_135_MonitoredItem.DisplayName = "IO_135 Variable";
IO_135_MonitoredItem.SamplingInterval = 1000;
IO_135_MonitoredItem.Notification += OnMonitoredItemNotification;
subscription.AddItem(IO_135_MonitoredItem);
MonitoredItem IO_140_MonitoredItem = new MonitoredItem(subscription.DefaultItem);
// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
IO_140_MonitoredItem.StartNodeId = new NodeId("ns=4;s=IO_140");
IO_140_MonitoredItem.AttributeId = Attributes.Value;
IO_140_MonitoredItem.DisplayName = "IO_140 Variable";
IO_140_MonitoredItem.SamplingInterval = 1000;
IO_140_MonitoredItem.Notification += OnMonitoredItemNotification;
subscription.AddItem(IO_140_MonitoredItem);
//MonitoredItem intMonitoredItem = new MonitoredItem(subscription.DefaultItem);
//// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
//intMonitoredItem.StartNodeId = new NodeId("ns=2;s=Scalar_Simulation_Int32");
//intMonitoredItem.AttributeId = Attributes.Value;
//intMonitoredItem.DisplayName = "Int32 Variable";
//intMonitoredItem.SamplingInterval = 1000;
//intMonitoredItem.Notification += OnMonitoredItemNotification;
//subscription.AddItem(intMonitoredItem);
//MonitoredItem floatMonitoredItem = new MonitoredItem(subscription.DefaultItem);
//// Float Node - Objects\CTT\Scalar\Simulation\Float
//floatMonitoredItem.StartNodeId = new NodeId("ns=2;s=Scalar_Simulation_Float");
//floatMonitoredItem.AttributeId = Attributes.Value;
//floatMonitoredItem.DisplayName = "Float Variable";
//floatMonitoredItem.SamplingInterval = 1000;
//floatMonitoredItem.Notification += OnMonitoredItemNotification;
//subscription.AddItem(floatMonitoredItem);
//MonitoredItem stringMonitoredItem = new MonitoredItem(subscription.DefaultItem);
//// String Node - Objects\CTT\Scalar\Simulation\String
//stringMonitoredItem.StartNodeId = new NodeId("ns=2;s=Scalar_Simulation_String");
//stringMonitoredItem.AttributeId = Attributes.Value;
//stringMonitoredItem.DisplayName = "String Variable";
//stringMonitoredItem.SamplingInterval = 1000;
//stringMonitoredItem.Notification += OnMonitoredItemNotification;
//subscription.AddItem(stringMonitoredItem);
#endif
// Create the monitored items on Server side
subscription.ApplyChanges();
m_output.WriteLine("MonitoredItems created for SubscriptionId = {0}.", subscription.Id);
}
catch (Exception ex)
{
m_output.WriteLine("Subscribe error: {0}", ex.Message);
}
return monItList;
}
/// <summary>
/// Write a list of nodes to the Server
/// </summary>
@@ -588,50 +233,28 @@ namespace Quickstarts.ConsoleReferenceClient
WriteValueCollection nodesToWrite = new WriteValueCollection();
// Int32 Node - Objects\CTT\Scalar\Scalar_Static\Int32
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId("ns=4;s=IO_151");
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = (int)111;
nodesToWrite.Add(commWriteVal);
WriteValue intWriteVal = new WriteValue();
intWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_Int32");
intWriteVal.AttributeId = Attributes.Value;
intWriteVal.Value = new DataValue();
intWriteVal.Value.Value = (int)100;
nodesToWrite.Add(intWriteVal);
WriteValue artWriteVal = new WriteValue();
artWriteVal.NodeId = new NodeId("ns=4;s=IO_151");
artWriteVal.AttributeId = Attributes.Value;
artWriteVal.Value = new DataValue();
artWriteVal.Value.Value = (int)222;
nodesToWrite.Add(artWriteVal);
// Float Node - Objects\CTT\Scalar\Scalar_Static\Float
WriteValue floatWriteVal = new WriteValue();
floatWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_Float");
floatWriteVal.AttributeId = Attributes.Value;
floatWriteVal.Value = new DataValue();
floatWriteVal.Value.Value = (float)100.5;
nodesToWrite.Add(floatWriteVal);
WriteValue qtyWriteVal = new WriteValue();
qtyWriteVal.NodeId = new NodeId("ns=4;s=IO_153");
qtyWriteVal.AttributeId = Attributes.Value;
qtyWriteVal.Value = new DataValue();
qtyWriteVal.Value.Value = (int)333;
nodesToWrite.Add(qtyWriteVal);
//// Int32 Node - Objects\CTT\Scalar\Scalar_Static\Int32
//WriteValue intWriteVal = new WriteValue();
//intWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_Int32");
//intWriteVal.AttributeId = Attributes.Value;
//intWriteVal.Value = new DataValue();
//intWriteVal.Value.Value = (int)100;
//nodesToWrite.Add(intWriteVal);
//// Float Node - Objects\CTT\Scalar\Scalar_Static\Float
//WriteValue floatWriteVal = new WriteValue();
//floatWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_Float");
//floatWriteVal.AttributeId = Attributes.Value;
//floatWriteVal.Value = new DataValue();
//floatWriteVal.Value.Value = (float)100.5;
//nodesToWrite.Add(floatWriteVal);
//// String Node - Objects\CTT\Scalar\Scalar_Static\String
//WriteValue stringWriteVal = new WriteValue();
//stringWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_String");
//stringWriteVal.AttributeId = Attributes.Value;
//stringWriteVal.Value = new DataValue();
//stringWriteVal.Value.Value = "String Test";
//nodesToWrite.Add(stringWriteVal);
// String Node - Objects\CTT\Scalar\Scalar_Static\String
WriteValue stringWriteVal = new WriteValue();
stringWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_String");
stringWriteVal.AttributeId = Attributes.Value;
stringWriteVal.Value = new DataValue();
stringWriteVal.Value.Value = "String Test";
nodesToWrite.Add(stringWriteVal);
// Write the node attributes
StatusCodeCollection results = null;
@@ -662,60 +285,217 @@ namespace Quickstarts.ConsoleReferenceClient
}
}
#endregion Public Methods
}
/// <summary>
/// Browse Server nodes
/// </summary>
public void Browse()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Create a Browser object
Browser browser = new Browser(m_session);
// Set browse parameters
browser.BrowseDirection = BrowseDirection.Forward;
browser.NodeClassMask = (int)NodeClass.Object | (int)NodeClass.Variable;
browser.ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences;
NodeId nodeToBrowse = ObjectIds.Server;
// Call Browse service
m_output.WriteLine("Browsing {0} node...", nodeToBrowse);
ReferenceDescriptionCollection browseResults = browser.Browse(nodeToBrowse);
// Display the results
m_output.WriteLine("Browse returned {0} results:", browseResults.Count);
foreach (ReferenceDescription result in browseResults)
{
m_output.WriteLine(" DisplayName = {0}, NodeClass = {1}", result.DisplayName.Text, result.NodeClass);
}
}
catch (Exception ex)
{
// Log Error
m_output.WriteLine($"Browse Error : {ex.Message}.");
}
}
/// <summary>
/// Call UA method
/// </summary>
public void CallMethod()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Define the UA Method to call
// Parent node - Objects\CTT\Methods
// Method node - Objects\CTT\Methods\Add
NodeId objectId = new NodeId("ns=2;s=Methods");
NodeId methodId = new NodeId("ns=2;s=Methods_Add");
// Define the method parameters
// Input argument requires a Float and an UInt32 value
object[] inputArguments = new object[] { (float)10.5, (uint)10 };
IList<object> outputArguments = null;
// Invoke Call service
m_output.WriteLine("Calling UAMethod for node {0} ...", methodId);
outputArguments = m_session.Call(objectId, methodId, inputArguments);
// Display results
m_output.WriteLine("Method call returned {0} output argument(s):", outputArguments.Count);
foreach (var outputArgument in outputArguments)
{
m_output.WriteLine(" OutputValue = {0}", outputArgument.ToString());
}
}
catch (Exception ex)
{
m_output.WriteLine("Method call error: {0}", ex.Message);
}
}
/// <summary>
/// Create Subscription and MonitoredItems for DataChanges
/// </summary>
public void SubscribeToDataChanges()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Create a subscription for receiving data change notifications
// Define Subscription parameters
Subscription subscription = new Subscription(m_session.DefaultSubscription);
subscription.DisplayName = "Console ReferenceClient Subscription";
subscription.PublishingEnabled = true;
subscription.PublishingInterval = 1000;
m_session.AddSubscription(subscription);
// Create the subscription on Server side
subscription.Create();
m_output.WriteLine("New Subscription created with SubscriptionId = {0}.", subscription.Id);
// Create MonitoredItems for data changes (Reference Server)
MonitoredItem intMonitoredItem = new MonitoredItem(subscription.DefaultItem);
// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
intMonitoredItem.StartNodeId = new NodeId("ns=2;s=Scalar_Simulation_Int32");
intMonitoredItem.AttributeId = Attributes.Value;
intMonitoredItem.DisplayName = "Int32 Variable";
intMonitoredItem.SamplingInterval = 1000;
intMonitoredItem.Notification += OnMonitoredItemNotification;
subscription.AddItem(intMonitoredItem);
MonitoredItem floatMonitoredItem = new MonitoredItem(subscription.DefaultItem);
// Float Node - Objects\CTT\Scalar\Simulation\Float
floatMonitoredItem.StartNodeId = new NodeId("ns=2;s=Scalar_Simulation_Float");
floatMonitoredItem.AttributeId = Attributes.Value;
floatMonitoredItem.DisplayName = "Float Variable";
floatMonitoredItem.SamplingInterval = 1000;
floatMonitoredItem.Notification += OnMonitoredItemNotification;
subscription.AddItem(floatMonitoredItem);
MonitoredItem stringMonitoredItem = new MonitoredItem(subscription.DefaultItem);
// String Node - Objects\CTT\Scalar\Simulation\String
stringMonitoredItem.StartNodeId = new NodeId("ns=2;s=Scalar_Simulation_String");
stringMonitoredItem.AttributeId = Attributes.Value;
stringMonitoredItem.DisplayName = "String Variable";
stringMonitoredItem.SamplingInterval = 1000;
stringMonitoredItem.Notification += OnMonitoredItemNotification;
subscription.AddItem(stringMonitoredItem);
// Create the monitored items on Server side
subscription.ApplyChanges();
m_output.WriteLine("MonitoredItems created for SubscriptionId = {0}.", subscription.Id);
}
catch (Exception ex)
{
m_output.WriteLine("Subscribe error: {0}", ex.Message);
}
}
#endregion
#region Private Methods
/// <summary>
/// Handle DataChange notifications from Server
/// </summary>
private void OnMonitoredItemNotification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
try
{
// Log MonitoredItem Notification event
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
m_output.WriteLine("Notification Received for Variable \"{0}\" and Value = {1}.", monitoredItem.DisplayName, notification.Value);
}
catch (Exception ex)
{
m_output.WriteLine("OnMonitoredItemNotification error: {0}", ex.Message);
}
}
/// <summary>
/// Handles the certificate validation event.
/// This event is triggered every time an untrusted certificate is received from the server.
/// </summary>
private void CertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e)
{
bool certificateAccepted = false;
// ****
// Implement a custom logic to decide if the certificate should be
// accepted or not and set certificateAccepted flag accordingly.
// The certificate can be retrieved from the e.Certificate field
// ***
ServiceResult error = e.Error;
m_output.WriteLine(error);
if (error.StatusCode == StatusCodes.BadCertificateUntrusted && AutoAccept)
{
certificateAccepted = true;
}
if (certificateAccepted)
{
m_output.WriteLine("Untrusted Certificate accepted. Subject = {0}", e.Certificate.Subject);
e.Accept = true;
}
else
{
m_output.WriteLine("Untrusted Certificate rejected. Subject = {0}", e.Certificate.Subject);
}
}
#endregion
/// <summary>
/// Evento per incapsulare dati x refresh pagina
/// </summary>
public class opcUaMonitItemChange : EventArgs
{
#region Private Fields
/// <summary>
/// Monitored Item da notificare
/// </summary>
private readonly MonitoredItem _monitoredItem;
/// <summary>
/// Valore notifica
/// </summary>
private readonly MonitoredItemNotification _notification;
#endregion Private Fields
#region Public Constructors
/// <summary>
/// salvataggio obj
/// </summary>
/// <param name="newObject"></param>
public opcUaMonitItemChange(MonitoredItem monitoredItem, MonitoredItemNotification notification)
{
_monitoredItem = monitoredItem;
_notification = notification;
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Proprietà lettura del MonitoredItem
/// </summary>
public MonitoredItem CurrMonitoredItem
{
get { return _monitoredItem; }
}
/// <summary>
/// Proprietà lettura della notifica
/// </summary>
public MonitoredItemNotification CurrNotify
{
get { return _notification; }
}
#endregion Public Properties
private ApplicationConfiguration m_configuration;
private Session m_session;
private readonly TextWriter m_output;
private readonly Action<IList, IList> m_validateResponse;
#endregion
}
}
@@ -0,0 +1,3 @@
REM collect a trace using the EventSource provider OPC-UA-Core
dotnet tool install --global dotnet-trace
dotnet-trace collect --name consolereferenceclient --providers OPC-UA-Core,OPC-UA-Client
@@ -11,6 +11,10 @@
<RootNamespace>Quickstarts.ConsoleReferencePublisher</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.PubSub\Opc.Ua.PubSub.csproj" />
@@ -28,24 +28,107 @@
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.Threading;
using Mono.Options;
using Opc.Ua;
using Opc.Ua.PubSub;
using Opc.Ua.PubSub.Configuration;
using Opc.Ua.PubSub.Transport;
namespace Quickstarts.ConsoleReferencePublisher
{
public static class Program
{
// constant DateTime that represents the initial time when the metadata for the configuration was created
private static readonly DateTime kTimeOfConfiguration = new DateTime(2021, 6, 1, 0, 0, 0, DateTimeKind.Utc);
public static void Main(string[] args)
{
Console.WriteLine("OPC UA Console Reference Publisher");
// command line options
bool showHelp = false;
bool useMqttJson = true;
bool useMqttUadp = false;
bool useUdpUadp = false;
string publisherUrl = null;
Mono.Options.OptionSet options = new Mono.Options.OptionSet {
{ "h|help", "Show usage information", v => showHelp = v != null },
{ "m|mqtt_json", "Use MQTT with Json encoding Profile. This is the default option.", v => useMqttJson = v != null },
{ "p|mqtt_uadp", "Use MQTT with UADP encoding Profile.", v => useMqttUadp = v != null },
{ "u|udp_uadp", "Use UDP with UADP encoding Profile", v => useUdpUadp = v != null },
{ "url|publisher_url=", "Publisher Url Address", v => publisherUrl = v},
};
try
{
// Define the configuration of UA Publisher application
PubSubConfigurationDataType pubSubConfiguration = CreatePublisherConfiguration();
IList<string> extraArgs = options.Parse(args);
if (extraArgs.Count > 0)
{
foreach (string extraArg in extraArgs)
{
Console.WriteLine("Error: Unknown option: {0}", extraArg);
showHelp = true;
}
}
}
catch (OptionException e)
{
Console.WriteLine(e.Message);
showHelp = true;
}
// Create the UA Publisher application
if (showHelp)
{
Console.WriteLine("Usage: dotnet ConsoleReferencePublisher.dll/exe [OPTIONS]");
Console.WriteLine();
Console.WriteLine("Options:");
options.WriteOptionDescriptions(Console.Out);
return;
}
try
{
InitializeLog();
PubSubConfigurationDataType pubSubConfiguration = null;
if (useUdpUadp)
{
// set default UDP Publisher Url to local multi-cast if not sent in args.
if (string.IsNullOrEmpty(publisherUrl))
{
publisherUrl = "opc.udp://239.0.0.1:4840";
}
// Create configuration using UDP protocol and UADP Encoding
pubSubConfiguration = CreatePublisherConfiguration_UdpUadp(publisherUrl);
Console.WriteLine("The PubSub Connection was initialized using UDP & UADP Profile.");
}
else
{
// set default MQTT Broker Url to localhost if not sent in args.
if (string.IsNullOrEmpty(publisherUrl))
{
publisherUrl = "mqtt://localhost:1883";
}
if (useMqttUadp)
{
// Create configuration using MQTT protocol and UADP Encoding
pubSubConfiguration = CreatePublisherConfiguration_MqttUadp(publisherUrl);
Console.WriteLine("The PubSub Connection was initialized using MQTT & UADP Profile.");
}
else
{
// Create configuration using MQTT protocol and JSON Encoding
pubSubConfiguration = CreatePublisherConfiguration_MqttJson(publisherUrl);
Console.WriteLine("The PubSub Connection was initialized using MQTT & JSON Profile.");
}
}
// Create the UA Publisher application using configuration file
using (UaPubSubApplication uaPubSubApplication = UaPubSubApplication.Create(pubSubConfiguration))
{
// Start values simulator
@@ -58,17 +141,11 @@ namespace Quickstarts.ConsoleReferencePublisher
Console.WriteLine("Publisher Started. Press Ctrl-C to exit...");
ManualResetEvent quitEvent = new ManualResetEvent(false);
try
{
Console.CancelKeyPress += (sender, eArgs) =>
{
quitEvent.Set();
eArgs.Cancel = true;
};
}
catch
{
}
Console.CancelKeyPress += (sender, eArgs) => {
quitEvent.Set();
eArgs.Cancel = true;
};
// wait for timeout or Ctrl-C
quitEvent.WaitOne();
@@ -84,26 +161,38 @@ namespace Quickstarts.ConsoleReferencePublisher
}
}
#region Private Methods
/// <summary>
/// Creates a PubSubConfiguration object programmatically.
/// Creates a PubSubConfiguration object for UDP & UADP programmatically.
/// </summary>
/// <returns></returns>
public static PubSubConfigurationDataType CreatePublisherConfiguration()
private static PubSubConfigurationDataType CreatePublisherConfiguration_UdpUadp(string urlAddress)
{
// Define a PubSub connection with PublisherId 100
// Define a PubSub connection with PublisherId 1
PubSubConnectionDataType pubSubConnection1 = new PubSubConnectionDataType();
pubSubConnection1.Name = "UADPConnection1";
pubSubConnection1.Name = "Publisher Connection UDP UADP";
pubSubConnection1.Enabled = true;
pubSubConnection1.PublisherId = (UInt16)100;
pubSubConnection1.TransportProfileUri = Profiles.UadpTransport;
pubSubConnection1.PublisherId = (UInt16)1;
pubSubConnection1.TransportProfileUri = Profiles.PubSubUdpUadpTransport;
NetworkAddressUrlDataType address = new NetworkAddressUrlDataType();
// Specify the local Network interface name to be used
// e.g. address.NetworkInterface = "Ethernet";
// Leave empty to publish on all available local interfaces.
address.NetworkInterface = String.Empty;
address.Url = "opc.udp://239.0.0.1:4840";
address.Url = urlAddress;
pubSubConnection1.Address = new ExtensionObject(address);
// configure custom DiscoveryAddress for Discovery messages
pubSubConnection1.TransportSettings = new ExtensionObject() {
Body = new DatagramConnectionTransportDataType() {
DiscoveryAddress = new ExtensionObject() {
Body = new NetworkAddressUrlDataType() {
Url = "opc.udp://224.0.2.15:4840"
}
}
}
};
#region Define WriterGroup1
WriterGroupDataType writerGroup1 = new WriterGroupDataType();
writerGroup1.Name = "WriterGroup 1";
@@ -113,17 +202,261 @@ namespace Quickstarts.ConsoleReferencePublisher
writerGroup1.KeepAliveTime = 5000;
writerGroup1.MaxNetworkMessageSize = 1500;
writerGroup1.HeaderLayoutUri = "UADP-Cyclic-Fixed";
UadpWriterGroupMessageDataType messageSettings = new UadpWriterGroupMessageDataType() {
UadpWriterGroupMessageDataType uadpMessageSettings = new UadpWriterGroupMessageDataType() {
DataSetOrdering = DataSetOrderingType.AscendingWriterId,
GroupVersion = 0,
NetworkMessageContentMask = (uint)(UadpNetworkMessageContentMask.PublisherId | UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.WriterGroupId | UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber | UadpNetworkMessageContentMask.SequenceNumber)
NetworkMessageContentMask = (uint)(UadpNetworkMessageContentMask.PublisherId
| UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.PayloadHeader // needed to be able to decode the DataSetWriterId
| UadpNetworkMessageContentMask.WriterGroupId
| UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber
| UadpNetworkMessageContentMask.SequenceNumber)
};
writerGroup1.MessageSettings = new ExtensionObject(messageSettings);
writerGroup1.MessageSettings = new ExtensionObject(uadpMessageSettings);
// initialize Datagram (UDP) Transport Settings
writerGroup1.TransportSettings = new ExtensionObject(new DatagramWriterGroupTransportDataType());
// Define DataSetWriter 'Simple'
DataSetWriterDataType dataSetWriter1 = new DataSetWriterDataType();
dataSetWriter1.Name = "Writer 1";
dataSetWriter1.DataSetWriterId = 1;
dataSetWriter1.Enabled = true;
dataSetWriter1.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetWriter1.DataSetName = "Simple";
dataSetWriter1.KeyFrameCount = 1;
UadpDataSetWriterMessageDataType uadpDataSetWriterMessage = new UadpDataSetWriterMessageDataType() {
NetworkMessageNumber = 1,
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetWriter1.MessageSettings = new ExtensionObject(uadpDataSetWriterMessage);
writerGroup1.DataSetWriters.Add(dataSetWriter1);
// Define DataSetWriter 'AllTypes'
DataSetWriterDataType dataSetWriter2 = new DataSetWriterDataType();
dataSetWriter2.Name = "Writer 2";
dataSetWriter2.DataSetWriterId = 2;
dataSetWriter2.Enabled = true;
dataSetWriter2.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetWriter2.DataSetName = "AllTypes";
dataSetWriter2.KeyFrameCount = 1;
uadpDataSetWriterMessage = new UadpDataSetWriterMessageDataType() {
NetworkMessageNumber = 1,
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetWriter2.MessageSettings = new ExtensionObject(uadpDataSetWriterMessage);
writerGroup1.DataSetWriters.Add(dataSetWriter2);
pubSubConnection1.WriterGroups.Add(writerGroup1);
#endregion
// Define PublishedDataSet Simple
PublishedDataSetDataType publishedDataSetSimple = CreatePublishedDataSetSimple();
// Define PublishedDataSet AllTypes
PublishedDataSetDataType publishedDataSetAllTypes = CreatePublishedDataSetAllTypes();
//create the PubSub configuration root object
PubSubConfigurationDataType pubSubConfiguration = new PubSubConfigurationDataType();
pubSubConfiguration.Connections = new PubSubConnectionDataTypeCollection()
{
pubSubConnection1
};
pubSubConfiguration.PublishedDataSets = new PublishedDataSetDataTypeCollection()
{
publishedDataSetSimple, publishedDataSetAllTypes
};
return pubSubConfiguration;
}
/// <summary>
/// Creates a PubSubConfiguration object for MQTT & Json programmatically.
/// </summary>
/// <returns></returns>
private static PubSubConfigurationDataType CreatePublisherConfiguration_MqttJson(string urlAddress)
{
// Define a PubSub connection with PublisherId 2
PubSubConnectionDataType pubSubConnection1 = new PubSubConnectionDataType();
pubSubConnection1.Name = "Publisher Connection MQTT Json";
pubSubConnection1.Enabled = true;
pubSubConnection1.PublisherId = (UInt16)2;
pubSubConnection1.TransportProfileUri = Profiles.PubSubMqttJsonTransport;
NetworkAddressUrlDataType address = new NetworkAddressUrlDataType();
// Specify the local Network interface name to be used
// e.g. address.NetworkInterface = "Ethernet";
// Leave empty to publish on all available local interfaces.
address.NetworkInterface = String.Empty;
address.Url = urlAddress;
pubSubConnection1.Address = new ExtensionObject(address);
// Configure the mqtt specific configuration with the MQTT broker
ITransportProtocolConfiguration mqttConfiguration = new MqttClientProtocolConfiguration(version: EnumMqttProtocolVersion.V500);
pubSubConnection1.ConnectionProperties = mqttConfiguration.ConnectionProperties;
string brokerQueueName = "Json_WriterGroup_1";
string brokerMetaData = "$Metadata";
#region Define WriterGroup1 - Json
WriterGroupDataType writerGroup1 = new WriterGroupDataType();
writerGroup1.Name = "WriterGroup 1";
writerGroup1.Enabled = true;
writerGroup1.WriterGroupId = 1;
writerGroup1.PublishingInterval = 5000;
writerGroup1.KeepAliveTime = 5000;
writerGroup1.MaxNetworkMessageSize = 1500;
JsonWriterGroupMessageDataType jsonMessageSettings = new JsonWriterGroupMessageDataType() {
NetworkMessageContentMask = (uint)(JsonNetworkMessageContentMask.NetworkMessageHeader
| JsonNetworkMessageContentMask.DataSetMessageHeader
| JsonNetworkMessageContentMask.PublisherId
| JsonNetworkMessageContentMask.DataSetClassId
| JsonNetworkMessageContentMask.ReplyTo)
};
writerGroup1.MessageSettings = new ExtensionObject(jsonMessageSettings);
writerGroup1.TransportSettings = new ExtensionObject(new BrokerWriterGroupTransportDataType() {
QueueName = brokerQueueName,
});
// Define DataSetWriter 'Simple' Variant encoding
DataSetWriterDataType dataSetWriter1 = new DataSetWriterDataType();
dataSetWriter1.Name = "Writer Variant Encoding";
dataSetWriter1.DataSetWriterId = 1;
dataSetWriter1.Enabled = true;
dataSetWriter1.DataSetFieldContentMask = (uint)DataSetFieldContentMask.None;// Variant encoding;
dataSetWriter1.DataSetName = "Simple";
dataSetWriter1.KeyFrameCount = 3;
JsonDataSetWriterMessageDataType jsonDataSetWriterMessage = new JsonDataSetWriterMessageDataType() {
DataSetMessageContentMask = (uint)(JsonDataSetMessageContentMask.DataSetWriterId
| JsonDataSetMessageContentMask.MetaDataVersion
| JsonDataSetMessageContentMask.SequenceNumber
| JsonDataSetMessageContentMask.Status
| JsonDataSetMessageContentMask.Timestamp),
};
dataSetWriter1.MessageSettings = new ExtensionObject(jsonDataSetWriterMessage);
BrokerDataSetWriterTransportDataType jsonDataSetWriterTransport = new BrokerDataSetWriterTransportDataType() {
QueueName = brokerQueueName,
RequestedDeliveryGuarantee = BrokerTransportQualityOfService.BestEffort,
MetaDataQueueName = $"{brokerQueueName}/{brokerMetaData}",
MetaDataUpdateTime = 0,
};
dataSetWriter1.TransportSettings = new ExtensionObject(jsonDataSetWriterTransport);
writerGroup1.DataSetWriters.Add(dataSetWriter1);
// Define DataSetWriter 'Simple' - Variant encoding
DataSetWriterDataType dataSetWriter2 = new DataSetWriterDataType();
dataSetWriter2.Name = "Writer RawData Encoding";
dataSetWriter2.DataSetWriterId = 2;
dataSetWriter2.Enabled = true;
dataSetWriter2.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetWriter2.DataSetName = "AllTypes";
dataSetWriter2.KeyFrameCount = 1;
jsonDataSetWriterMessage = new JsonDataSetWriterMessageDataType() {
DataSetMessageContentMask = (uint)(JsonDataSetMessageContentMask.DataSetWriterId
| JsonDataSetMessageContentMask.MetaDataVersion
| JsonDataSetMessageContentMask.SequenceNumber
| JsonDataSetMessageContentMask.Status
| JsonDataSetMessageContentMask.Timestamp),
};
dataSetWriter2.MessageSettings = new ExtensionObject(jsonDataSetWriterMessage);
jsonDataSetWriterTransport = new BrokerDataSetWriterTransportDataType()
{
QueueName = brokerQueueName,
RequestedDeliveryGuarantee = BrokerTransportQualityOfService.BestEffort,
MetaDataQueueName = $"{brokerQueueName}/{brokerMetaData}",
MetaDataUpdateTime = 0
};
dataSetWriter2.TransportSettings = new ExtensionObject(jsonDataSetWriterTransport);
writerGroup1.DataSetWriters.Add(dataSetWriter2);
pubSubConnection1.WriterGroups.Add(writerGroup1);
#endregion
// Define PublishedDataSet Simple
PublishedDataSetDataType publishedDataSetSimple = CreatePublishedDataSetSimple();
// Define PublishedDataSet AllTypes
PublishedDataSetDataType publishedDataSetAllTypes = CreatePublishedDataSetAllTypes();
//create the PubSub configuration root object
PubSubConfigurationDataType pubSubConfiguration = new PubSubConfigurationDataType();
pubSubConfiguration.Connections = new PubSubConnectionDataTypeCollection()
{
pubSubConnection1
};
pubSubConfiguration.PublishedDataSets = new PublishedDataSetDataTypeCollection()
{
publishedDataSetSimple, publishedDataSetAllTypes
};
return pubSubConfiguration;
}
/// <summary>
/// Creates a PubSubConfiguration object for MQTT & UADP programmatically.
/// </summary>
/// <returns></returns>
private static PubSubConfigurationDataType CreatePublisherConfiguration_MqttUadp(string urlAddress)
{
// Define a PubSub connection with PublisherId 3
PubSubConnectionDataType pubSubConnection1 = new PubSubConnectionDataType();
pubSubConnection1.Name = "Publisher Connection MQTT UADP";
pubSubConnection1.Enabled = true;
pubSubConnection1.PublisherId = (UInt16)3;
pubSubConnection1.TransportProfileUri = Profiles.PubSubMqttUadpTransport;
NetworkAddressUrlDataType address = new NetworkAddressUrlDataType();
// Specify the local Network interface name to be used
// e.g. address.NetworkInterface = "Ethernet";
// Leave empty to publish on all available local interfaces.
address.NetworkInterface = String.Empty;
address.Url = urlAddress;
pubSubConnection1.Address = new ExtensionObject(address);
// Configure the mqtt specific configuration with the MQTTbroker
ITransportProtocolConfiguration mqttConfiguration = new MqttClientProtocolConfiguration(version: EnumMqttProtocolVersion.V500);
pubSubConnection1.ConnectionProperties = mqttConfiguration.ConnectionProperties;
string brokerQueueName = "Uadp_WriterGroup_1";
string brokerMetaData = "$Metadata";
#region Define WriterGroup1
WriterGroupDataType writerGroup1 = new WriterGroupDataType();
writerGroup1.Name = "WriterGroup 1";
writerGroup1.Enabled = true;
writerGroup1.WriterGroupId = 1;
writerGroup1.PublishingInterval = 5000;
writerGroup1.KeepAliveTime = 5000;
writerGroup1.MaxNetworkMessageSize = 1500;
writerGroup1.HeaderLayoutUri = "UADP-Cyclic-Fixed";
UadpWriterGroupMessageDataType uadpMessageSettings = new UadpWriterGroupMessageDataType() {
DataSetOrdering = DataSetOrderingType.AscendingWriterId,
GroupVersion = 0,
NetworkMessageContentMask = (uint)(UadpNetworkMessageContentMask.PublisherId
| UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.WriterGroupId
| UadpNetworkMessageContentMask.PayloadHeader
| UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber
| UadpNetworkMessageContentMask.SequenceNumber)
};
writerGroup1.MessageSettings = new ExtensionObject(uadpMessageSettings);
// initialize Broker transport settings
writerGroup1.TransportSettings = new ExtensionObject(new BrokerWriterGroupTransportDataType() {
QueueName = brokerQueueName,
});
// Define DataSetWriter 'Simple'
DataSetWriterDataType dataSetWriter1 = new DataSetWriterDataType();
dataSetWriter1.Name = "Writer 1";
@@ -138,7 +471,16 @@ namespace Quickstarts.ConsoleReferencePublisher
NetworkMessageNumber = 1,
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetWriter1.MessageSettings = new ExtensionObject(uadpDataSetWriterMessage);
BrokerDataSetWriterTransportDataType uadpDataSetWriterTransport = new BrokerDataSetWriterTransportDataType() {
QueueName = brokerQueueName,
MetaDataQueueName = $"{brokerQueueName}/{brokerMetaData}",
MetaDataUpdateTime = 60000
};
dataSetWriter1.TransportSettings = new ExtensionObject(uadpDataSetWriterTransport);
writerGroup1.DataSetWriters.Add(dataSetWriter1);
// Define DataSetWriter 'AllTypes'
@@ -155,13 +497,41 @@ namespace Quickstarts.ConsoleReferencePublisher
NetworkMessageNumber = 1,
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetWriter2.MessageSettings = new ExtensionObject(uadpDataSetWriterMessage);
dataSetWriter2.TransportSettings = new ExtensionObject(uadpDataSetWriterTransport);
writerGroup1.DataSetWriters.Add(dataSetWriter2);
pubSubConnection1.WriterGroups.Add(writerGroup1);
#endregion
#region Define PublishedDataSet Simple
// Define PublishedDataSet Simple
PublishedDataSetDataType publishedDataSetSimple = CreatePublishedDataSetSimple();
// Define PublishedDataSet AllTypes
PublishedDataSetDataType publishedDataSetAllTypes = CreatePublishedDataSetAllTypes();
//create the PubSub configuration root object
PubSubConfigurationDataType pubSubConfiguration = new PubSubConfigurationDataType();
pubSubConfiguration.Connections = new PubSubConnectionDataTypeCollection()
{
pubSubConnection1
};
pubSubConfiguration.PublishedDataSets = new PublishedDataSetDataTypeCollection()
{
publishedDataSetSimple, publishedDataSetAllTypes
};
return pubSubConfiguration;
}
/// <summary>
/// Creates the "Simple" DataSet
/// </summary>
/// <returns></returns>
private static PublishedDataSetDataType CreatePublishedDataSetSimple()
{
PublishedDataSetDataType publishedDataSetSimple = new PublishedDataSetDataType();
publishedDataSetSimple.Name = "Simple"; //name shall be unique in a configuration
// Define publishedDataSetSimple.DataSetMetaData
@@ -203,9 +573,11 @@ namespace Quickstarts.ConsoleReferencePublisher
ValueRank = ValueRanks.Scalar
},
};
// set the ConfigurationVersion relative to kTimeOfConfiguration constant
publishedDataSetSimple.DataSetMetaData.ConfigurationVersion = new ConfigurationVersionDataType() {
MinorVersion = 1,
MajorVersion = 1
MinorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration),
MajorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration)
};
PublishedDataItemsDataType publishedDataSetSimpleSource = new PublishedDataItemsDataType();
@@ -221,9 +593,16 @@ namespace Quickstarts.ConsoleReferencePublisher
}
publishedDataSetSimple.DataSetSource = new ExtensionObject(publishedDataSetSimpleSource);
#endregion
#region Define PublishedDataSet AllTypes
return publishedDataSetSimple;
}
/// <summary>
/// Creates the "AllTypes" DataSet
/// </summary>
/// <returns></returns>
private static PublishedDataSetDataType CreatePublishedDataSetAllTypes()
{
PublishedDataSetDataType publishedDataSetAllTypes = new PublishedDataSetDataType();
publishedDataSetAllTypes.Name = "AllTypes"; //name shall be unique in a configuration
// Define publishedDataSetAllTypes.DataSetMetaData
@@ -289,6 +668,14 @@ namespace Quickstarts.ConsoleReferencePublisher
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "UInt64",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.UInt64,
DataType = DataTypeIds.UInt64,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "Float",
DataSetFieldId = new Uuid(Guid.NewGuid()),
@@ -304,12 +691,55 @@ namespace Quickstarts.ConsoleReferencePublisher
DataType = DataTypeIds.Double,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "String",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.String,
DataType = DataTypeIds.String,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "ByteString",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.ByteString,
DataType = DataTypeIds.ByteString,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "Guid",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.Guid,
DataType = DataTypeIds.Guid,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "DateTime",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.DateTime,
DataType = DataTypeIds.DateTime,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "UInt32Array",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.UInt32,
DataType = DataTypeIds.UInt32,
ValueRank = ValueRanks.OneDimension
},
};
// set the ConfigurationVersion relative to kTimeOfConfiguration constant
publishedDataSetAllTypes.DataSetMetaData.ConfigurationVersion = new ConfigurationVersionDataType() {
MinorVersion = 1,
MajorVersion = 1
MinorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration),
MajorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration)
};
PublishedDataItemsDataType publishedDataSetAllTypesSource = new PublishedDataItemsDataType();
//create PublishedData based on metadata names
foreach (var field in publishedDataSetAllTypes.DataSetMetaData.Fields)
{
@@ -320,20 +750,20 @@ namespace Quickstarts.ConsoleReferencePublisher
});
}
publishedDataSetAllTypes.DataSetSource = new ExtensionObject(publishedDataSetAllTypesSource);
#endregion
//create the PubSub configuration root object
PubSubConfigurationDataType pubSubConfiguration = new PubSubConfigurationDataType();
pubSubConfiguration.Connections = new PubSubConnectionDataTypeCollection()
{
pubSubConnection1
};
pubSubConfiguration.PublishedDataSets = new PublishedDataSetDataTypeCollection()
{
publishedDataSetSimple, publishedDataSetAllTypes
};
return pubSubConfiguration;
return publishedDataSetAllTypes;
}
/// <summary>
/// Initialize logging
/// </summary>
private static void InitializeLog()
{
// Initialize logger
Utils.SetTraceLog("%CommonApplicationData%\\OPC Foundation\\Logs\\Quickstarts.ConsoleReferencePublisher.log.txt", true);
Utils.SetTraceMask(Utils.TraceMasks.Error);
Utils.SetTraceOutput(Utils.TraceOutput.DebugAndFile);
}
#endregion
}
}
@@ -55,7 +55,12 @@ namespace Quickstarts.ConsoleReferencePublisher
private PublishedDataSetDataTypeCollection m_publishedDataSets;
private IUaPubSubDataStore m_dataStore;
private Timer m_updateValuesTimer;
string[] m_aviationAlphabet = new string[] {
"Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India",
"Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo",
"Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-Ray", "Yankee", "Zulu"
};
int m_aviationAlphabetIndex = 0;
private object m_lock = new object();
#endregion
@@ -140,8 +145,15 @@ namespace Quickstarts.ConsoleReferencePublisher
WriteFieldData("SByte", NamespaceIndexAllTypes, new DataValue(new Variant((sbyte)0), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("UInt16", NamespaceIndexAllTypes, new DataValue(new Variant((UInt16)0), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("UInt32", NamespaceIndexAllTypes, new DataValue(new Variant((UInt32)0), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("UInt64", NamespaceIndexAllTypes, new DataValue(new Variant((UInt64)0), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("Float", NamespaceIndexAllTypes, new DataValue(new Variant((float)0F), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("Double", NamespaceIndexAllTypes, new DataValue(new Variant((double)0.0), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("String", NamespaceIndexAllTypes, new DataValue(new Variant(m_aviationAlphabet[0]), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("ByteString", NamespaceIndexAllTypes, new DataValue(new Variant(new byte[] { 1, 2, 3 }), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("Guid", NamespaceIndexAllTypes, new DataValue(new Variant(Guid.NewGuid()), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("DateTime", NamespaceIndexAllTypes, new DataValue(new Variant(DateTime.UtcNow), StatusCodes.Good, DateTime.UtcNow));
WriteFieldData("UInt32Array", NamespaceIndexAllTypes, new DataValue(new Variant(new UInt32[] { 1, 2, 3 }), StatusCodes.Good, DateTime.UtcNow));
#endregion
}
@@ -235,8 +247,8 @@ namespace Quickstarts.ConsoleReferencePublisher
case BuiltInType.Byte:
if (variable.ValueRank == ValueRanks.Scalar)
{
Byte byteValue = Convert.ToByte(dataValue.Value);
dataValue.Value = ++byteValue;
byte byteValue = Convert.ToByte(dataValue.Value);
dataValue.Value = (byte)(byteValue + 1);
valueUpdated = true;
}
break;
@@ -295,13 +307,38 @@ namespace Quickstarts.ConsoleReferencePublisher
dataValue.Value = (UInt32)Interlocked.Increment(ref longIdentifier);
valueUpdated = true;
}
else if (variable.ValueRank == ValueRanks.OneDimension)
{
uint[] values = dataValue.Value as uint[];
if (values != null)
{
for (int i = 0; i < values.Length; i++)
{
UInt32 uint32Value = values[i];
long longIdentifier = uint32Value;
Interlocked.CompareExchange(ref longIdentifier, 0, UInt32.MaxValue);
values[i] = (UInt32)Interlocked.Increment(ref longIdentifier);
}
valueUpdated = true;
}
}
break;
case BuiltInType.UInt64:
if (variable.ValueRank == ValueRanks.Scalar)
{
UInt64 uint64Value = Convert.ToUInt64(dataValue.Value);
float longIdentifier = uint64Value + 1;
Interlocked.CompareExchange(ref longIdentifier, 0, UInt64.MaxValue);
dataValue.Value = (UInt64)longIdentifier;
valueUpdated = true;
}
break;
case BuiltInType.Float:
if (variable.ValueRank == ValueRanks.Scalar)
{
float floatValue = Convert.ToSingle(dataValue.Value);
Interlocked.CompareExchange(ref floatValue, 0, float.MaxValue);
dataValue.Value = ++floatValue;
dataValue.Value = floatValue + 1;
valueUpdated = true;
}
break;
@@ -310,7 +347,7 @@ namespace Quickstarts.ConsoleReferencePublisher
{
double doubleValue = Convert.ToDouble(dataValue.Value);
Interlocked.CompareExchange(ref doubleValue, 0, double.MaxValue);
dataValue.Value = ++doubleValue;
dataValue.Value = doubleValue + 1;
valueUpdated = true;
}
break;
@@ -321,6 +358,21 @@ namespace Quickstarts.ConsoleReferencePublisher
valueUpdated = true;
}
break;
case BuiltInType.Guid:
if (variable.ValueRank == ValueRanks.Scalar)
{
dataValue.Value = Guid.NewGuid();
valueUpdated = true;
}
break;
case BuiltInType.String:
if (variable.ValueRank == ValueRanks.Scalar)
{
m_aviationAlphabetIndex = (m_aviationAlphabetIndex + 1) % m_aviationAlphabet.Length;
dataValue.Value = m_aviationAlphabet[m_aviationAlphabetIndex];
valueUpdated = true;
}
break;
}
if (valueUpdated)
@@ -0,0 +1,141 @@
# OPC Foundation UA .NET Standard Library - Console Reference Publisher
## Introduction
This OPC application was created to provide the sample code for creating Publisher applications using the OPC Foundation UA .NET Standard PubSub Library. There is a .NET Core 3.1 (2.1) console version of the Publisher which runs on any OS supporting [.NET Standard](https://docs.microsoft.com/en-us/dotnet/articles/standard).
The Reference Publisher is configured to run in parallel with the [Console Reference Subscriber](../ConsoleReferenceSubscriber/README.md)
## How to build and run the console OPC UA Reference Publisher from Visual Studio
1. Open the solution **UA Reference.sln** with Visual Studio 2019.
2. Choose the project `ConsoleReferencePublisher` in the Solution Explorer and set it with a right click as `Startup Project`.
3. Hit `F5` to build and execute the sample.
## How to build and run the console OPC UA Reference Publisher on Windows, Linux and iOS
This section describes how to run the **ConsoleReferencePublisher**.
Please follow instructions in this [article](https://aka.ms/dotnetcoregs) to setup the dotnet command line environment for your platform.
## Start the Publisher
1. Open a command prompt.
2. Navigate to the folder **Applications/ConsoleReferencePublisher**.
3. To run the Publisher sample execute:
`dotnet run --project ConsoleReferencePublisher.csproj --framework netcoreapp3.1`
The Publisher will start and publish network messages that can be consumed by the Reference Subscriber.
Publisher Initialization
## Command Line Arguments for *ConsoleReferencePublisher*
**ConsoleReferencePublisher** can be executed using the following command line arguments:
- -h|help - Shows usage information
- -m|mqtt_json - Creates a connection using there MQTT with Json encoding Profile. This is the default option.
- -u|udp_uadp - Creates a connection using there UDP with UADP encoding Profile.
To run the Publisher sample using a connection with MQTT with Json encoding execute:
dotnet run --project ConsoleReferencePublisher.csproj --framework netcoreapp3.1
or
dotnet run --project ConsoleReferencePublisher.csproj --framework netcoreapp3.1 -m
To run the Publisher sample using a connection with the UDP with UADP encoding execute:
dotnet run --project ConsoleReferencePublisher.csproj --framework netcoreapp3.1 -u
# Programmer's Guide
To create a new OPC UA Publisher application:
- Open Microsoft Visual Studio 2019 environment,
- Create a new project and give it a name,
- Add a reference to the [OPCFoundation.NetStandard.Opc.Ua.PubSub NuGet package](https://www.nuget.org/packages/OPCFoundation.NetStandard.Opc.Ua.PubSub/),
- Initialize Publisher application (see [Publisher Initialization](#publisher-initialization)).
## Publisher Initialization
The following four steps are required to implement a functional Publisher:
1. Create [Publisher Configuration](#publisher-configuration).
// Create configuration using MQTT protocol and JSON Encoding
PubSubConfigurationDataType pubSubConfiguration = CreatePublisherConfiguration_MqttJson();
Or use the alternative configuration object for UDP with UADP encoding
// Create configuration using UDP protocol and UADP Encoding
PubSubConfigurationDataType pubSubConfiguration = CreatePublisherConfiguration_UdpUadp();
The CreatePublisherConfiguration methods can be found in [ConsoleReferencePublisher/Program.cs](./Program.cs) file.
2. Create an instance of the [UaPubSubApplication Class](../../Docs/PubSub.md#uapubsubapplication-class) using the configuration data from step 1.
// Create an instance of UaPubSubApplication
UaPubSubApplication uaPubSubApplication = UaPubSubApplication.Create(pubSubConfiguration);
3. Provide the data to be published based on the configuration of published data sets. This step is described in the [Publisher Data](#publisher-data) section.
4. Start PubSub application
// Start the publisher
uaPubSubApplication.Start();
After this step the Publisher will publish data as configured.
## Publisher Configuration
The Publisher configuration is a subset of the [PubSub Configuration](../../Docs/PubSub.md#pubsub-configuration). A functional *Publisher* application needs to have a configuration (*PubSubConfgurationDataType* instance) that contains a list of published data sets (*PublishedDataSetDataType* instances) and at least one connection (*PubSubConnectionDataType* instance) with at least one writer group configuration (*WriterGroupDataType* instance). The writer group contains at least one data set writer (*DataSetWriterDataType* instance) pointing to a published data set from the current configuration.
The diagram shows the subset of classes involved in an *OPC UA Publisher* configuration.
![PublisherConfigClasses](../../Docs/Images/PublisherConfigClasses.png)
## Publisher Data
The [UaPubSubApplication Class](../../Docs/PubSub.md#uapubsubapplication-class) provides a property of type [IUaPubSubDataStore](../../Docs/PubSub.md#iuapubsubdatastore-interface) called DataStore. In **ConsoleReferencePublisher** there is no custom implementation provided for *IUaPubSubDataStore* therefore the pub sub application object is initialized using the default implementation of this interface, an instance of *UaPubSubDataStore*.
The code responsible for generating the data values to be published is located in the [PublishedValuesWrites](/PublishedValuesWrites.cs) file from the **ConsoleReferencePublisher** project. It maintains a list of all the fields from the table below and uses a timer for writing the values to *UaPubSubApplication.DataStore* using the *WritePublishedDataItem*() method from *DataStore* class. The data values simulator component is initialized like:
// Start values simulator
PublishedValuesWrites valuesSimulator = new PublishedValuesWrites(uaPubSubApplication);
valuesSimulator.Start();
The **Publisher** component from **ConsoleReferencePublisher** application will use the data generated by *PublishedValuesWrites* to create the *NetworkMessages* that will be published as configured in [Publisher Configuration](#publisher-configuration).
Note:
The current PubSub implementation only supports *PublishedDataItemsDataType* as *DataSetSource* of a *PublishedDataSetDataType* from the configuration. *Events* will be added in a future version.
The **ConsoleReferencePublisher** application is configured to use the following data sets and will generate values as specified in the table below if the default configuration method is used:
### PublishedDataSet 'Simple' - NamespaceIndex = 2
| Name | DataType | ValueRank |Behavior |
|--|--|--|--|
|BoolToggle |Boolean |Scalar |Toggles every 3 seconds|
|Int32|Int32|Scalar |Counts (1 per second) from 0 to 10,000 and then resets|
|Int32Fast|Int32Fast|Scalar |Counts (100 per second) from 0 to 10,000 and then resets|
|DateTime|DateTime|Scalar |Current time refreshed with every packet sent|
The *CreatePublishedDataSetSimple*() method from [Program.cs](Program.cs) creates a *PublishedDataSetDataType* configuration object that contains the metadata information for *'Simple' DataSet*.
### PublishedDataSet 'AllTypes' - NamespaceIndex = 3
| Name | DataType | ValueRank |Behavior |
|--|--|--|--|
|BoolToggle |Boolean |Scalar |Toggles every second|
|Byte|Byte|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|Int16|Int16|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|Int32|Int32|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|SByte|SByte|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|UInt16|UInt16|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|UInt32|UInt32|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|UInt64|UInt64|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|Float|Float|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|Double|Double|Scalar |Counts (1 per second) from 0 to type-max and then resets|
|String|String|Scalar |Spells the aviation alphabet (Alpha, Bravo …) (1 per second)|
|ByteString|ByteString|Scalar |1 new random ByteString per second|
|Guid|Guid|Scalar |1 new random Guid per second|
|DateTime|DateTime|Scalar |Current time refreshed with every packet sent|
|UInt32Array|UInt32|OneDimension|Counts (1 per second on every element) from 0 to type-max and then resets. The count starting point for each value should differ|
The *CreatePublishedDataSetAllTypes*() method from [Program.cs](Program.cs) creates a *PublishedDataSetDataType* configuration object that contains the metadata information for *'AllTypes' DataSet*.
@@ -1,41 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(AppTargetFrameWork)</TargetFramework>
<TargetFrameworks>$(AppTargetFrameWorks)</TargetFrameworks>
<AssemblyName>ConsoleReferenceServer</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>ConsoleReferenceServer</PackageId>
<Company>OPC Foundation</Company>
<Description>.NET Core Reference Server</Description>
<Copyright>Copyright © 2004-2021 OPC Foundation, Inc</Copyright>
<Description>.NET Console Reference Server</Description>
<Copyright>Copyright © 2004-2022 OPC Foundation, Inc</Copyright>
<RootNamespace>Quickstarts</RootNamespace>
<UserSecretsId>46345736-a30f-4466-b3bb-42548ecfaacc</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<None Remove="Quickstarts.MonoReferenceServer.Config.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\ReferenceServer\ReferenceNodeManager.cs" />
<Compile Include="..\ReferenceServer\ReferenceServerConfiguration.cs" />
<Compile Include="..\ReferenceServer\ReferenceServer.cs" />
<Compile Include="..\ReferenceServer\SerilogTraceLogger.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup Condition=" '$(NoHttps)' != 'true' ">
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Server\Opc.Ua.Server.csproj" />
<ProjectReference Include="..\Quickstarts.Servers\Quickstarts.Servers.csproj" />
</ItemGroup>
<Choose>
<!-- Note: Due to incompatibilities of Microsoft.Extensions Nuget packages between versions 3.x and 6.0,
use latest versions only on .NET 5/6, otherwise 3.1.x -->
<When Condition="'$(TargetFramework)' == 'net5.0' OR '$(TargetFramework)' == 'net6.0'">
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.23" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="3.1.23" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="6.6.0.161" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
</ItemGroup>
@@ -0,0 +1,382 @@
/* ========================================================================
* Copyright (c) 2005-2021 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 System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Mono.Options;
using Opc.Ua;
using Opc.Ua.Configuration;
using Serilog;
using Serilog.Events;
using Serilog.Templates;
using static Opc.Ua.Utils;
namespace Quickstarts
{
/// <summary>
/// The log output implementation of a TextWriter.
/// </summary>
public class LogWriter : TextWriter
{
private StringBuilder m_builder = new StringBuilder();
public override void Write(char value)
{
m_builder.Append(value);
}
public override void WriteLine(char value)
{
m_builder.Append(value);
LogInfo("{0}", m_builder.ToString());
m_builder.Clear();
}
public override void WriteLine()
{
LogInfo("{0}", m_builder.ToString());
m_builder.Clear();
}
public override void WriteLine(string format, object arg0)
{
m_builder.Append(format);
LogInfo(m_builder.ToString(), arg0);
m_builder.Clear();
}
public override void WriteLine(string format, object arg0, object arg1)
{
m_builder.Append(format);
LogInfo(m_builder.ToString(), arg0, arg1);
m_builder.Clear();
}
public override void WriteLine(string format, params object[] arg)
{
m_builder.Append(format);
LogInfo(m_builder.ToString(), arg);
m_builder.Clear();
}
public override void Write(string value)
{
m_builder.Append(value);
}
public override void WriteLine(string value)
{
m_builder.Append(value);
LogInfo("{0}", m_builder.ToString());
m_builder.Clear();
}
public override Encoding Encoding
{
get { return Encoding.Default; }
}
}
/// <summary>
/// The error code why the application exit.
/// </summary>
public enum ExitCode : int
{
Ok = 0,
ErrorNotStarted = 0x80,
ErrorRunning = 0x81,
ErrorException = 0x82,
ErrorStopping = 0x83,
ErrorCertificate = 0x84,
ErrorInvalidCommandLine = 0x100
};
/// <summary>
/// An exception that occured and caused an exit of the application.
/// </summary>
public class ErrorExitException : Exception
{
public ExitCode ExitCode { get; }
public ErrorExitException(ExitCode exitCode)
{
ExitCode = exitCode;
}
public ErrorExitException()
{
ExitCode = ExitCode.Ok;
}
public ErrorExitException(string message) : base(message)
{
ExitCode = ExitCode.Ok;
}
public ErrorExitException(string message, ExitCode exitCode) : base(message)
{
ExitCode = exitCode;
}
public ErrorExitException(string message, Exception innerException) : base(message, innerException)
{
ExitCode = ExitCode.Ok;
}
public ErrorExitException(string message, Exception innerException, ExitCode exitCode) : base(message, innerException)
{
ExitCode = exitCode;
}
}
/// <summary>
/// A dialog which asks for user input.
/// </summary>
public class ApplicationMessageDlg : IApplicationMessageDlg
{
private TextWriter m_output;
private string m_message = string.Empty;
private bool m_ask;
public ApplicationMessageDlg(TextWriter output)
{
m_output = output;
}
public override void Message(string text, bool ask)
{
m_message = text;
m_ask = ask;
}
public override async Task<bool> ShowAsync()
{
if (m_ask)
{
var message = new StringBuilder(m_message);
message.Append(" (y/n, default y): ");
m_output.Write(message.ToString());
try
{
ConsoleKeyInfo result = Console.ReadKey();
m_output.WriteLine();
return await Task.FromResult((result.KeyChar == 'y') ||
(result.KeyChar == 'Y') || (result.KeyChar == '\r')).ConfigureAwait(false);
}
catch
{
// intentionally fall through
}
}
else
{
m_output.WriteLine(m_message);
}
return await Task.FromResult(true).ConfigureAwait(false);
}
}
/// <summary>
/// Helper functions shared in various console applications.
/// </summary>
public static class ConsoleUtils
{
/// <summary>
/// Process a command line of the console sample application.
/// </summary>
public static string ProcessCommandLine(
TextWriter output,
string[] args,
Mono.Options.OptionSet options,
ref bool showHelp,
bool noExtraArgs = true)
{
IList<string> extraArgs = null;
try
{
extraArgs = options.Parse(args);
if (noExtraArgs)
{
foreach (string extraArg in extraArgs)
{
output.WriteLine("Error: Unknown option: {0}", extraArg);
showHelp = true;
}
}
}
catch (OptionException e)
{
output.WriteLine(e.Message);
showHelp = true;
}
if (showHelp)
{
options.WriteOptionDescriptions(output);
throw new ErrorExitException("Invalid Commandline or help requested.", ExitCode.ErrorInvalidCommandLine);
}
return extraArgs.FirstOrDefault();
}
/// <summary>
/// Configure the logging providers.
/// </summary>
/// <remarks>
/// Replaces the Opc.Ua.Core default ILogger with a
/// Microsoft.Extension.Logger with a Serilog file, debug and console logger.
/// The debug logger is only enabled for debug builds.
/// The console logger is enabled by the logConsole flag at the consoleLogLevel.
/// The file logger uses the setting in the ApplicationConfiguration.
/// The Trace logLevel is chosen if required by the Tracemasks.
/// </remarks>
/// <param name="configuration">The application configuration.</param>
/// <param name="context">The context name for the logger. </param>
/// <param name="logConsole">Enable logging to the console.</param>
/// <param name="consoleLogLevel">The LogLevel to use for the console/debug.<
/// /param>
public static void ConfigureLogging(
ApplicationConfiguration configuration,
string context,
bool logConsole,
LogLevel consoleLogLevel)
{
var loggerConfiguration = new LoggerConfiguration()
.Enrich.FromLogContext();
if (logConsole)
{
loggerConfiguration.WriteTo.Console(
restrictedToMinimumLevel: (LogEventLevel)consoleLogLevel
);
}
#if DEBUG
else
{
loggerConfiguration
.WriteTo.Debug(restrictedToMinimumLevel: (LogEventLevel)consoleLogLevel);
}
#endif
LogLevel fileLevel = LogLevel.Information;
// switch for Trace/Verbose output
var traceMasks = configuration.TraceConfiguration.TraceMasks;
if ((traceMasks & ~(TraceMasks.Information | TraceMasks.Error |
TraceMasks.Security | TraceMasks.StartStop | TraceMasks.StackTrace)) != 0)
{
fileLevel = LogLevel.Trace;
}
// add file logging if configured
var outputFilePath = configuration.TraceConfiguration.OutputFilePath;
if (!string.IsNullOrWhiteSpace(outputFilePath))
{
loggerConfiguration.WriteTo.File(
new ExpressionTemplate("{UtcDateTime(@t):yyyy-MM-dd HH:mm:ss.fff} [{@l:u3}] {@m}\n{@x}"),
ReplaceSpecialFolderNames(outputFilePath),
restrictedToMinimumLevel: (LogEventLevel)fileLevel,
rollOnFileSizeLimit: true);
}
// adjust minimum level
if (fileLevel < LogLevel.Information || consoleLogLevel < LogLevel.Information)
{
loggerConfiguration.MinimumLevel.Verbose();
}
// create the serilog logger
var serilogger = loggerConfiguration
.CreateLogger();
// create the ILogger for Opc.Ua.Core
var logger = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Trace))
.AddSerilog(serilogger)
.CreateLogger(context);
// set logger interface, disables TraceEvent
SetLogger(logger);
}
/// <summary>
/// Output log messages.
/// </summary>
public static void LogTest()
{
// print legacy logging output, for testing
Trace(TraceMasks.Error, "This is an Error message: {0}", TraceMasks.Error);
Trace(TraceMasks.Information, "This is a Information message: {0}", TraceMasks.Information);
Trace(TraceMasks.StackTrace, "This is a StackTrace message: {0}", TraceMasks.StackTrace);
Trace(TraceMasks.Service, "This is a Service message: {0}", TraceMasks.Service);
Trace(TraceMasks.ServiceDetail, "This is a ServiceDetail message: {0}", TraceMasks.ServiceDetail);
Trace(TraceMasks.Operation, "This is a Operation message: {0}", TraceMasks.Operation);
Trace(TraceMasks.OperationDetail, "This is a OperationDetail message: {0}", TraceMasks.OperationDetail);
Trace(TraceMasks.StartStop, "This is a StartStop message: {0}", TraceMasks.StartStop);
Trace(TraceMasks.ExternalSystem, "This is a ExternalSystem message: {0}", TraceMasks.ExternalSystem);
Trace(TraceMasks.Security, "This is a Security message: {0}", TraceMasks.Security);
// print ILogger logging output
LogTrace("This is a Trace message: {0}", LogLevel.Trace);
LogDebug("This is a Debug message: {0}", LogLevel.Debug);
LogInfo("This is a Info message: {0}", LogLevel.Information);
LogWarning("This is a Warning message: {0}", LogLevel.Warning);
LogError("This is a Error message: {0}", LogLevel.Error);
LogCritical("This is a Critical message: {0}", LogLevel.Critical);
}
/// <summary>
/// Create an event which is set if a user
/// enters the Ctrl-C key combination.
/// </summary>
public static ManualResetEvent CtrlCHandler()
{
var quitEvent = new ManualResetEvent(false);
try
{
Console.CancelKeyPress += (_, eArgs) => {
quitEvent.Set();
eArgs.Cancel = true;
};
}
catch
{
// intentionally left blank
}
return quitEvent;
}
}
}
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/core/runtime:3.1
FROM mcr.microsoft.com/dotnet/runtime:6.0
COPY ./publish /publish
WORKDIR /publish
@@ -1,5 +1,5 @@
/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
@@ -27,10 +27,6 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.Text;
namespace Quickstarts.ReferenceServer
{
/// <summary>
@@ -27,75 +27,15 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using Mono.Options;
using Opc.Ua;
using Opc.Ua.Configuration;
using Opc.Ua.Server;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Opc.Ua;
namespace Quickstarts.ReferenceServer
{
/// <summary>
/// A dialog which asks for user input.
/// </summary>
public class ApplicationMessageDlg : IApplicationMessageDlg
{
private string m_message = string.Empty;
private bool m_ask = false;
public override void Message(string text, bool ask)
{
m_message = text;
m_ask = ask;
}
public override async Task<bool> ShowAsync()
{
if (m_ask)
{
m_message += " (y/n, default y): ";
Console.Write(m_message);
}
else
{
Console.WriteLine(m_message);
}
if (m_ask)
{
try
{
ConsoleKeyInfo result = Console.ReadKey();
Console.WriteLine();
return await Task.FromResult((result.KeyChar == 'y') || (result.KeyChar == 'Y') || (result.KeyChar == '\r')).ConfigureAwait(false);
}
catch
{
// intentionally fall through
}
}
return await Task.FromResult(true).ConfigureAwait(false);
}
}
/// <summary>
/// The error code why the server exited.
/// </summary>
public enum ExitCode : int
{
Ok = 0,
ErrorServerNotStarted = 0x80,
ErrorServerRunning = 0x81,
ErrorServerException = 0x82,
ErrorInvalidCommandLine = 0x100
};
/// <summary>
/// The program.
/// </summary>
@@ -103,241 +43,109 @@ namespace Quickstarts.ReferenceServer
{
public static async Task<int> Main(string[] args)
{
Console.WriteLine("{0} OPC UA Reference Server", Utils.IsRunningOnMono() ? "Mono" : ".Net Core");
TextWriter output = Console.Out;
output.WriteLine("{0} OPC UA Reference Server", Utils.IsRunningOnMono() ? "Mono" : ".NET Core");
output.WriteLine("OPC UA library: {0} @ {1} -- {2}",
Utils.GetAssemblyBuildNumber(),
Utils.GetAssemblyTimestamp().ToString("G", CultureInfo.InvariantCulture),
Utils.GetAssemblySoftwareVersion());
// The application name and config file names
var applicationName = Utils.IsRunningOnMono() ? "MonoReferenceServer" : "ConsoleReferenceServer";
var configSectionName = Utils.IsRunningOnMono() ? "Quickstarts.MonoReferenceServer" : "Quickstarts.ReferenceServer";
// command line options
bool showHelp = false;
bool autoAccept = false;
bool console = false;
bool logConsole = false;
bool appLog = false;
bool renewCertificate = false;
bool shadowConfig = false;
string password = null;
int timeout = -1;
var usage = Utils.IsRunningOnMono() ? $"Usage: mono {applicationName}.exe [OPTIONS]" : $"Usage: dotnet {applicationName}.dll [OPTIONS]";
Mono.Options.OptionSet options = new Mono.Options.OptionSet {
usage,
{ "h|help", "show this message and exit", h => showHelp = h != null },
{ "a|autoaccept", "auto accept certificates (for testing only)", a => autoAccept = a != null },
{ "c|console", "log trace to console", c => console = c != null },
{ "p|password=", "optional password for private key", (string p) => password = p }
{ "c|console", "log to console", c => logConsole = c != null },
{ "l|log", "log app output", c => appLog = c != null },
{ "p|password=", "optional password for private key", (string p) => password = p },
{ "r|renew", "renew application certificate", r => renewCertificate = r != null },
{ "t|timeout=", "timeout in seconds to exit application", (int t) => timeout = t * 1000 },
{ "s|shadowconfig", "create configuration in pki root", s => shadowConfig = s != null },
};
try
{
IList<string> extraArgs = options.Parse(args);
foreach (string extraArg in extraArgs)
// parse command line and set options
ConsoleUtils.ProcessCommandLine(output, args, options, ref showHelp);
if (logConsole && appLog)
{
Console.WriteLine("Error: Unknown option: {0}", extraArg);
showHelp = true;
output = new LogWriter();
}
}
catch (OptionException e)
{
Console.WriteLine(e.Message);
showHelp = true;
}
if (showHelp)
{
Console.WriteLine(Utils.IsRunningOnMono() ? "Usage: mono MonoReferenceServer.exe [OPTIONS]" : "Usage: dotnet ConsoleReferenceServer.dll [OPTIONS]");
Console.WriteLine();
Console.WriteLine("Options:");
options.WriteOptionDescriptions(Console.Out);
return (int)ExitCode.ErrorInvalidCommandLine;
}
var server = new MyRefServer() {
AutoAccept = autoAccept,
LogConsole = console,
Password = password
};
await server.Run().ConfigureAwait(false);
return (int)server.ExitCode;
}
}
public class MyRefServer
{
private ReferenceServer m_server;
private Task m_status;
private DateTime m_lastEventTime;
public bool LogConsole { get; set; } = false;
public bool AutoAccept { get; set; } = false;
public string Password { get; set; } = null;
public ExitCode ExitCode { get; private set; }
public async Task Run()
{
try
{
ExitCode = ExitCode.ErrorServerNotStarted;
await StartConsoleReferenceServerAsync().ConfigureAwait(false);
Console.WriteLine("Server started. Press Ctrl-C to exit...");
ExitCode = ExitCode.ErrorServerRunning;
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex.Message);
ExitCode = ExitCode.ErrorServerException;
return;
}
var quitEvent = new ManualResetEvent(false);
try
{
Console.CancelKeyPress += (sender, eArgs) => {
quitEvent.Set();
eArgs.Cancel = true;
// create the UA server
var server = new UAServer<ReferenceServer>(output) {
AutoAccept = autoAccept,
Password = password
};
}
catch
{
}
// wait for timeout or Ctrl-C
quitEvent.WaitOne();
// load the server configuration, validate certificates
output.WriteLine("Loading configuration from {0}.", configSectionName);
await server.LoadAsync(applicationName, configSectionName).ConfigureAwait(false);
if (m_server != null)
{
Console.WriteLine("Server stopped. Waiting for exit...");
using (ReferenceServer server = m_server)
// use the shadow config to map the config to an externally accessible location
if (shadowConfig)
{
// Stop status thread
m_server = null;
m_status.Wait();
// Stop server and dispose
server.Stop();
}
}
ExitCode = ExitCode.Ok;
}
private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
{
if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
{
if (AutoAccept)
{
if (!LogConsole)
output.WriteLine("Using shadow configuration.");
var shadowPath = Directory.GetParent(Path.GetDirectoryName(
Utils.ReplaceSpecialFolderNames(server.Configuration.TraceConfiguration.OutputFilePath))).FullName;
var shadowFilePath = Path.Combine(shadowPath, Path.GetFileName(server.Configuration.SourceFilePath));
if (!File.Exists(shadowFilePath))
{
Console.WriteLine("Accepted Certificate: {0}", e.Certificate.Subject);
output.WriteLine("Create a copy of the config in the shadow location.");
File.Copy(server.Configuration.SourceFilePath, shadowFilePath, true);
}
Utils.Trace(Utils.TraceMasks.Security, "Accepted Certificate: {0}", e.Certificate.Subject);
e.Accept = true;
return;
output.WriteLine("Reloading configuration from {0}.", shadowFilePath);
await server.LoadAsync(applicationName, Path.Combine(shadowPath, configSectionName)).ConfigureAwait(false);
}
// setup the logging
ConsoleUtils.ConfigureLogging(server.Configuration, applicationName, logConsole, LogLevel.Information);
// check or renew the certificate
output.WriteLine("Check the certificate.");
await server.CheckCertificateAsync(renewCertificate).ConfigureAwait(false);
// Create and add the node managers
server.Create(Servers.Utils.NodeManagerFactories);
// start the server
output.WriteLine("Start the server.");
await server.StartAsync().ConfigureAwait(false);
output.WriteLine("Server started. Press Ctrl-C to exit...");
// wait for timeout or Ctrl-C
var quitEvent = ConsoleUtils.CtrlCHandler();
bool ctrlc = quitEvent.WaitOne(timeout);
// stop server. May have to wait for clients to disconnect.
output.WriteLine("Server stopped. Waiting for exit...");
await server.StopAsync().ConfigureAwait(false);
return (int)ExitCode.Ok;
}
if (!LogConsole)
catch (ErrorExitException eee)
{
Console.WriteLine("Rejected Certificate: {0} {1}", e.Error, e.Certificate.Subject);
}
Utils.Trace(Utils.TraceMasks.Security, "Rejected Certificate: {0} {1}", e.Error, e.Certificate.Subject);
}
private async Task StartConsoleReferenceServerAsync()
{
ApplicationInstance.MessageDlg = new ApplicationMessageDlg();
CertificatePasswordProvider PasswordProvider = new CertificatePasswordProvider(Password);
ApplicationInstance application = new ApplicationInstance {
ApplicationName = "Quickstart Reference Server",
ApplicationType = ApplicationType.Server,
ConfigSectionName = Utils.IsRunningOnMono() ? "Quickstarts.MonoReferenceServer" : "Quickstarts.ReferenceServer",
CertificatePasswordProvider = PasswordProvider
};
// load the application configuration.
ApplicationConfiguration config = await application.LoadApplicationConfiguration(false).ConfigureAwait(false);
var loggerConfiguration = new Serilog.LoggerConfiguration();
if (LogConsole)
{
loggerConfiguration.WriteTo.Console(restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Warning);
}
#if DEBUG
else
{
loggerConfiguration.WriteTo.Debug(restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Warning);
}
#endif
SerilogTraceLogger.Create(loggerConfiguration, config);
// check the application certificate.
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(
false, CertificateFactory.DefaultKeySize, CertificateFactory.DefaultLifeTime).ConfigureAwait(false);
if (!haveAppCertificate)
{
throw new Exception("Application instance certificate invalid!");
}
if (!config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
{
config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
}
// start the server.
m_server = new ReferenceServer();
await application.Start(m_server).ConfigureAwait(false);
// print endpoint info
var endpoints = application.Server.GetEndpoints().Select(e => e.EndpointUrl).Distinct();
foreach (var endpoint in endpoints)
{
Console.WriteLine(endpoint);
}
// start the status thread
m_status = Task.Run(new Action(StatusThreadAsync));
// print notification on session events
m_server.CurrentInstance.SessionManager.SessionActivated += EventStatus;
m_server.CurrentInstance.SessionManager.SessionClosing += EventStatus;
m_server.CurrentInstance.SessionManager.SessionCreated += EventStatus;
}
private void EventStatus(Session session, SessionEventReason reason)
{
m_lastEventTime = DateTime.UtcNow;
PrintSessionStatus(session, reason.ToString());
}
private void PrintSessionStatus(Session session, string reason, bool lastContact = false)
{
lock (session.DiagnosticsLock)
{
StringBuilder item = new StringBuilder();
item.AppendFormat("{0,9}:{1,20}:", reason, session.SessionDiagnostics.SessionName);
if (lastContact)
{
item.AppendFormat("Last Event:{0:HH:mm:ss}", session.SessionDiagnostics.ClientLastContactTime.ToLocalTime());
}
else
{
if (session.Identity != null)
{
item.AppendFormat(":{0,20}", session.Identity.DisplayName);
}
item.AppendFormat(":{0}", session.Id);
}
Console.WriteLine(item.ToString());
}
}
private async void StatusThreadAsync()
{
while (m_server != null)
{
if (DateTime.UtcNow - m_lastEventTime > TimeSpan.FromMilliseconds(6000))
{
IList<Session> sessions = m_server.CurrentInstance.SessionManager.GetSessions();
for (int ii = 0; ii < sessions.Count; ii++)
{
Session session = sessions[ii];
PrintSessionStatus(session, "-Status-", true);
}
m_lastEventTime = DateTime.UtcNow;
}
await Task.Delay(1000).ConfigureAwait(false);
output.WriteLine("The application exits with error: {0}", eee.Message);
return (int)eee.ExitCode;
}
}
}
}
@@ -24,7 +24,7 @@
<StorePath>%LocalApplicationData%/OPC Foundation/pki/issuer</StorePath>
</TrustedIssuerCertificates>
<!-- Where the trust list is stored (UA Applications) -->
<!-- Where the trust list is stored -->
<TrustedPeerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/trusted</StorePath>
@@ -41,8 +41,8 @@
<AutoAcceptUntrustedCertificates>false</AutoAcceptUntrustedCertificates>
<!-- WARNING: SHA1 signed certficates are by default rejected and should be phased out.
The setting below to allow them is only required for UACTT (1.02.336.244) which uses SHA-1 signed certs. -->
<RejectSHA1SignedCertificates>false</RejectSHA1SignedCertificates>
only nano and embedded profiles are allowed to use sha1 signed certificates. -->
<RejectSHA1SignedCertificates>true</RejectSHA1SignedCertificates>
<RejectUnknownRevocationStatus>true</RejectUnknownRevocationStatus>
<MinimumCertificateKeySize>2048</MinimumCertificateKeySize>
<AddAppCertToTrustedStore>false</AddAppCertToTrustedStore>
@@ -63,7 +63,7 @@
<TransportConfigurations></TransportConfigurations>
<TransportQuotas>
<OperationTimeout>600000</OperationTimeout>
<OperationTimeout>120000</OperationTimeout>
<MaxStringLength>1048576</MaxStringLength>
<MaxByteStringLength>1048576</MaxByteStringLength>
<MaxArrayLength>65535</MaxArrayLength>
@@ -141,32 +141,35 @@
<!-- Allows anonymous users -->
<ua:UserTokenPolicy>
<ua:TokenType>Anonymous_0</ua:TokenType>
<ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#None</ua:SecurityPolicyUri>
<!-- <ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#None</ua:SecurityPolicyUri> -->
</ua:UserTokenPolicy>
<!-- Allows username/password -->
<ua:UserTokenPolicy>
<ua:TokenType>UserName_1</ua:TokenType>
<!-- passwords must be encrypted - this specifies what algorithm to use -->
<ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri>
<!-- if no algorithm is specified, the active security policy is used -->
<!-- <ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri> -->
</ua:UserTokenPolicy>
<!-- Allows user certificates -->
<ua:UserTokenPolicy>
<ua:TokenType>Certificate_2</ua:TokenType>
<!-- certificate possession must be proven with a digital signature - this specifies what algorithm to use -->
<ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri>
<!-- if no algorithm is specified, the active security policy is used -->
<!-- <ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri> -->
</ua:UserTokenPolicy>
</UserTokenPolicies>
<DiagnosticsEnabled>true</DiagnosticsEnabled>
<MaxSessionCount>100</MaxSessionCount>
<!-- Settings for CTT testing -->
<MaxSessionCount>75</MaxSessionCount>
<MinSessionTimeout>10000</MinSessionTimeout>
<MaxSessionTimeout>3600000</MaxSessionTimeout>
<MaxBrowseContinuationPoints>10</MaxBrowseContinuationPoints>
<MaxQueryContinuationPoints>10</MaxQueryContinuationPoints>
<MaxHistoryContinuationPoints>100</MaxHistoryContinuationPoints>
<MaxRequestAge>600000</MaxRequestAge>
<MinPublishingInterval>100</MinPublishingInterval>
<MinPublishingInterval>50</MinPublishingInterval>
<MaxPublishingInterval>3600000</MaxPublishingInterval>
<PublishingResolution>50</PublishingResolution>
<MaxSubscriptionLifetime>3600000</MaxSubscriptionLifetime>
@@ -224,6 +227,7 @@
<ua:String>http://opcfoundation.org/UA-Profile/Server/DataAccess</ua:String>
<ua:String>http://opcfoundation.org/UA-Profile/Server/Methods</ua:String>
<ua:String>http://opcfoundation.org/UA-Profile/Server/ReverseConnect</ua:String>
<ua:String>http://opcfoundation.org/UA-Profile/Server/ClientRedundancy</ua:String>
</ServerProfileArray>
<ShutdownDelay>5</ShutdownDelay>
<ServerCapabilities>
@@ -235,6 +239,7 @@
</SupportedPrivateKeyFormats>
<MaxTrustListSize>0</MaxTrustListSize>
<MultiCastDnsEnabled>false</MultiCastDnsEnabled>
<!-- Reverse connection parameters for aggregation server sample -->
<!--
<ReverseConnect>
@@ -250,7 +255,43 @@
<RejectTimeout>60000</RejectTimeout>
</ReverseConnect>
-->
<OperationLimits>
<MaxNodesPerRead>2500</MaxNodesPerRead>
<MaxNodesPerHistoryReadData>1000</MaxNodesPerHistoryReadData>
<MaxNodesPerHistoryReadEvents>1000</MaxNodesPerHistoryReadEvents>
<MaxNodesPerWrite>2500</MaxNodesPerWrite>
<MaxNodesPerHistoryUpdateData>1000</MaxNodesPerHistoryUpdateData>
<MaxNodesPerHistoryUpdateEvents>1000</MaxNodesPerHistoryUpdateEvents>
<MaxNodesPerMethodCall>2500</MaxNodesPerMethodCall>
<MaxNodesPerBrowse>2500</MaxNodesPerBrowse>
<MaxNodesPerRegisterNodes>2500</MaxNodesPerRegisterNodes>
<MaxNodesPerTranslateBrowsePathsToNodeIds>2500</MaxNodesPerTranslateBrowsePathsToNodeIds>
<MaxNodesPerNodeManagement>2500</MaxNodesPerNodeManagement>
<MaxMonitoredItemsPerCall>2500</MaxMonitoredItemsPerCall>
</OperationLimits>
</ServerConfiguration>
<Extensions>
<ua:XmlElement>
<MemoryBufferConfiguration xmlns="http://samples.org/UA/MemoryBuffer">
<Buffers>
<MemoryBufferInstance>
<Name>UInt32</Name>
<TagCount>100</TagCount>
<DataType>UInt32</DataType>
</MemoryBufferInstance>
<MemoryBufferInstance>
<Name>Double</Name>
<TagCount>100</TagCount>
<DataType>Double</DataType>
</MemoryBufferInstance>
</Buffers>
</MemoryBufferConfiguration>
</ua:XmlElement>
</Extensions>
<TraceConfiguration>
<OutputFilePath>%LocalApplicationData%/OPC Foundation/Logs/Quickstarts.ReferenceServer.log.txt</OutputFilePath>
<DeleteOnLoad>true</DeleteOnLoad>
@@ -265,7 +306,7 @@
<!-- Show Only Security, Service Calls, Errors and Trace -->
<!-- <TraceMasks>523</TraceMasks> -->
<!-- Show Only Security, ServiceResultExceptions, Errors and Trace -->
<!-- <TraceMasks>519</TraceMasks> -->
<TraceMasks>519</TraceMasks>
</TraceConfiguration>
</ApplicationConfiguration>
@@ -0,0 +1,290 @@
/* ========================================================================
* 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 System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Opc.Ua;
using Opc.Ua.Configuration;
using Opc.Ua.Server;
namespace Quickstarts
{
public class UAServer<T> where T : StandardServer, new()
{
public ApplicationInstance Application => m_application;
public ApplicationConfiguration Configuration => m_application.ApplicationConfiguration;
public bool AutoAccept { get; set; }
public string Password { get; set; }
public ExitCode ExitCode { get; private set; }
/// <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>
public async Task LoadAsync(string applicationName, string configSectionName)
{
try
{
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
};
// load the application configuration.
await m_application.LoadApplicationConfiguration(false).ConfigureAwait(false);
}
catch (Exception ex)
{
throw new ErrorExitException(ex.Message, ExitCode);
}
}
/// <summary>
/// Load the application configuration.
/// </summary>
public async Task CheckCertificateAsync(bool renewCertificate)
{
try
{
var config = m_application.ApplicationConfiguration;
if (renewCertificate)
{
await m_application.DeleteApplicationInstanceCertificate().ConfigureAwait(false);
}
// check the application certificate.
bool haveAppCertificate = await m_application.CheckApplicationInstanceCertificate(false, minimumKeySize: 0).ConfigureAwait(false);
if (!haveAppCertificate)
{
throw new ErrorExitException("Application instance certificate invalid!");
}
if (!config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
{
config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
}
}
catch (Exception ex)
{
throw new ErrorExitException(ex.Message, ExitCode);
}
}
/// <summary>
/// Create server instance and add node managers.
/// </summary>
public void Create(IList<INodeManagerFactory> nodeManagerFactories)
{
try
{
// create the server.
m_server = new T();
if (nodeManagerFactories != null)
{
foreach (var factory in nodeManagerFactories)
{
m_server.AddNodeManager(factory);
}
}
}
catch (Exception ex)
{
throw new ErrorExitException(ex.Message, ExitCode);
}
}
/// <summary>
/// Start the server.
/// </summary>
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);
// save state
ExitCode = ExitCode.ErrorRunning;
// print endpoint info
var endpoints = m_application.Server.GetEndpoints().Select(e => e.EndpointUrl).Distinct();
foreach (var endpoint in endpoints)
{
m_output.WriteLine(endpoint);
}
// start the status thread
m_status = Task.Run(StatusThreadAsync);
// print notification on session events
m_server.CurrentInstance.SessionManager.SessionActivated += EventStatus;
m_server.CurrentInstance.SessionManager.SessionClosing += EventStatus;
m_server.CurrentInstance.SessionManager.SessionCreated += EventStatus;
}
catch (Exception ex)
{
throw new ErrorExitException(ex.Message, ExitCode);
}
}
/// <summary>
/// Stops the server.
/// </summary>
public async Task StopAsync()
{
try
{
if (m_server != null)
{
using (T server = m_server)
{
// Stop status thread
m_server = null;
await m_status.ConfigureAwait(false);
// Stop server and dispose
server.Stop();
}
}
ExitCode = ExitCode.Ok;
}
catch (Exception ex)
{
throw new ErrorExitException(ex.Message, ExitCode.ErrorStopping);
}
}
/// <summary>
/// The certificate validator is used
/// if auto accept is not selected in the configuration.
/// </summary>
private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
{
if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
{
if (AutoAccept)
{
m_output.WriteLine("Accepted Certificate: [{0}] [{1}]", e.Certificate.Subject, e.Certificate.Thumbprint);
e.Accept = true;
return;
}
}
m_output.WriteLine("Rejected Certificate: {0} [{1}] [{2}]", e.Error, e.Certificate.Subject, e.Certificate.Thumbprint);
}
/// <summary>
/// Update the session status.
/// </summary>
private void EventStatus(Session session, SessionEventReason reason)
{
m_lastEventTime = DateTime.UtcNow;
PrintSessionStatus(session, reason.ToString());
}
/// <summary>
/// Output the status of a connected session.
/// </summary>
private void PrintSessionStatus(Session session, string reason, bool lastContact = false)
{
StringBuilder item = new StringBuilder();
lock (session.DiagnosticsLock)
{
item.AppendFormat("{0,9}:{1,20}:", reason, session.SessionDiagnostics.SessionName);
if (lastContact)
{
item.AppendFormat("Last Event:{0:HH:mm:ss}", session.SessionDiagnostics.ClientLastContactTime.ToLocalTime());
}
else
{
if (session.Identity != null)
{
item.AppendFormat(":{0,20}", session.Identity.DisplayName);
}
item.AppendFormat(":{0}", session.Id);
}
}
m_output.WriteLine(item.ToString());
}
/// <summary>
/// Status thread, prints connection status every 10 seconds.
/// </summary>
private async Task StatusThreadAsync()
{
while (m_server != null)
{
if (DateTime.UtcNow - m_lastEventTime > TimeSpan.FromMilliseconds(10000))
{
IList<Session> sessions = m_server.CurrentInstance.SessionManager.GetSessions();
for (int ii = 0; ii < sessions.Count; ii++)
{
Session session = sessions[ii];
PrintSessionStatus(session, "-Status-", true);
}
m_lastEventTime = DateTime.UtcNow;
}
await Task.Delay(1000).ConfigureAwait(false);
}
}
#region Private Members
private readonly TextWriter m_output;
private ApplicationInstance m_application;
private T m_server;
private Task m_status;
private DateTime m_lastEventTime;
#endregion
}
}
@@ -1,4 +1,5 @@
REM build a docker container of the console reference server
dotnet build ConsoleReferenceServer.csproj
dotnet publish ConsoleReferenceServer.csproj -o ./publish
docker build -t consolerefserver .
set buildoptions=--configuration Release -p:NoHttps=true --framework net6.0
dotnet build %buildoptions% ConsoleReferenceServer.csproj
dotnet publish %buildoptions% ConsoleReferenceServer.csproj -o ./publish
docker build -f Dockerfile -t consolerefserver .
@@ -1,5 +1,6 @@
#!/bin/bash
echo build a docker container of the .NET Core reference server
dotnet build ConsoleReferenceServer.csproj
dotnet publish ConsoleReferenceServer.csproj -o ./publish
sudo docker build -t consolerefserver .
echo build a docker container of the .NET Core reference server, without https support
buildoptions="--configuration Release -p:NoHttps=true --framework net6.0"
dotnet build $buildoptions ConsoleReferenceServer.csproj
dotnet publish $buildoptions ConsoleReferenceServer.csproj -o ./publish
sudo docker build -f Dockerfile -t consolerefserver .
@@ -1,3 +1,6 @@
echo Run a docker container of the console reference server
echo The certificate store of the ref server is mapped to './OPC Foundation'
docker run -it -p 62541:62541 -h refserver -v "%CD%/OPC Foundation:/root/.local/share/OPC Foundation" consolerefserver:latest
@echo off
echo Run the local docker container of the console reference server
echo By default, the certificate store of the ref server is mapped to '.\OPC Foundation\pki'
echo A log file is created at '.\OPC Foundation\Logs\Quickstarts.ReferenceServer.log.txt'
echo A shadow configuration file for customization is created in '.\OPC Foundation\Quickstarts.ReferenceServer.Config.xml'
docker run -it -p 62541:62541 -h %COMPUTERNAME% -v "%CD%/OPC Foundation:/root/.local/share/OPC Foundation" consolerefserver:latest -c -s
@@ -1,4 +1,7 @@
#!/bin/bash
echo Run a docker container of the console reference server
echo The certificate store of the ref server is mapped to './OPC Foundation'
sudo docker run -it -p 62541:62541 -h refserver -v "$(pwd)/OPC Foundation:/root/.local/share/OPC Foundation" consolerefserver:latest
set echo off
echo Run the local docker container of the console reference server
echo By default, the certificate store of the ref server is mapped to './OPC Foundation/pki'
echo A log file is created at './OPC Foundation/Logs/Quickstarts.ReferenceServer.log.txt'
echo A shadow configuration file for customization is created in './OPC Foundation/Quickstarts.ReferenceServer.Config.xml'
sudo docker run -it -p 62541:62541 -h $HOSTNAME -v "$(pwd)/OPC Foundation:/root/.local/share/OPC Foundation" consolerefserver:latest -c -s
@@ -0,0 +1,7 @@
@echo off
echo Run the local docker container of the console reference server
echo By default, the certificate store of the ref server is mapped to '.\OPC Foundation\pki'
echo A log file is created at '.\OPC Foundation\Logs\Quickstarts.ReferenceServer.log.txt'
echo A shadow configuration file for customization is created in '.\OPC Foundation\Quickstarts.ReferenceServer.Config.xml'
docker pull ghcr.io/opcfoundation/uanetstandard/refserver:latest
docker run -it -p 62541:62541 -h %COMPUTERNAME% -v "%CD%/OPC Foundation:/root/.local/share/OPC Foundation" ghcr.io/opcfoundation/uanetstandard/refserver:latest -c -s
@@ -0,0 +1,8 @@
#!/bin/bash
set echo off
echo Run the local docker container of the console reference server
echo By default, the certificate store of the ref server is mapped to './OPC Foundation/pki'
echo A log file is created at './OPC Foundation/Logs/Quickstarts.ReferenceServer.log.txt'
echo A shadow configuration file for customization is created in './OPC Foundation/Quickstarts.ReferenceServer.Config.xml'
sudo docker pull ghcr.io/opcfoundation/uanetstandard/refserver:latest
sudo docker run -it -p 62541:62541 -h $HOSTNAME -v "$(pwd)/OPC Foundation:/root/.local/share/OPC Foundation" ghcr.io/opcfoundation/uanetstandard/refserver:latest -c -s
@@ -0,0 +1,3 @@
REM collect a trace using the EventSource provider OPC-UA-Core
dotnet tool install --global dotnet-trace
dotnet-trace collect --name consolereferenceserver --providers OPC-UA-Core,OPC-UA-Server
@@ -11,6 +11,10 @@
<RootNamespace>Quickstarts.ConsoleReferenceSubscriber</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.PubSub\Opc.Ua.PubSub.csproj" />
@@ -28,10 +28,15 @@
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.Threading;
using Mono.Options;
using Opc.Ua;
using Opc.Ua.PubSub;
using Opc.Ua.PubSub.Configuration;
using Opc.Ua.PubSub.Encoding;
using Opc.Ua.PubSub.PublishedData;
using Opc.Ua.PubSub.Transport;
namespace Quickstarts.ConsoleReferenceSubscriber
{
@@ -40,23 +45,112 @@ namespace Quickstarts.ConsoleReferenceSubscriber
public const ushort NamespaceIndexSimple = 2;
public const ushort NamespaceIndexAllTypes = 3;
// constant DateTime that represents the initial time when the metadata for the configuration was created
private static DateTime kTimeOfConfiguration = new DateTime(2021, 5, 1, 0, 0, 0, DateTimeKind.Utc);
private const string kDisplaySeparator = "------------------------------------------------";
private static object m_lock = new object();
public static void Main(string[] args)
{
Console.WriteLine("OPC UA Console Reference Subscriber");
// command line options
bool showHelp = false;
bool useMqttJson = true;
bool useMqttUadp = false;
bool useUdpUadp = false;
string subscriberUrl = null;
Mono.Options.OptionSet options = new Mono.Options.OptionSet {
{ "h|help", "Show usage information", v => showHelp = v != null },
{ "m|mqtt_json", "Use MQTT with Json encoding Profile. This is the default option.", v => useMqttJson = v != null },
{ "p|mqtt_uadp", "Use MQTT with UADP encoding Profile.", v => useMqttUadp = v != null },
{ "u|udp_uadp", "Use UDP with UADP encoding Profile", v => useUdpUadp = v != null },
{ "url|subscriber_url=", "Subscriber Url Address", v => subscriberUrl = v},
};
IList<string> extraArgs = null;
try
{
// Define the configuration of UA Subscriber application
PubSubConfigurationDataType pubSubConfiguration = CreateSubscriberConfiguration();
extraArgs = options.Parse(args);
if (extraArgs.Count > 0)
{
foreach (string extraArg in extraArgs)
{
Console.WriteLine("Error: Unknown option: {0}", extraArg);
showHelp = true;
}
}
}
catch (OptionException e)
{
Console.WriteLine(e.Message);
showHelp = true;
}
if (showHelp)
{
Console.WriteLine("Usage: dotnet ConsoleReferenceSubscriber.dll [OPTIONS]");
Console.WriteLine();
Console.WriteLine("Options:");
options.WriteOptionDescriptions(Console.Out);
return;
}
try
{
InitializeLog();
PubSubConfigurationDataType pubSubConfiguration = null;
if (useUdpUadp)
{
// set default UDP Subscriber Url to local multicast if not sent in args.
if (string.IsNullOrEmpty(subscriberUrl))
{
subscriberUrl = "opc.udp://239.0.0.1:4840";
}
// Create configuration using UDP protocol and UADP Encoding
pubSubConfiguration = CreateSubscriberConfiguration_UdpUadp(subscriberUrl);
Console.WriteLine("The Pubsub Connection was initialized using UDP & UADP Profile.");
}
else
{
// set default MQTT Broker Url to localhost if not sent in args.
if (string.IsNullOrEmpty(subscriberUrl))
{
subscriberUrl = "mqtt://localhost:1883";
}
if (useMqttUadp)
{
// Create configuration using MQTT protocol and UADP Encoding
pubSubConfiguration = CreateSubscriberConfiguration_MqttUadp(subscriberUrl);
Console.WriteLine("The PubSub Connection was initialized using MQTT & UADP Profile.");
}
else
{
// Create configuration using MQTT protocol and JSON Encoding
pubSubConfiguration = CreateSubscriberConfiguration_MqttJson(subscriberUrl);
Console.WriteLine("The PubSub Connection was initialized using MQTT & JSON Profile.");
}
}
// Create the UA Publisher application
using (UaPubSubApplication uaPubSubApplication = UaPubSubApplication.Create(pubSubConfiguration))
{
// Subscribte to RawDataReceived event
uaPubSubApplication.RawDataReceived += UaPubSubApplication_RawDataReceived;
// Subscribte to DataReceived event
uaPubSubApplication.DataReceived += UaPubSubApplication_DataReceived;
// Subscribte to MetaDataReceived event
uaPubSubApplication.MetaDataReceived += UaPubSubApplication_MetaDataDataReceived;
uaPubSubApplication.ConfigurationUpdating += UaPubSubApplication_ConfigurationUpdating;
// Start the publisher
uaPubSubApplication.Start();
@@ -88,48 +182,573 @@ namespace Quickstarts.ConsoleReferenceSubscriber
}
}
private static void UaPubSubApplication_DataReceived(object sender, SubscribedDataEventArgs e)
#region Private Methods
/// <summary>
/// Handler for <see cref="UaPubSubApplication.RawDataReceived" /> event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void UaPubSubApplication_RawDataReceived(object sender, RawDataReceivedEventArgs e)
{
lock (m_lock)
{
Console.WriteLine("Data Received from Source={0}, SequenceNumber={1}, DataSet count={2}",
e.SourceEndPoint, e.NetworkMessageSequenceNumber, e.DataSets.Count);
Console.WriteLine("RawDataReceived bytes:{0}, Source:{1}, TransportProtocol:{2}, MessageMapping:{3}",
e.Message.Length, e.Source, e.TransportProtocol, e.MessageMapping);
foreach (DataSet dataSet in e.DataSets)
{
Console.WriteLine("\tDataSet.Name={0}, DataSetWriterId={1}", dataSet.Name, dataSet.DataSetWriterId);
for (int i = 0; i < dataSet.Fields.Length; i++)
{
Console.WriteLine("\t\tTargetNodeId:{0}, Attribute:{1}, Value:{2}",
dataSet.Fields[i].TargetNodeId, dataSet.Fields[i].TargetAttribute, dataSet.Fields[i].Value);
}
}
Console.WriteLine("------------------------------------------------");
Console.WriteLine(kDisplaySeparator);
}
}
/// <summary>
/// Creates a PubSubConfiguration object programmatically.
/// Handler for <see cref="UaPubSubApplication.DataReceived" /> event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void UaPubSubApplication_DataReceived(object sender, SubscribedDataEventArgs e)
{
lock (m_lock)
{
Console.WriteLine("DataReceived event:");
if (e.NetworkMessage is UadpNetworkMessage)
{
Console.WriteLine("UADP Network DataSetMessage ({0} DataSets): Source={1}, SequenceNumber={2}",
e.NetworkMessage.DataSetMessages.Count, e.Source, ((UadpNetworkMessage)e.NetworkMessage).SequenceNumber);
}
else if (e.NetworkMessage is JsonNetworkMessage)
{
Console.WriteLine("JSON Network DataSetMessage ({0} DataSets): Source={1}, MessageId={2}",
e.NetworkMessage.DataSetMessages.Count, e.Source, ((JsonNetworkMessage)e.NetworkMessage).MessageId);
}
foreach (UaDataSetMessage dataSetMessage in e.NetworkMessage.DataSetMessages)
{
DataSet dataSet = dataSetMessage.DataSet;
Console.WriteLine("\tDataSet.Name={0}, DataSetWriterId={1}, SequenceNumber={2}", dataSet.Name, dataSet.DataSetWriterId, dataSetMessage.SequenceNumber);
for (int i = 0; i < dataSet.Fields.Length; i++)
{
Console.WriteLine("\t\tTargetNodeId:{0}, Attribute:{1}, Value:{2}",
dataSet.Fields[i].TargetNodeId, dataSet.Fields[i].TargetAttribute, dataSetMessage.DataSet.Fields[i].Value);
}
}
Console.WriteLine(kDisplaySeparator);
}
}
/// <summary>
/// Handler for <see cref="UaPubSubApplication.MetaDataDataReceived" /> event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void UaPubSubApplication_MetaDataDataReceived(object sender, SubscribedDataEventArgs e)
{
lock (m_lock)
{
Console.WriteLine("MetaDataDataReceived event:");
if (e.NetworkMessage is JsonNetworkMessage)
{
Console.WriteLine("JSON Network MetaData Message: Source={0}, PublisherId={1}, DataSetWriterId={2} Fields count={3}\n",
e.Source,
((JsonNetworkMessage)e.NetworkMessage).PublisherId,
((JsonNetworkMessage)e.NetworkMessage).DataSetWriterId,
e.NetworkMessage.DataSetMetaData.Fields.Count);
}
if (e.NetworkMessage is UadpNetworkMessage)
{
Console.WriteLine("UADP Network MetaData Message: Source={0}, PublisherId={1}, DataSetWriterId={2} Fields count={3}\n",
e.Source,
((UadpNetworkMessage)e.NetworkMessage).PublisherId,
((UadpNetworkMessage)e.NetworkMessage).DataSetWriterId,
e.NetworkMessage.DataSetMetaData.Fields.Count);
}
Console.WriteLine("\tMetaData.Name={0}, MajorVersion={1} MinorVersion={2}",
e.NetworkMessage.DataSetMetaData.Name,
e.NetworkMessage.DataSetMetaData.ConfigurationVersion.MajorVersion,
e.NetworkMessage.DataSetMetaData.ConfigurationVersion.MinorVersion);
foreach (FieldMetaData metaDataField in e.NetworkMessage.DataSetMetaData.Fields)
{
Console.WriteLine("\t\t{0, -20} DataType:{1, 10}, ValueRank:{2, 5}", metaDataField.Name, metaDataField.DataType, metaDataField.ValueRank);
}
Console.WriteLine(kDisplaySeparator);
}
}
/// <summary>
/// Handler for <see cref="UaPubSubApplication.ConfigurationUpdating"/>
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void UaPubSubApplication_ConfigurationUpdating(object sender, ConfigurationUpdatingEventArgs e)
{
Console.WriteLine("The UaPubSubApplication.ConfigurationUpdating event was triggered for part: {0} for {1}, With new value: {2}",
e.ChangedProperty, e.Parent.GetType().Name, e.NewValue.GetType().Name);
Console.WriteLine(kDisplaySeparator);
}
/// <summary>
/// Creates a Subscriber PubSubConfiguration object for UDP & UADP programmatically.
/// </summary>
/// <returns></returns>
public static PubSubConfigurationDataType CreateSubscriberConfiguration()
private static PubSubConfigurationDataType CreateSubscriberConfiguration_UdpUadp(string urlAddress)
{
// Define a PubSub connection with PublisherId 100
// Define a PubSub connection with PublisherId 1
PubSubConnectionDataType pubSubConnection1 = new PubSubConnectionDataType();
pubSubConnection1.Name = "UADPConnection1";
pubSubConnection1.Name = "Subscriber Connection UDP UADP";
pubSubConnection1.Enabled = true;
pubSubConnection1.PublisherId = (UInt16)100;
pubSubConnection1.TransportProfileUri = Profiles.UadpTransport;
pubSubConnection1.PublisherId = (UInt16)1;
pubSubConnection1.TransportProfileUri = Profiles.PubSubUdpUadpTransport;
NetworkAddressUrlDataType address = new NetworkAddressUrlDataType();
// Specify the local Network interface name to be used
// e.g. address.NetworkInterface = "Ethernet";
// Leave empty to subscribe on all available local interfaces.
address.NetworkInterface = String.Empty;
address.Url = "opc.udp://239.0.0.1:4840";
address.Url = urlAddress;
pubSubConnection1.Address = new ExtensionObject(address);
#region Define 'Simple' MetaData
// configure custoom DicoveryAddress for Dicovery messages
pubSubConnection1.TransportSettings = new ExtensionObject() {
Body = new DatagramConnectionTransportDataType() {
DiscoveryAddress = new ExtensionObject() {
Body = new NetworkAddressUrlDataType() {
Url = "opc.udp://224.0.2.15:4840"
}
}
}
};
#region Define ReaderGroup1
ReaderGroupDataType readerGroup1 = new ReaderGroupDataType();
readerGroup1.Name = "ReaderGroup 1";
readerGroup1.Enabled = true;
readerGroup1.MaxNetworkMessageSize = 1500;
readerGroup1.MessageSettings = new ExtensionObject(new ReaderGroupMessageDataType());
readerGroup1.TransportSettings = new ExtensionObject(new ReaderGroupTransportDataType());
#region Define DataSetReader 'Simple' for PublisherId = (UInt16)1, DataSetWriterId = 1
DataSetReaderDataType dataSetReaderSimple = new DataSetReaderDataType();
dataSetReaderSimple.Name = "Reader 1 UDP UADP";
dataSetReaderSimple.PublisherId = (UInt16)1;
dataSetReaderSimple.WriterGroupId = 0;
dataSetReaderSimple.DataSetWriterId = 1;
dataSetReaderSimple.Enabled = true;
dataSetReaderSimple.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetReaderSimple.KeyFrameCount = 1;
dataSetReaderSimple.TransportSettings = new ExtensionObject(new DataSetReaderTransportDataType());
UadpDataSetReaderMessageDataType uadpDataSetReaderMessage = new UadpDataSetReaderMessageDataType() {
GroupVersion = 0,
NetworkMessageNumber = 0,
NetworkMessageContentMask = (uint)(uint)(UadpNetworkMessageContentMask.PublisherId
| UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.WriterGroupId
| UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber
| UadpNetworkMessageContentMask.SequenceNumber),
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetReaderSimple.MessageSettings = new ExtensionObject(uadpDataSetReaderMessage);
// Create and set DataSetMetaData for DataSet Simple
DataSetMetaDataType simpleMetaData = CreateDataSetMetaDataSimple();
dataSetReaderSimple.DataSetMetaData = simpleMetaData;
// Create and set SubscribedDataSet
TargetVariablesDataType subscribedDataSet = new TargetVariablesDataType();
subscribedDataSet.TargetVariables = new FieldTargetDataTypeCollection();
foreach (var fieldMetaData in simpleMetaData.Fields)
{
subscribedDataSet.TargetVariables.Add(new FieldTargetDataType() {
DataSetFieldId = fieldMetaData.DataSetFieldId,
TargetNodeId = new NodeId(fieldMetaData.Name, NamespaceIndexSimple),
AttributeId = Attributes.Value,
OverrideValueHandling = OverrideValueHandling.OverrideValue,
OverrideValue = new Variant(TypeInfo.GetDefaultValue(fieldMetaData.DataType, (int)ValueRanks.Scalar))
});
}
dataSetReaderSimple.SubscribedDataSet = new ExtensionObject(subscribedDataSet);
#endregion
readerGroup1.DataSetReaders.Add(dataSetReaderSimple);
#region Define DataSetReader 'AllTypes' for PublisherId = (UInt16)1, DataSetWriterId = 2
DataSetReaderDataType dataSetReaderAllTypes = new DataSetReaderDataType();
dataSetReaderAllTypes.Name = "Reader 2 UDP UADP";
dataSetReaderAllTypes.PublisherId = (UInt16)1;
dataSetReaderAllTypes.WriterGroupId = 0;
dataSetReaderAllTypes.DataSetWriterId = 2;
dataSetReaderAllTypes.Enabled = true;
dataSetReaderAllTypes.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetReaderAllTypes.KeyFrameCount = 1;
dataSetReaderAllTypes.TransportSettings = new ExtensionObject(new DataSetReaderTransportDataType());
uadpDataSetReaderMessage = new UadpDataSetReaderMessageDataType() {
GroupVersion = 0,
NetworkMessageNumber = 0,
NetworkMessageContentMask = (uint)(uint)(UadpNetworkMessageContentMask.PublisherId
| UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.WriterGroupId
| UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber
| UadpNetworkMessageContentMask.SequenceNumber),
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetReaderAllTypes.MessageSettings = new ExtensionObject(uadpDataSetReaderMessage);
// Create and set DataSetMetaData for DataSet AllTypes
DataSetMetaDataType allTypesMetaData = CreateDataSetMetaDataAllTypes();
dataSetReaderAllTypes.DataSetMetaData = allTypesMetaData;
// Create and set SubscribedDataSet
subscribedDataSet = new TargetVariablesDataType();
subscribedDataSet.TargetVariables = new FieldTargetDataTypeCollection();
foreach (var fieldMetaData in allTypesMetaData.Fields)
{
subscribedDataSet.TargetVariables.Add(new FieldTargetDataType() {
DataSetFieldId = fieldMetaData.DataSetFieldId,
TargetNodeId = new NodeId(fieldMetaData.Name, NamespaceIndexAllTypes),
AttributeId = Attributes.Value,
OverrideValueHandling = OverrideValueHandling.OverrideValue,
OverrideValue = new Variant(TypeInfo.GetDefaultValue(fieldMetaData.DataType, (int)ValueRanks.Scalar))
});
}
dataSetReaderAllTypes.SubscribedDataSet = new ExtensionObject(subscribedDataSet);
#endregion
readerGroup1.DataSetReaders.Add(dataSetReaderAllTypes);
#endregion
pubSubConnection1.ReaderGroups.Add(readerGroup1);
//create pub sub configuration root object
PubSubConfigurationDataType pubSubConfiguration = new PubSubConfigurationDataType();
pubSubConfiguration.Connections = new PubSubConnectionDataTypeCollection()
{
pubSubConnection1
};
return pubSubConfiguration;
}
/// <summary>
/// Creates a Subscriber PubSubConfiguration object for MQTT & Json programmatically.
/// </summary>
/// <returns></returns>
private static PubSubConfigurationDataType CreateSubscriberConfiguration_MqttJson(string urlAddress)
{
// Define a PubSub connection with PublisherId 2
PubSubConnectionDataType pubSubConnection1 = new PubSubConnectionDataType();
pubSubConnection1.Name = "Subscriber Connection MQTT Json";
pubSubConnection1.Enabled = true;
pubSubConnection1.PublisherId = (UInt16)2;
pubSubConnection1.TransportProfileUri = Profiles.PubSubMqttJsonTransport;
NetworkAddressUrlDataType address = new NetworkAddressUrlDataType();
// Specify the local Network interface name to be used
// e.g. address.NetworkInterface = "Ethernet";
// Leave empty to subscribe on all available local interfaces.
address.NetworkInterface = String.Empty;
address.Url = urlAddress;
pubSubConnection1.Address = new ExtensionObject(address);
// Configure the mqtt specific configuration with the MQTTbroker
ITransportProtocolConfiguration mqttConfiguration = new MqttClientProtocolConfiguration(version: EnumMqttProtocolVersion.V500);
pubSubConnection1.ConnectionProperties = mqttConfiguration.ConnectionProperties;
string brokerQueueName = "Json_WriterGroup_1";
string brokerMetaData = "$Metadata";
#region Define ReaderGroup1
ReaderGroupDataType readerGroup1 = new ReaderGroupDataType();
readerGroup1.Name = "ReaderGroup 1";
readerGroup1.Enabled = true;
readerGroup1.MaxNetworkMessageSize = 1500;
readerGroup1.MessageSettings = new ExtensionObject(new ReaderGroupMessageDataType());
readerGroup1.TransportSettings = new ExtensionObject(new ReaderGroupTransportDataType());
#region Define DataSetReader1 'Simple' for PublisherId = (UInt16)2, DataSetWriterId = 1
DataSetReaderDataType dataSetReaderSimple = new DataSetReaderDataType();
dataSetReaderSimple.Name = "Reader 1 MQTT JSON Variant Encoding";
dataSetReaderSimple.PublisherId = (UInt16)2;
dataSetReaderSimple.WriterGroupId = 1;
dataSetReaderSimple.DataSetWriterId = 1;
dataSetReaderSimple.Enabled = true;
dataSetReaderSimple.DataSetFieldContentMask = (uint)DataSetFieldContentMask.None;// Variant encoding;
dataSetReaderSimple.KeyFrameCount = 3;
JsonDataSetReaderMessageDataType jsonDataSetReaderMessage = new JsonDataSetReaderMessageDataType() {
NetworkMessageContentMask = (uint)(uint)(JsonNetworkMessageContentMask.NetworkMessageHeader
| JsonNetworkMessageContentMask.DataSetMessageHeader
| JsonNetworkMessageContentMask.PublisherId
| JsonNetworkMessageContentMask.DataSetClassId
| JsonNetworkMessageContentMask.ReplyTo),
DataSetMessageContentMask = (uint)(JsonDataSetMessageContentMask.DataSetWriterId
| JsonDataSetMessageContentMask.MetaDataVersion
| JsonDataSetMessageContentMask.SequenceNumber
| JsonDataSetMessageContentMask.Status
| JsonDataSetMessageContentMask.Timestamp),
};
dataSetReaderSimple.MessageSettings = new ExtensionObject(jsonDataSetReaderMessage);
BrokerDataSetReaderTransportDataType brokerTransportSettings = new BrokerDataSetReaderTransportDataType() {
QueueName = brokerQueueName,
RequestedDeliveryGuarantee = BrokerTransportQualityOfService.BestEffort,
MetaDataQueueName = $"{brokerQueueName}/{brokerMetaData}",
};
dataSetReaderSimple.TransportSettings = new ExtensionObject(brokerTransportSettings);
// Create and set DataSetMetaData for DataSet Simple
DataSetMetaDataType simpleMetaData = CreateDataSetMetaDataSimple();
dataSetReaderSimple.DataSetMetaData = simpleMetaData;
// Create and set SubscribedDataSet
TargetVariablesDataType subscribedDataSet = new TargetVariablesDataType();
subscribedDataSet.TargetVariables = new FieldTargetDataTypeCollection();
foreach (var fieldMetaData in simpleMetaData.Fields)
{
subscribedDataSet.TargetVariables.Add(new FieldTargetDataType() {
DataSetFieldId = fieldMetaData.DataSetFieldId,
TargetNodeId = new NodeId(fieldMetaData.Name, NamespaceIndexSimple),
AttributeId = Attributes.Value,
OverrideValueHandling = OverrideValueHandling.OverrideValue,
OverrideValue = new Variant(TypeInfo.GetDefaultValue(fieldMetaData.DataType, (int)ValueRanks.Scalar))
});
}
dataSetReaderSimple.SubscribedDataSet = new ExtensionObject(subscribedDataSet);
#endregion
readerGroup1.DataSetReaders.Add(dataSetReaderSimple);
#region Define DataSetReader2 'AllTypes' for PublisherId = (UInt16)2, DataSetWriterId = 2
DataSetReaderDataType dataSetReaderAllTypes = new DataSetReaderDataType();
dataSetReaderAllTypes.Name = "Reader 2 MQTT JSON RawData Encoding";
dataSetReaderAllTypes.PublisherId = (UInt16)2;
dataSetReaderAllTypes.WriterGroupId = 1;
dataSetReaderAllTypes.DataSetWriterId = 2;
dataSetReaderAllTypes.Enabled = true;
dataSetReaderAllTypes.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;// RawData encoding;
dataSetReaderAllTypes.KeyFrameCount = 1;
jsonDataSetReaderMessage = new JsonDataSetReaderMessageDataType() {
NetworkMessageContentMask = (uint)(JsonNetworkMessageContentMask.NetworkMessageHeader
| JsonNetworkMessageContentMask.DataSetMessageHeader
| JsonNetworkMessageContentMask.PublisherId
| JsonNetworkMessageContentMask.DataSetClassId
| JsonNetworkMessageContentMask.ReplyTo),
DataSetMessageContentMask = (uint)(JsonDataSetMessageContentMask.DataSetWriterId
| JsonDataSetMessageContentMask.MetaDataVersion
| JsonDataSetMessageContentMask.SequenceNumber
| JsonDataSetMessageContentMask.Status
| JsonDataSetMessageContentMask.Timestamp),
};
dataSetReaderAllTypes.MessageSettings = new ExtensionObject(jsonDataSetReaderMessage);
brokerTransportSettings = new BrokerDataSetReaderTransportDataType() {
QueueName = brokerQueueName,
RequestedDeliveryGuarantee = BrokerTransportQualityOfService.BestEffort,
MetaDataQueueName = $"{brokerQueueName}/{brokerMetaData}",
};
dataSetReaderAllTypes.TransportSettings = new ExtensionObject(brokerTransportSettings);
// Create and set DataSetMetaData for DataSet AllTypes
DataSetMetaDataType allTypesMetaData = CreateDataSetMetaDataAllTypes();
dataSetReaderAllTypes.DataSetMetaData = allTypesMetaData;
// Create and set SubscribedDataSet
subscribedDataSet = new TargetVariablesDataType();
subscribedDataSet.TargetVariables = new FieldTargetDataTypeCollection();
foreach (var fieldMetaData in allTypesMetaData.Fields)
{
subscribedDataSet.TargetVariables.Add(new FieldTargetDataType() {
DataSetFieldId = fieldMetaData.DataSetFieldId,
TargetNodeId = new NodeId(fieldMetaData.Name, NamespaceIndexAllTypes),
AttributeId = Attributes.Value,
OverrideValueHandling = OverrideValueHandling.OverrideValue,
OverrideValue = new Variant(TypeInfo.GetDefaultValue(fieldMetaData.DataType, (int)ValueRanks.Scalar))
});
}
dataSetReaderAllTypes.SubscribedDataSet = new ExtensionObject(subscribedDataSet);
#endregion
readerGroup1.DataSetReaders.Add(dataSetReaderAllTypes);
#endregion
pubSubConnection1.ReaderGroups.Add(readerGroup1);
//create pub sub configuration root object
PubSubConfigurationDataType pubSubConfiguration = new PubSubConfigurationDataType();
pubSubConfiguration.Connections = new PubSubConnectionDataTypeCollection()
{
pubSubConnection1
};
return pubSubConfiguration;
}
/// <summary>
/// Creates a Subscriber PubSubConfiguration object for UDP & UADP programmatically.
/// </summary>
/// <returns></returns>
private static PubSubConfigurationDataType CreateSubscriberConfiguration_MqttUadp(string urlAddress)
{
// Define a PubSub connection with PublisherId 3
PubSubConnectionDataType pubSubConnection1 = new PubSubConnectionDataType();
pubSubConnection1.Name = "Subscriber Connection MQTT UADP";
pubSubConnection1.Enabled = true;
pubSubConnection1.PublisherId = (UInt16)3;
pubSubConnection1.TransportProfileUri = Profiles.PubSubMqttUadpTransport;
NetworkAddressUrlDataType address = new NetworkAddressUrlDataType();
// Specify the local Network interface name to be used
// e.g. address.NetworkInterface = "Ethernet";
// Leave empty to subscribe on all available local interfaces.
address.NetworkInterface = String.Empty;
address.Url = urlAddress;
pubSubConnection1.Address = new ExtensionObject(address);
// Configure the mqtt specific configuration with the MQTTbroker
ITransportProtocolConfiguration mqttConfiguration = new MqttClientProtocolConfiguration(version: EnumMqttProtocolVersion.V500);
pubSubConnection1.ConnectionProperties = mqttConfiguration.ConnectionProperties;
string brokerQueueName = "Uadp_WriterGroup_1";
string brokerMetaData = "$Metadata";
#region Define ReaderGroup1
ReaderGroupDataType readerGroup1 = new ReaderGroupDataType();
readerGroup1.Name = "ReaderGroup 1";
readerGroup1.Enabled = true;
readerGroup1.MaxNetworkMessageSize = 1500;
readerGroup1.MessageSettings = new ExtensionObject(new ReaderGroupMessageDataType());
readerGroup1.TransportSettings = new ExtensionObject(new ReaderGroupTransportDataType());
#region Define DataSetReader 'Simple' for PublisherId = (UInt16)1, DataSetWriterId = 1
DataSetReaderDataType dataSetReaderSimple = new DataSetReaderDataType();
dataSetReaderSimple.Name = "Reader 1 MQTT UADP";
dataSetReaderSimple.PublisherId = (UInt16)3;
dataSetReaderSimple.WriterGroupId = 0;
dataSetReaderSimple.DataSetWriterId = 1;
dataSetReaderSimple.Enabled = true;
dataSetReaderSimple.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetReaderSimple.KeyFrameCount = 1;
BrokerDataSetReaderTransportDataType brokerTransportSettings = new BrokerDataSetReaderTransportDataType() {
QueueName = brokerQueueName,
MetaDataQueueName = $"{brokerQueueName}/{brokerMetaData}",
};
dataSetReaderSimple.TransportSettings = new ExtensionObject(brokerTransportSettings);
UadpDataSetReaderMessageDataType uadpDataSetReaderMessage = new UadpDataSetReaderMessageDataType() {
GroupVersion = 0,
NetworkMessageNumber = 0,
NetworkMessageContentMask = (uint)(uint)(UadpNetworkMessageContentMask.PublisherId
| UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.WriterGroupId
| UadpNetworkMessageContentMask.PayloadHeader
| UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber
| UadpNetworkMessageContentMask.SequenceNumber),
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetReaderSimple.MessageSettings = new ExtensionObject(uadpDataSetReaderMessage);
// Create and set DataSetMetaData for DataSet Simple
DataSetMetaDataType simpleMetaData = CreateDataSetMetaDataSimple();
dataSetReaderSimple.DataSetMetaData = simpleMetaData;
// Create and set SubscribedDataSet
TargetVariablesDataType subscribedDataSet = new TargetVariablesDataType();
subscribedDataSet.TargetVariables = new FieldTargetDataTypeCollection();
foreach (var fieldMetaData in simpleMetaData.Fields)
{
subscribedDataSet.TargetVariables.Add(new FieldTargetDataType() {
DataSetFieldId = fieldMetaData.DataSetFieldId,
TargetNodeId = new NodeId(fieldMetaData.Name, NamespaceIndexSimple),
AttributeId = Attributes.Value,
OverrideValueHandling = OverrideValueHandling.OverrideValue,
OverrideValue = new Variant(TypeInfo.GetDefaultValue(fieldMetaData.DataType, (int)ValueRanks.Scalar))
});
}
dataSetReaderSimple.SubscribedDataSet = new ExtensionObject(subscribedDataSet);
#endregion
readerGroup1.DataSetReaders.Add(dataSetReaderSimple);
#region Define DataSetReader 'AllTypes' for PublisherId = (UInt16)1, DataSetWriterId = 2
DataSetReaderDataType dataSetReaderAllTypes = new DataSetReaderDataType();
dataSetReaderAllTypes.Name = "Reader 2 MQTT UADP";
dataSetReaderAllTypes.PublisherId = (UInt16)3;
dataSetReaderAllTypes.WriterGroupId = 0;
dataSetReaderAllTypes.DataSetWriterId = 2;
dataSetReaderAllTypes.Enabled = true;
dataSetReaderAllTypes.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetReaderAllTypes.KeyFrameCount = 1;
dataSetReaderAllTypes.TransportSettings = new ExtensionObject(brokerTransportSettings);
uadpDataSetReaderMessage = new UadpDataSetReaderMessageDataType() {
GroupVersion = 0,
NetworkMessageNumber = 0,
NetworkMessageContentMask = (uint)(uint)(UadpNetworkMessageContentMask.PublisherId
| UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.WriterGroupId
| UadpNetworkMessageContentMask.PayloadHeader
| UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber
| UadpNetworkMessageContentMask.SequenceNumber),
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetReaderAllTypes.MessageSettings = new ExtensionObject(uadpDataSetReaderMessage);
// Create and set DataSetMetaData for DataSet AllTypes
DataSetMetaDataType allTypesMetaData = CreateDataSetMetaDataAllTypes();
dataSetReaderAllTypes.DataSetMetaData = allTypesMetaData;
// Create and set SubscribedDataSet
subscribedDataSet = new TargetVariablesDataType();
subscribedDataSet.TargetVariables = new FieldTargetDataTypeCollection();
foreach (var fieldMetaData in allTypesMetaData.Fields)
{
subscribedDataSet.TargetVariables.Add(new FieldTargetDataType() {
DataSetFieldId = fieldMetaData.DataSetFieldId,
TargetNodeId = new NodeId(fieldMetaData.Name, NamespaceIndexAllTypes),
AttributeId = Attributes.Value,
OverrideValueHandling = OverrideValueHandling.OverrideValue,
OverrideValue = new Variant(TypeInfo.GetDefaultValue(fieldMetaData.DataType, (int)ValueRanks.Scalar))
});
}
dataSetReaderAllTypes.SubscribedDataSet = new ExtensionObject(subscribedDataSet);
#endregion
readerGroup1.DataSetReaders.Add(dataSetReaderAllTypes);
#endregion
pubSubConnection1.ReaderGroups.Add(readerGroup1);
//create pub sub configuration root object
PubSubConfigurationDataType pubSubConfiguration = new PubSubConfigurationDataType();
pubSubConfiguration.Connections = new PubSubConnectionDataTypeCollection()
{
pubSubConnection1
};
return pubSubConfiguration;
}
/// <summary>
/// Creates the "Simple" DataSetMetaData
/// </summary>
/// <returns></returns>
private static DataSetMetaDataType CreateDataSetMetaDataSimple()
{
DataSetMetaDataType simpleMetaData = new DataSetMetaDataType();
simpleMetaData.DataSetClassId = new Uuid(Guid.Empty);
simpleMetaData.Name = "Simple";
@@ -168,23 +787,31 @@ namespace Quickstarts.ConsoleReferenceSubscriber
ValueRank = ValueRanks.Scalar
},
};
// set the ConfigurationVersion relative to kTimeOfConfiguration constant
simpleMetaData.ConfigurationVersion = new ConfigurationVersionDataType() {
MinorVersion = 1,
MajorVersion = 1
MinorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration),
MajorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration)
};
#endregion
#region Define 'AllTypes' Metadata
return simpleMetaData;
}
/// <summary>
/// Creates the "AllTypes" DataSetMetaData
/// </summary>
/// <returns></returns>
private static DataSetMetaDataType CreateDataSetMetaDataAllTypes()
{
DataSetMetaDataType allTypesMetaData = new DataSetMetaDataType();
allTypesMetaData.DataSetClassId = new Uuid(Guid.Empty);
allTypesMetaData.Name = "AllTypes";
allTypesMetaData.Fields = new FieldMetaDataCollection()
{
new FieldMetaData()
{
new FieldMetaData()
{
Name = "BoolToggle",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.Boolean,
BuiltInType = (byte)DataTypes.Boolean,
DataType = DataTypeIds.Boolean,
ValueRank = ValueRanks.Scalar
},
@@ -192,7 +819,7 @@ namespace Quickstarts.ConsoleReferenceSubscriber
{
Name = "Byte",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.Byte,
BuiltInType = (byte)DataTypes.Byte,
DataType = DataTypeIds.Byte,
ValueRank = ValueRanks.Scalar
},
@@ -200,7 +827,7 @@ namespace Quickstarts.ConsoleReferenceSubscriber
{
Name = "Int16",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.Int16,
BuiltInType = (byte)DataTypes.Int16,
DataType = DataTypeIds.Int16,
ValueRank = ValueRanks.Scalar
},
@@ -208,7 +835,7 @@ namespace Quickstarts.ConsoleReferenceSubscriber
{
Name = "Int32",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.Int32,
BuiltInType = (byte)DataTypes.Int32,
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.Scalar
},
@@ -216,7 +843,7 @@ namespace Quickstarts.ConsoleReferenceSubscriber
{
Name = "SByte",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.SByte,
BuiltInType = (byte)DataTypes.SByte,
DataType = DataTypeIds.SByte,
ValueRank = ValueRanks.Scalar
},
@@ -224,7 +851,7 @@ namespace Quickstarts.ConsoleReferenceSubscriber
{
Name = "UInt16",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.UInt16,
BuiltInType = (byte)DataTypes.UInt16,
DataType = DataTypeIds.UInt16,
ValueRank = ValueRanks.Scalar
},
@@ -232,15 +859,23 @@ namespace Quickstarts.ConsoleReferenceSubscriber
{
Name = "UInt32",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.UInt32,
BuiltInType = (byte)DataTypes.UInt32,
DataType = DataTypeIds.UInt32,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "UInt64",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.UInt64,
DataType = DataTypeIds.UInt64,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "Float",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.Float,
BuiltInType = (byte)DataTypes.Float,
DataType = DataTypeIds.Float,
ValueRank = ValueRanks.Scalar
},
@@ -248,113 +883,70 @@ namespace Quickstarts.ConsoleReferenceSubscriber
{
Name = "Double",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte) DataTypes.Double,
BuiltInType = (byte)DataTypes.Double,
DataType = DataTypeIds.Double,
ValueRank = ValueRanks.Scalar
},
};
allTypesMetaData.ConfigurationVersion = new ConfigurationVersionDataType() {
MinorVersion = 1,
MajorVersion = 1
};
#endregion
#region Define ReaderGroup1
ReaderGroupDataType readerGroup1 = new ReaderGroupDataType();
readerGroup1.Name = "ReaderGroup 1";
readerGroup1.Enabled = true;
readerGroup1.MaxNetworkMessageSize = 1500;
readerGroup1.MessageSettings = new ExtensionObject(new ReaderGroupMessageDataType());
readerGroup1.TransportSettings = new ExtensionObject(new ReaderGroupTransportDataType());
#region Define DataSetReader 'Simple' for PublisherId = (UInt16)100, DataSetWriterId = 1
DataSetReaderDataType dataSetReaderSimple = new DataSetReaderDataType();
dataSetReaderSimple.Name = "Reader 1";
dataSetReaderSimple.PublisherId = (UInt16)100;
dataSetReaderSimple.WriterGroupId = 0;
dataSetReaderSimple.DataSetWriterId = 0;
dataSetReaderSimple.Enabled = true;
dataSetReaderSimple.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetReaderSimple.KeyFrameCount = 1;
dataSetReaderSimple.DataSetMetaData = simpleMetaData;
UadpDataSetReaderMessageDataType uadpDataSetReaderMessage = new UadpDataSetReaderMessageDataType() {
GroupVersion = 0,
DataSetOffset = 15,
NetworkMessageNumber = 0,
NetworkMessageContentMask = (uint)(uint)(UadpNetworkMessageContentMask.PublisherId | UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.WriterGroupId | UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber | UadpNetworkMessageContentMask.SequenceNumber),
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetReaderSimple.MessageSettings = new ExtensionObject(uadpDataSetReaderMessage);
TargetVariablesDataType subscribedDataSet = new TargetVariablesDataType();
subscribedDataSet.TargetVariables = new FieldTargetDataTypeCollection();
foreach (var fieldMetaData in simpleMetaData.Fields)
{
subscribedDataSet.TargetVariables.Add(new FieldTargetDataType() {
DataSetFieldId = fieldMetaData.DataSetFieldId,
TargetNodeId = new NodeId(fieldMetaData.Name, NamespaceIndexSimple),
AttributeId = Attributes.Value,
OverrideValueHandling = OverrideValueHandling.OverrideValue,
OverrideValue = new Variant(TypeInfo.GetDefaultValue(fieldMetaData.DataType, (int)ValueRanks.Scalar))
});
}
dataSetReaderSimple.SubscribedDataSet = new ExtensionObject(subscribedDataSet);
#endregion
readerGroup1.DataSetReaders.Add(dataSetReaderSimple);
#region Define DataSetReader 'AllTypes' for PublisherId = (UInt16)100, DataSetWriterId = 2
DataSetReaderDataType dataSetReaderAllTypes = new DataSetReaderDataType();
dataSetReaderAllTypes.Name = "Reader 2";
dataSetReaderAllTypes.PublisherId = (UInt16)100;
dataSetReaderAllTypes.WriterGroupId = 0;
dataSetReaderAllTypes.DataSetWriterId = 0;
dataSetReaderAllTypes.Enabled = true;
dataSetReaderAllTypes.DataSetFieldContentMask = (uint)DataSetFieldContentMask.RawData;
dataSetReaderAllTypes.KeyFrameCount = 1;
dataSetReaderAllTypes.DataSetMetaData = allTypesMetaData;
uadpDataSetReaderMessage = new UadpDataSetReaderMessageDataType() {
GroupVersion = 0,
DataSetOffset = 47,
NetworkMessageNumber = 0,
NetworkMessageContentMask = (uint)(uint)(UadpNetworkMessageContentMask.PublisherId | UadpNetworkMessageContentMask.GroupHeader
| UadpNetworkMessageContentMask.WriterGroupId | UadpNetworkMessageContentMask.GroupVersion
| UadpNetworkMessageContentMask.NetworkMessageNumber | UadpNetworkMessageContentMask.SequenceNumber),
DataSetMessageContentMask = (uint)(UadpDataSetMessageContentMask.Status | UadpDataSetMessageContentMask.SequenceNumber),
};
dataSetReaderAllTypes.MessageSettings = new ExtensionObject(uadpDataSetReaderMessage);
subscribedDataSet = new TargetVariablesDataType();
subscribedDataSet.TargetVariables = new FieldTargetDataTypeCollection();
foreach (var fieldMetaData in allTypesMetaData.Fields)
{
subscribedDataSet.TargetVariables.Add(new FieldTargetDataType() {
DataSetFieldId = fieldMetaData.DataSetFieldId,
TargetNodeId = new NodeId(fieldMetaData.Name, NamespaceIndexAllTypes),
AttributeId = Attributes.Value,
OverrideValueHandling = OverrideValueHandling.OverrideValue,
OverrideValue = new Variant(TypeInfo.GetDefaultValue(fieldMetaData.DataType, (int)ValueRanks.Scalar))
});
}
dataSetReaderAllTypes.SubscribedDataSet = new ExtensionObject(subscribedDataSet);
#endregion
readerGroup1.DataSetReaders.Add(dataSetReaderAllTypes);
#endregion
pubSubConnection1.ReaderGroups.Add(readerGroup1);
//create pub sub configuration root object
PubSubConfigurationDataType pubSubConfiguration = new PubSubConfigurationDataType();
pubSubConfiguration.Connections = new PubSubConnectionDataTypeCollection()
{
pubSubConnection1
new FieldMetaData()
{
Name = "String",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.String,
DataType = DataTypeIds.String,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "ByteString",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.ByteString,
DataType = DataTypeIds.ByteString,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "Guid",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.Guid,
DataType = DataTypeIds.Guid,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "DateTime",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.DateTime,
DataType = DataTypeIds.DateTime,
ValueRank = ValueRanks.Scalar
},
new FieldMetaData()
{
Name = "UInt32Array",
DataSetFieldId = new Uuid(Guid.NewGuid()),
BuiltInType = (byte)DataTypes.UInt32,
DataType = DataTypeIds.UInt32,
ValueRank = ValueRanks.OneDimension
},
};
// set the ConfigurationVersion relative to kTimeOfConfiguration constant
allTypesMetaData.ConfigurationVersion = new ConfigurationVersionDataType() {
MinorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration),
MajorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration)
};
return pubSubConfiguration;
return allTypesMetaData;
}
/// <summary>
/// Initialize logging
/// </summary>
private static void InitializeLog()
{
// Initialize logger
Utils.SetTraceLog("%CommonApplicationData%\\OPC Foundation\\Logs\\Quickstarts.ConsoleReferenceSubscriber.log.txt", true);
Utils.SetTraceMask(Utils.TraceMasks.Error);
Utils.SetTraceOutput(Utils.TraceOutput.DebugAndFile);
}
#endregion
}
}
@@ -0,0 +1,92 @@
# OPC Foundation UA .NET Standard Library - Console Reference Subscriber
## Introduction
This OPC application was created to provide the sample code for creating Subscriber applications using the OPC Foundation UA .NET Standard PubSub Library. There is a .NET Core 3.1 (2.1) console version of the Subscriber which runs on any OS supporting [.NET Standard](https://docs.microsoft.com/en-us/dotnet/articles/standard).
The Reference Subscriber is configured to run in parallel with the [Console Reference Publisher](../ConsoleReferencePublisher/README.md)
## How to build and run the Windows OPC UA Reference Server from Visual Studio
1. Open the solution **UA Reference.sln** with Visual Studio 2019.
2. Choose the project `ConsoleReferenceSubscriber` in the Solution Explorer and set it with a right click as `Startup Project`.
3. Hit `F5` to build and execute the sample.
## How to build and run the console OPC UA Reference Subscriber on Windows, Linux and iOS
This section describes how to run the **ConsoleReferenceSubscriber**.
Please follow instructions in this [article](https://aka.ms/dotnetcoregs) to setup the dotnet command line environment for your platform.
## Start the Subscriber
1. Open a command prompt.
2. Navigate to the folder **Applications/ConsoleReferenceSubscriber**.
3. To run the Subscriber sample type
`dotnet run --project ConsoleReferenceSubscriber.csproj --framework netcoreapp3.1.`
The Subscriber will start and listen for network messages sent by the Reference Publisher.
## Command Line Arguments for *ConsoleReferenceSubscriber*
**ConsoleReferenceSubscriber** can be executed using the following command line arguments:
- -h|help - Shows usage information
- -m|mqtt_json - Creates a connection using there MQTT with Json encoding Profile. This is the default option.
- -u|udp_uadp - Creates a connection using there UDP with UADP encoding Profile.
To run the Subscriber sample using a connection with MQTT with Json encoding execute:
dotnet run --project ConsoleReferenceSubscriber.csproj --framework netcoreapp3.1
or
dotnet run --project ConsoleReferenceSubscriber.csproj --framework netcoreapp3.1 -m
To run the Subscriber sample using a connection with the UDP with UADP encoding execute:
dotnet run --project ConsoleReferenceSubscriber.csproj --framework netcoreapp3.1 -u
# Programmer's Guide
To create a new OPC UA Subscriber application:
- Open Microsoft Visual Studio 2019 environment,
- Create a new project and give it a name,
- Add a reference to the [OPCFoundation.NetStandard.Opc.Ua.PubSub NuGet package](https://www.nuget.org/packages/OPCFoundation.NetStandard.Opc.Ua.PubSub/),
- Initialize Subscriber application (see [Subscriber Initialization](#subscriber-initialization)).
## Subscriber Initialization
The following four steps are required to implement a functional Subscriber:
1. Create [Subscriber Configuration](#subscriber-configuration).
// Create configuration using UDP protocol and UADP Encoding
PubSubConfigurationDataType pubSubConfiguration = CreateSubscriberConfiguration_UdpUadp();
Or use the alternative configuration object for MQTT with JSON encoding
// Create configuration using MQTT protocol and JSON Encoding
PubSubConfigurationDataType pubSubConfiguration = CreateSubscriberConfiguration_MqttJson();
The CreateSubscriberConfiguration methods can be found in [ConsoleReferenceSubscriber/Program.cs](./Program.cs) file.
2. Create an instance of the [UaPubSubApplication Class](../../Docs/PubSub.md#uapubsubapplication-class) using the configuration data from step 1.
// Subscribe to data events
UaPubSubApplication uaPubSubApplication = UaPubSubApplication.Create(pubSubConfiguration);
3. Provide the event handler for the *DataReceived* event. This event will be raised when data sets matching the subscriber configuration arrive over the network. See the DataReceived Event section for more details.
// Create an instance of UaPubSubApplication
uaPubSubApplication.DataReceived += PubSubApplication_DataReceived;
4. Start PubSub application
// Start the publisher
uaPubSubApplication.Start();
After this step the *Subscriber* will listen for *NetworkMessages* as configured.
## Subscriber Configuration
The Subscriber configuration is a subset of the [PubSub Configuration](../../Docs/PubSub.md#pubsub-configuration). A functional *Subscriber* application needs to have a configuration (*PubSubConfgurationDataType* instance) that contains at least one connection (*PubSubConnectionDataType* instance) with at least one reader group configuration (*ReaderGroupDataType* instance). The reader group contains at least one data set reader (*DataSetReaderDataType* instance) that describes a published data set that can be processed and retrieved by the *Subscriber* application.
The diagram shows the subset of classes involved in an *OPC UA Publisher* configuration.
![SubscriberConfigClasses](../../Docs/Images/SubscriberConfigClasses.png)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
@@ -28,16 +28,12 @@
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Runtime.Serialization;
using Opc.Ua;
#if NETFRAMEWORK
namespace Opc.Ua.Gds.Tests
namespace Boiler
{
static class Program
{
// Main Method
static public void Main(String[] args)
{
}
}
}
#endif
@@ -0,0 +1,18 @@
BoilerDrumType,1116,ObjectType
BoilerInputPipeType,1101,ObjectType
BoilerOutputPipeType,1124,ObjectType
Boilers,1240,Object
BoilerStateMachineType,1039,ObjectType
BoilerType,1132,ObjectType
CustomControllerType,513,ObjectType
FlowControllerType,1021,ObjectType
FlowTo,985,ReferenceType
FlowTransmitterType,1032,ObjectType
GenericActuatorType,998,ObjectType
GenericControllerType,210,ObjectType
GenericSensorType,991,ObjectType
HotFlowTo,986,ReferenceType
LevelControllerType,1017,ObjectType
LevelIndicatorType,1025,ObjectType
SignalTo,987,ReferenceType
ValveType,1010,ObjectType
1 BoilerDrumType 1116 ObjectType
2 BoilerInputPipeType 1101 ObjectType
3 BoilerOutputPipeType 1124 ObjectType
4 Boilers 1240 Object
5 BoilerStateMachineType 1039 ObjectType
6 BoilerType 1132 ObjectType
7 CustomControllerType 513 ObjectType
8 FlowControllerType 1021 ObjectType
9 FlowTo 985 ReferenceType
10 FlowTransmitterType 1032 ObjectType
11 GenericActuatorType 998 ObjectType
12 GenericControllerType 210 ObjectType
13 GenericSensorType 991 ObjectType
14 HotFlowTo 986 ReferenceType
15 LevelControllerType 1017 ObjectType
16 LevelIndicatorType 1025 ObjectType
17 SignalTo 987 ReferenceType
18 ValveType 1010 ObjectType
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
<opc:TypeDictionary
xmlns:opc="http://opcfoundation.org/BinarySchema/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ua="http://opcfoundation.org/UA/"
xmlns:tns="http://opcfoundation.org/UA/Boiler/"
DefaultByteOrder="LittleEndian"
TargetNamespace="http://opcfoundation.org/UA/Boiler/"
>
<opc:Import Namespace="http://opcfoundation.org/UA/" Location="Opc.Ua.BinarySchema.bsd"/>
</opc:TypeDictionary>
@@ -0,0 +1,10 @@
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
xmlns:tns="http://opcfoundation.org/UA/Boiler/"
targetNamespace="http://opcfoundation.org/UA/Boiler/"
elementFormDefault="qualified"
>
<xs:import namespace="http://opcfoundation.org/UA/2008/02/Types.xsd" />
</xs:schema>
@@ -0,0 +1,321 @@
GenericControllerType,210,ObjectType
CustomControllerType,513,ObjectType
FlowTo,985,ReferenceType
HotFlowTo,986,ReferenceType
SignalTo,987,ReferenceType
GenericControllerType_Measurement,988,Variable
GenericControllerType_SetPoint,989,Variable
GenericControllerType_ControlOut,990,Variable
GenericSensorType,991,ObjectType
GenericSensorType_Output,992,Variable
GenericSensorType_Output_Definition,993,Variable
GenericSensorType_Output_ValuePrecision,994,Variable
GenericSensorType_Output_EURange,995,Variable
GenericSensorType_Output_InstrumentRange,996,Variable
GenericSensorType_Output_EngineeringUnits,997,Variable
GenericActuatorType,998,ObjectType
GenericActuatorType_Input,999,Variable
GenericActuatorType_Input_Definition,1000,Variable
GenericActuatorType_Input_ValuePrecision,1001,Variable
GenericActuatorType_Input_EURange,1002,Variable
GenericActuatorType_Input_InstrumentRange,1003,Variable
GenericActuatorType_Input_EngineeringUnits,1004,Variable
CustomControllerType_Input1,1005,Variable
CustomControllerType_Input2,1006,Variable
CustomControllerType_Input3,1007,Variable
CustomControllerType_ControlOut,1008,Variable
CustomControllerType_DescriptionX,1009,Variable
ValveType,1010,ObjectType
ValveType_Input,1011,Variable
ValveType_Input_Definition,1012,Variable
ValveType_Input_ValuePrecision,1013,Variable
ValveType_Input_EURange,1014,Variable
ValveType_Input_InstrumentRange,1015,Variable
ValveType_Input_EngineeringUnits,1016,Variable
LevelControllerType,1017,ObjectType
LevelControllerType_Measurement,1018,Variable
LevelControllerType_SetPoint,1019,Variable
LevelControllerType_ControlOut,1020,Variable
FlowControllerType,1021,ObjectType
FlowControllerType_Measurement,1022,Variable
FlowControllerType_SetPoint,1023,Variable
FlowControllerType_ControlOut,1024,Variable
LevelIndicatorType,1025,ObjectType
LevelIndicatorType_Output,1026,Variable
LevelIndicatorType_Output_Definition,1027,Variable
LevelIndicatorType_Output_ValuePrecision,1028,Variable
LevelIndicatorType_Output_EURange,1029,Variable
LevelIndicatorType_Output_InstrumentRange,1030,Variable
LevelIndicatorType_Output_EngineeringUnits,1031,Variable
FlowTransmitterType,1032,ObjectType
FlowTransmitterType_Output,1033,Variable
FlowTransmitterType_Output_Definition,1034,Variable
FlowTransmitterType_Output_ValuePrecision,1035,Variable
FlowTransmitterType_Output_EURange,1036,Variable
FlowTransmitterType_Output_InstrumentRange,1037,Variable
FlowTransmitterType_Output_EngineeringUnits,1038,Variable
BoilerStateMachineType,1039,ObjectType
BoilerStateMachineType_CurrentState,1040,Variable
BoilerStateMachineType_CurrentState_Id,1041,Variable
BoilerStateMachineType_CurrentState_Name,1042,Variable
BoilerStateMachineType_CurrentState_Number,1043,Variable
BoilerStateMachineType_CurrentState_EffectiveDisplayName,1044,Variable
BoilerStateMachineType_LastTransition,1045,Variable
BoilerStateMachineType_LastTransition_Id,1046,Variable
BoilerStateMachineType_LastTransition_Name,1047,Variable
BoilerStateMachineType_LastTransition_Number,1048,Variable
BoilerStateMachineType_LastTransition_TransitionTime,1049,Variable
BoilerStateMachineType_Creatable,1050,Variable
BoilerStateMachineType_Deletable,1051,Variable
BoilerStateMachineType_AutoDelete,1052,Variable
BoilerStateMachineType_RecycleCount,1053,Variable
BoilerStateMachineType_InstanceCount,1054,Variable
BoilerStateMachineType_MaxInstanceCount,1055,Variable
BoilerStateMachineType_MaxRecycleCount,1056,Variable
BoilerStateMachineType_FinalResultData,1068,Object
BoilerStateMachineType_Ready,1069,Object
BoilerStateMachineType_Ready_StateNumber,1070,Variable
BoilerStateMachineType_Running,1071,Object
BoilerStateMachineType_Running_StateNumber,1072,Variable
BoilerStateMachineType_Suspended,1073,Object
BoilerStateMachineType_Suspended_StateNumber,1074,Variable
BoilerStateMachineType_Halted,1075,Object
BoilerStateMachineType_Halted_StateNumber,1076,Variable
BoilerStateMachineType_HaltedToReady,1077,Object
BoilerStateMachineType_HaltedToReady_TransitionNumber,1078,Variable
BoilerStateMachineType_ReadyToRunning,1079,Object
BoilerStateMachineType_ReadyToRunning_TransitionNumber,1080,Variable
BoilerStateMachineType_RunningToHalted,1081,Object
BoilerStateMachineType_RunningToHalted_TransitionNumber,1082,Variable
BoilerStateMachineType_RunningToReady,1083,Object
BoilerStateMachineType_RunningToReady_TransitionNumber,1084,Variable
BoilerStateMachineType_RunningToSuspended,1085,Object
BoilerStateMachineType_RunningToSuspended_TransitionNumber,1086,Variable
BoilerStateMachineType_SuspendedToRunning,1087,Object
BoilerStateMachineType_SuspendedToRunning_TransitionNumber,1088,Variable
BoilerStateMachineType_SuspendedToHalted,1089,Object
BoilerStateMachineType_SuspendedToHalted_TransitionNumber,1090,Variable
BoilerStateMachineType_SuspendedToReady,1091,Object
BoilerStateMachineType_SuspendedToReady_TransitionNumber,1092,Variable
BoilerStateMachineType_ReadyToHalted,1093,Object
BoilerStateMachineType_ReadyToHalted_TransitionNumber,1094,Variable
BoilerStateMachineType_Start,1095,Method
BoilerStateMachineType_Suspend,1096,Method
BoilerStateMachineType_Resume,1097,Method
BoilerStateMachineType_Halt,1098,Method
BoilerStateMachineType_Reset,1099,Method
BoilerStateMachineType_UpdateRate,1100,Variable
BoilerInputPipeType,1101,ObjectType
BoilerInputPipeType_FlowTransmitter1,1102,Object
BoilerInputPipeType_FlowTransmitter1_Output,1103,Variable
BoilerInputPipeType_FlowTransmitter1_Output_Definition,1104,Variable
BoilerInputPipeType_FlowTransmitter1_Output_ValuePrecision,1105,Variable
BoilerInputPipeType_FlowTransmitter1_Output_EURange,1106,Variable
BoilerInputPipeType_FlowTransmitter1_Output_InstrumentRange,1107,Variable
BoilerInputPipeType_FlowTransmitter1_Output_EngineeringUnits,1108,Variable
BoilerInputPipeType_Valve,1109,Object
BoilerInputPipeType_Valve_Input,1110,Variable
BoilerInputPipeType_Valve_Input_Definition,1111,Variable
BoilerInputPipeType_Valve_Input_ValuePrecision,1112,Variable
BoilerInputPipeType_Valve_Input_EURange,1113,Variable
BoilerInputPipeType_Valve_Input_InstrumentRange,1114,Variable
BoilerInputPipeType_Valve_Input_EngineeringUnits,1115,Variable
BoilerDrumType,1116,ObjectType
BoilerDrumType_LevelIndicator,1117,Object
BoilerDrumType_LevelIndicator_Output,1118,Variable
BoilerDrumType_LevelIndicator_Output_Definition,1119,Variable
BoilerDrumType_LevelIndicator_Output_ValuePrecision,1120,Variable
BoilerDrumType_LevelIndicator_Output_EURange,1121,Variable
BoilerDrumType_LevelIndicator_Output_InstrumentRange,1122,Variable
BoilerDrumType_LevelIndicator_Output_EngineeringUnits,1123,Variable
BoilerOutputPipeType,1124,ObjectType
BoilerOutputPipeType_FlowTransmitter2,1125,Object
BoilerOutputPipeType_FlowTransmitter2_Output,1126,Variable
BoilerOutputPipeType_FlowTransmitter2_Output_Definition,1127,Variable
BoilerOutputPipeType_FlowTransmitter2_Output_ValuePrecision,1128,Variable
BoilerOutputPipeType_FlowTransmitter2_Output_EURange,1129,Variable
BoilerOutputPipeType_FlowTransmitter2_Output_InstrumentRange,1130,Variable
BoilerOutputPipeType_FlowTransmitter2_Output_EngineeringUnits,1131,Variable
BoilerType,1132,ObjectType
BoilerType_InputPipe,1133,Object
BoilerType_InputPipe_FlowTransmitter1,1134,Object
BoilerType_InputPipe_FlowTransmitter1_Output,1135,Variable
BoilerType_InputPipe_FlowTransmitter1_Output_Definition,1136,Variable
BoilerType_InputPipe_FlowTransmitter1_Output_ValuePrecision,1137,Variable
BoilerType_InputPipe_FlowTransmitter1_Output_EURange,1138,Variable
BoilerType_InputPipe_FlowTransmitter1_Output_InstrumentRange,1139,Variable
BoilerType_InputPipe_FlowTransmitter1_Output_EngineeringUnits,1140,Variable
BoilerType_InputPipe_Valve,1141,Object
BoilerType_InputPipe_Valve_Input,1142,Variable
BoilerType_InputPipe_Valve_Input_Definition,1143,Variable
BoilerType_InputPipe_Valve_Input_ValuePrecision,1144,Variable
BoilerType_InputPipe_Valve_Input_EURange,1145,Variable
BoilerType_InputPipe_Valve_Input_InstrumentRange,1146,Variable
BoilerType_InputPipe_Valve_Input_EngineeringUnits,1147,Variable
BoilerType_Drum,1148,Object
BoilerType_Drum_LevelIndicator,1149,Object
BoilerType_Drum_LevelIndicator_Output,1150,Variable
BoilerType_Drum_LevelIndicator_Output_Definition,1151,Variable
BoilerType_Drum_LevelIndicator_Output_ValuePrecision,1152,Variable
BoilerType_Drum_LevelIndicator_Output_EURange,1153,Variable
BoilerType_Drum_LevelIndicator_Output_InstrumentRange,1154,Variable
BoilerType_Drum_LevelIndicator_Output_EngineeringUnits,1155,Variable
BoilerType_OutputPipe,1156,Object
BoilerType_OutputPipe_FlowTransmitter2,1157,Object
BoilerType_OutputPipe_FlowTransmitter2_Output,1158,Variable
BoilerType_OutputPipe_FlowTransmitter2_Output_Definition,1159,Variable
BoilerType_OutputPipe_FlowTransmitter2_Output_ValuePrecision,1160,Variable
BoilerType_OutputPipe_FlowTransmitter2_Output_EURange,1161,Variable
BoilerType_OutputPipe_FlowTransmitter2_Output_InstrumentRange,1162,Variable
BoilerType_OutputPipe_FlowTransmitter2_Output_EngineeringUnits,1163,Variable
BoilerType_FlowController,1164,Object
BoilerType_FlowController_Measurement,1165,Variable
BoilerType_FlowController_SetPoint,1166,Variable
BoilerType_FlowController_ControlOut,1167,Variable
BoilerType_LevelController,1168,Object
BoilerType_LevelController_Measurement,1169,Variable
BoilerType_LevelController_SetPoint,1170,Variable
BoilerType_LevelController_ControlOut,1171,Variable
BoilerType_CustomController,1172,Object
BoilerType_CustomController_Input1,1173,Variable
BoilerType_CustomController_Input2,1174,Variable
BoilerType_CustomController_Input3,1175,Variable
BoilerType_CustomController_ControlOut,1176,Variable
BoilerType_CustomController_DescriptionX,1177,Variable
BoilerType_Simulation,1178,Object
BoilerType_Simulation_CurrentState,1179,Variable
BoilerType_Simulation_CurrentState_Id,1180,Variable
BoilerType_Simulation_CurrentState_Name,1181,Variable
BoilerType_Simulation_CurrentState_Number,1182,Variable
BoilerType_Simulation_CurrentState_EffectiveDisplayName,1183,Variable
BoilerType_Simulation_LastTransition,1184,Variable
BoilerType_Simulation_LastTransition_Id,1185,Variable
BoilerType_Simulation_LastTransition_Name,1186,Variable
BoilerType_Simulation_LastTransition_Number,1187,Variable
BoilerType_Simulation_LastTransition_TransitionTime,1188,Variable
BoilerType_Simulation_Deletable,1190,Variable
BoilerType_Simulation_AutoDelete,1191,Variable
BoilerType_Simulation_RecycleCount,1192,Variable
BoilerType_Simulation_FinalResultData,1207,Object
BoilerType_Simulation_UpdateRate,1239,Variable
Boilers,1240,Object
Boilers_Boiler1,1241,Object
Boilers_Boiler1_InputPipe,1242,Object
Boilers_Boiler1_InputPipe_FlowTransmitter1,1243,Object
Boilers_Boiler1_InputPipe_FlowTransmitter1_Output,1244,Variable
Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_Definition,1245,Variable
Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_ValuePrecision,1246,Variable
Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_EURange,1247,Variable
Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_InstrumentRange,1248,Variable
Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_EngineeringUnits,1249,Variable
Boilers_Boiler1_InputPipe_Valve,1250,Object
Boilers_Boiler1_InputPipe_Valve_Input,1251,Variable
Boilers_Boiler1_InputPipe_Valve_Input_Definition,1252,Variable
Boilers_Boiler1_InputPipe_Valve_Input_ValuePrecision,1253,Variable
Boilers_Boiler1_InputPipe_Valve_Input_EURange,1254,Variable
Boilers_Boiler1_InputPipe_Valve_Input_InstrumentRange,1255,Variable
Boilers_Boiler1_InputPipe_Valve_Input_EngineeringUnits,1256,Variable
Boilers_Boiler1_Drum,1257,Object
Boilers_Boiler1_Drum_LevelIndicator,1258,Object
Boilers_Boiler1_Drum_LevelIndicator_Output,1259,Variable
Boilers_Boiler1_Drum_LevelIndicator_Output_Definition,1260,Variable
Boilers_Boiler1_Drum_LevelIndicator_Output_ValuePrecision,1261,Variable
Boilers_Boiler1_Drum_LevelIndicator_Output_EURange,1262,Variable
Boilers_Boiler1_Drum_LevelIndicator_Output_InstrumentRange,1263,Variable
Boilers_Boiler1_Drum_LevelIndicator_Output_EngineeringUnits,1264,Variable
Boilers_Boiler1_OutputPipe,1265,Object
Boilers_Boiler1_OutputPipe_FlowTransmitter2,1266,Object
Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output,1267,Variable
Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_Definition,1268,Variable
Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_ValuePrecision,1269,Variable
Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_EURange,1270,Variable
Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_InstrumentRange,1271,Variable
Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_EngineeringUnits,1272,Variable
Boilers_Boiler1_FlowController,1273,Object
Boilers_Boiler1_FlowController_Measurement,1274,Variable
Boilers_Boiler1_FlowController_SetPoint,1275,Variable
Boilers_Boiler1_FlowController_ControlOut,1276,Variable
Boilers_Boiler1_LevelController,1277,Object
Boilers_Boiler1_LevelController_Measurement,1278,Variable
Boilers_Boiler1_LevelController_SetPoint,1279,Variable
Boilers_Boiler1_LevelController_ControlOut,1280,Variable
Boilers_Boiler1_CustomController,1281,Object
Boilers_Boiler1_CustomController_Input1,1282,Variable
Boilers_Boiler1_CustomController_Input2,1283,Variable
Boilers_Boiler1_CustomController_Input3,1284,Variable
Boilers_Boiler1_CustomController_ControlOut,1285,Variable
Boilers_Boiler1_CustomController_DescriptionX,1286,Variable
Boilers_Boiler1_Simulation,1287,Object
Boilers_Boiler1_Simulation_CurrentState,1288,Variable
Boilers_Boiler1_Simulation_CurrentState_Id,1289,Variable
Boilers_Boiler1_Simulation_CurrentState_Name,1290,Variable
Boilers_Boiler1_Simulation_CurrentState_Number,1291,Variable
Boilers_Boiler1_Simulation_CurrentState_EffectiveDisplayName,1292,Variable
Boilers_Boiler1_Simulation_LastTransition,1293,Variable
Boilers_Boiler1_Simulation_LastTransition_Id,1294,Variable
Boilers_Boiler1_Simulation_LastTransition_Name,1295,Variable
Boilers_Boiler1_Simulation_LastTransition_Number,1296,Variable
Boilers_Boiler1_Simulation_LastTransition_TransitionTime,1297,Variable
Boilers_Boiler1_Simulation_Deletable,1299,Variable
Boilers_Boiler1_Simulation_AutoDelete,1300,Variable
Boilers_Boiler1_Simulation_RecycleCount,1301,Variable
Boilers_Boiler1_Simulation_FinalResultData,1316,Object
Boilers_Boiler1_Simulation_UpdateRate,1348,Variable
BoilerStateMachineType_LastTransition_EffectiveTransitionTime,1349,Variable
BoilerType_Simulation_LastTransition_EffectiveTransitionTime,1350,Variable
Boilers_Boiler1_Simulation_LastTransition_EffectiveTransitionTime,1351,Variable
BoilerStateMachineType_AvailableStates,15001,Variable
BoilerStateMachineType_AvailableTransitions,15002,Variable
BoilerType_Simulation_AvailableStates,15005,Variable
BoilerType_Simulation_AvailableTransitions,15006,Variable
Boilers_Boiler1_Simulation_AvailableStates,15009,Variable
Boilers_Boiler1_Simulation_AvailableTransitions,15010,Variable
BoilerType_Simulation_Start,15013,Method
BoilerType_Simulation_Suspend,15014,Method
BoilerType_Simulation_Resume,15015,Method
BoilerType_Simulation_Halt,15016,Method
BoilerType_Simulation_Reset,15017,Method
Boilers_Boiler1_Simulation_Start,15018,Method
Boilers_Boiler1_Simulation_Suspend,15019,Method
Boilers_Boiler1_Simulation_Resume,15020,Method
Boilers_Boiler1_Simulation_Halt,15021,Method
Boilers_Boiler1_Simulation_Reset,15022,Method
BoilerStateMachineType_ProgramDiagnostic,15023,Variable
BoilerStateMachineType_ProgramDiagnostic_CreateSessionId,15024,Variable
BoilerStateMachineType_ProgramDiagnostic_CreateClientName,15025,Variable
BoilerStateMachineType_ProgramDiagnostic_InvocationCreationTime,15026,Variable
BoilerStateMachineType_ProgramDiagnostic_LastTransitionTime,15027,Variable
BoilerStateMachineType_ProgramDiagnostic_LastMethodCall,15028,Variable
BoilerStateMachineType_ProgramDiagnostic_LastMethodSessionId,15029,Variable
BoilerStateMachineType_ProgramDiagnostic_LastMethodInputArguments,15030,Variable
BoilerStateMachineType_ProgramDiagnostic_LastMethodOutputArguments,15031,Variable
BoilerStateMachineType_ProgramDiagnostic_LastMethodInputValues,15032,Variable
BoilerStateMachineType_ProgramDiagnostic_LastMethodOutputValues,15033,Variable
BoilerStateMachineType_ProgramDiagnostic_LastMethodCallTime,15034,Variable
BoilerStateMachineType_ProgramDiagnostic_LastMethodReturnStatus,15035,Variable
BoilerType_Simulation_ProgramDiagnostic,15036,Variable
BoilerType_Simulation_ProgramDiagnostic_CreateSessionId,15037,Variable
BoilerType_Simulation_ProgramDiagnostic_CreateClientName,15038,Variable
BoilerType_Simulation_ProgramDiagnostic_InvocationCreationTime,15039,Variable
BoilerType_Simulation_ProgramDiagnostic_LastTransitionTime,15040,Variable
BoilerType_Simulation_ProgramDiagnostic_LastMethodCall,15041,Variable
BoilerType_Simulation_ProgramDiagnostic_LastMethodSessionId,15042,Variable
BoilerType_Simulation_ProgramDiagnostic_LastMethodInputArguments,15043,Variable
BoilerType_Simulation_ProgramDiagnostic_LastMethodOutputArguments,15044,Variable
BoilerType_Simulation_ProgramDiagnostic_LastMethodInputValues,15045,Variable
BoilerType_Simulation_ProgramDiagnostic_LastMethodOutputValues,15046,Variable
BoilerType_Simulation_ProgramDiagnostic_LastMethodCallTime,15047,Variable
BoilerType_Simulation_ProgramDiagnostic_LastMethodReturnStatus,15048,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic,15049,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_CreateSessionId,15050,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_CreateClientName,15051,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_InvocationCreationTime,15052,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastTransitionTime,15053,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodCall,15054,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodSessionId,15055,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodInputArguments,15056,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodOutputArguments,15057,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodInputValues,15058,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodOutputValues,15059,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodCallTime,15060,Variable
Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodReturnStatus,15061,Variable
1 GenericControllerType 210 ObjectType
2 CustomControllerType 513 ObjectType
3 FlowTo 985 ReferenceType
4 HotFlowTo 986 ReferenceType
5 SignalTo 987 ReferenceType
6 GenericControllerType_Measurement 988 Variable
7 GenericControllerType_SetPoint 989 Variable
8 GenericControllerType_ControlOut 990 Variable
9 GenericSensorType 991 ObjectType
10 GenericSensorType_Output 992 Variable
11 GenericSensorType_Output_Definition 993 Variable
12 GenericSensorType_Output_ValuePrecision 994 Variable
13 GenericSensorType_Output_EURange 995 Variable
14 GenericSensorType_Output_InstrumentRange 996 Variable
15 GenericSensorType_Output_EngineeringUnits 997 Variable
16 GenericActuatorType 998 ObjectType
17 GenericActuatorType_Input 999 Variable
18 GenericActuatorType_Input_Definition 1000 Variable
19 GenericActuatorType_Input_ValuePrecision 1001 Variable
20 GenericActuatorType_Input_EURange 1002 Variable
21 GenericActuatorType_Input_InstrumentRange 1003 Variable
22 GenericActuatorType_Input_EngineeringUnits 1004 Variable
23 CustomControllerType_Input1 1005 Variable
24 CustomControllerType_Input2 1006 Variable
25 CustomControllerType_Input3 1007 Variable
26 CustomControllerType_ControlOut 1008 Variable
27 CustomControllerType_DescriptionX 1009 Variable
28 ValveType 1010 ObjectType
29 ValveType_Input 1011 Variable
30 ValveType_Input_Definition 1012 Variable
31 ValveType_Input_ValuePrecision 1013 Variable
32 ValveType_Input_EURange 1014 Variable
33 ValveType_Input_InstrumentRange 1015 Variable
34 ValveType_Input_EngineeringUnits 1016 Variable
35 LevelControllerType 1017 ObjectType
36 LevelControllerType_Measurement 1018 Variable
37 LevelControllerType_SetPoint 1019 Variable
38 LevelControllerType_ControlOut 1020 Variable
39 FlowControllerType 1021 ObjectType
40 FlowControllerType_Measurement 1022 Variable
41 FlowControllerType_SetPoint 1023 Variable
42 FlowControllerType_ControlOut 1024 Variable
43 LevelIndicatorType 1025 ObjectType
44 LevelIndicatorType_Output 1026 Variable
45 LevelIndicatorType_Output_Definition 1027 Variable
46 LevelIndicatorType_Output_ValuePrecision 1028 Variable
47 LevelIndicatorType_Output_EURange 1029 Variable
48 LevelIndicatorType_Output_InstrumentRange 1030 Variable
49 LevelIndicatorType_Output_EngineeringUnits 1031 Variable
50 FlowTransmitterType 1032 ObjectType
51 FlowTransmitterType_Output 1033 Variable
52 FlowTransmitterType_Output_Definition 1034 Variable
53 FlowTransmitterType_Output_ValuePrecision 1035 Variable
54 FlowTransmitterType_Output_EURange 1036 Variable
55 FlowTransmitterType_Output_InstrumentRange 1037 Variable
56 FlowTransmitterType_Output_EngineeringUnits 1038 Variable
57 BoilerStateMachineType 1039 ObjectType
58 BoilerStateMachineType_CurrentState 1040 Variable
59 BoilerStateMachineType_CurrentState_Id 1041 Variable
60 BoilerStateMachineType_CurrentState_Name 1042 Variable
61 BoilerStateMachineType_CurrentState_Number 1043 Variable
62 BoilerStateMachineType_CurrentState_EffectiveDisplayName 1044 Variable
63 BoilerStateMachineType_LastTransition 1045 Variable
64 BoilerStateMachineType_LastTransition_Id 1046 Variable
65 BoilerStateMachineType_LastTransition_Name 1047 Variable
66 BoilerStateMachineType_LastTransition_Number 1048 Variable
67 BoilerStateMachineType_LastTransition_TransitionTime 1049 Variable
68 BoilerStateMachineType_Creatable 1050 Variable
69 BoilerStateMachineType_Deletable 1051 Variable
70 BoilerStateMachineType_AutoDelete 1052 Variable
71 BoilerStateMachineType_RecycleCount 1053 Variable
72 BoilerStateMachineType_InstanceCount 1054 Variable
73 BoilerStateMachineType_MaxInstanceCount 1055 Variable
74 BoilerStateMachineType_MaxRecycleCount 1056 Variable
75 BoilerStateMachineType_FinalResultData 1068 Object
76 BoilerStateMachineType_Ready 1069 Object
77 BoilerStateMachineType_Ready_StateNumber 1070 Variable
78 BoilerStateMachineType_Running 1071 Object
79 BoilerStateMachineType_Running_StateNumber 1072 Variable
80 BoilerStateMachineType_Suspended 1073 Object
81 BoilerStateMachineType_Suspended_StateNumber 1074 Variable
82 BoilerStateMachineType_Halted 1075 Object
83 BoilerStateMachineType_Halted_StateNumber 1076 Variable
84 BoilerStateMachineType_HaltedToReady 1077 Object
85 BoilerStateMachineType_HaltedToReady_TransitionNumber 1078 Variable
86 BoilerStateMachineType_ReadyToRunning 1079 Object
87 BoilerStateMachineType_ReadyToRunning_TransitionNumber 1080 Variable
88 BoilerStateMachineType_RunningToHalted 1081 Object
89 BoilerStateMachineType_RunningToHalted_TransitionNumber 1082 Variable
90 BoilerStateMachineType_RunningToReady 1083 Object
91 BoilerStateMachineType_RunningToReady_TransitionNumber 1084 Variable
92 BoilerStateMachineType_RunningToSuspended 1085 Object
93 BoilerStateMachineType_RunningToSuspended_TransitionNumber 1086 Variable
94 BoilerStateMachineType_SuspendedToRunning 1087 Object
95 BoilerStateMachineType_SuspendedToRunning_TransitionNumber 1088 Variable
96 BoilerStateMachineType_SuspendedToHalted 1089 Object
97 BoilerStateMachineType_SuspendedToHalted_TransitionNumber 1090 Variable
98 BoilerStateMachineType_SuspendedToReady 1091 Object
99 BoilerStateMachineType_SuspendedToReady_TransitionNumber 1092 Variable
100 BoilerStateMachineType_ReadyToHalted 1093 Object
101 BoilerStateMachineType_ReadyToHalted_TransitionNumber 1094 Variable
102 BoilerStateMachineType_Start 1095 Method
103 BoilerStateMachineType_Suspend 1096 Method
104 BoilerStateMachineType_Resume 1097 Method
105 BoilerStateMachineType_Halt 1098 Method
106 BoilerStateMachineType_Reset 1099 Method
107 BoilerStateMachineType_UpdateRate 1100 Variable
108 BoilerInputPipeType 1101 ObjectType
109 BoilerInputPipeType_FlowTransmitter1 1102 Object
110 BoilerInputPipeType_FlowTransmitter1_Output 1103 Variable
111 BoilerInputPipeType_FlowTransmitter1_Output_Definition 1104 Variable
112 BoilerInputPipeType_FlowTransmitter1_Output_ValuePrecision 1105 Variable
113 BoilerInputPipeType_FlowTransmitter1_Output_EURange 1106 Variable
114 BoilerInputPipeType_FlowTransmitter1_Output_InstrumentRange 1107 Variable
115 BoilerInputPipeType_FlowTransmitter1_Output_EngineeringUnits 1108 Variable
116 BoilerInputPipeType_Valve 1109 Object
117 BoilerInputPipeType_Valve_Input 1110 Variable
118 BoilerInputPipeType_Valve_Input_Definition 1111 Variable
119 BoilerInputPipeType_Valve_Input_ValuePrecision 1112 Variable
120 BoilerInputPipeType_Valve_Input_EURange 1113 Variable
121 BoilerInputPipeType_Valve_Input_InstrumentRange 1114 Variable
122 BoilerInputPipeType_Valve_Input_EngineeringUnits 1115 Variable
123 BoilerDrumType 1116 ObjectType
124 BoilerDrumType_LevelIndicator 1117 Object
125 BoilerDrumType_LevelIndicator_Output 1118 Variable
126 BoilerDrumType_LevelIndicator_Output_Definition 1119 Variable
127 BoilerDrumType_LevelIndicator_Output_ValuePrecision 1120 Variable
128 BoilerDrumType_LevelIndicator_Output_EURange 1121 Variable
129 BoilerDrumType_LevelIndicator_Output_InstrumentRange 1122 Variable
130 BoilerDrumType_LevelIndicator_Output_EngineeringUnits 1123 Variable
131 BoilerOutputPipeType 1124 ObjectType
132 BoilerOutputPipeType_FlowTransmitter2 1125 Object
133 BoilerOutputPipeType_FlowTransmitter2_Output 1126 Variable
134 BoilerOutputPipeType_FlowTransmitter2_Output_Definition 1127 Variable
135 BoilerOutputPipeType_FlowTransmitter2_Output_ValuePrecision 1128 Variable
136 BoilerOutputPipeType_FlowTransmitter2_Output_EURange 1129 Variable
137 BoilerOutputPipeType_FlowTransmitter2_Output_InstrumentRange 1130 Variable
138 BoilerOutputPipeType_FlowTransmitter2_Output_EngineeringUnits 1131 Variable
139 BoilerType 1132 ObjectType
140 BoilerType_InputPipe 1133 Object
141 BoilerType_InputPipe_FlowTransmitter1 1134 Object
142 BoilerType_InputPipe_FlowTransmitter1_Output 1135 Variable
143 BoilerType_InputPipe_FlowTransmitter1_Output_Definition 1136 Variable
144 BoilerType_InputPipe_FlowTransmitter1_Output_ValuePrecision 1137 Variable
145 BoilerType_InputPipe_FlowTransmitter1_Output_EURange 1138 Variable
146 BoilerType_InputPipe_FlowTransmitter1_Output_InstrumentRange 1139 Variable
147 BoilerType_InputPipe_FlowTransmitter1_Output_EngineeringUnits 1140 Variable
148 BoilerType_InputPipe_Valve 1141 Object
149 BoilerType_InputPipe_Valve_Input 1142 Variable
150 BoilerType_InputPipe_Valve_Input_Definition 1143 Variable
151 BoilerType_InputPipe_Valve_Input_ValuePrecision 1144 Variable
152 BoilerType_InputPipe_Valve_Input_EURange 1145 Variable
153 BoilerType_InputPipe_Valve_Input_InstrumentRange 1146 Variable
154 BoilerType_InputPipe_Valve_Input_EngineeringUnits 1147 Variable
155 BoilerType_Drum 1148 Object
156 BoilerType_Drum_LevelIndicator 1149 Object
157 BoilerType_Drum_LevelIndicator_Output 1150 Variable
158 BoilerType_Drum_LevelIndicator_Output_Definition 1151 Variable
159 BoilerType_Drum_LevelIndicator_Output_ValuePrecision 1152 Variable
160 BoilerType_Drum_LevelIndicator_Output_EURange 1153 Variable
161 BoilerType_Drum_LevelIndicator_Output_InstrumentRange 1154 Variable
162 BoilerType_Drum_LevelIndicator_Output_EngineeringUnits 1155 Variable
163 BoilerType_OutputPipe 1156 Object
164 BoilerType_OutputPipe_FlowTransmitter2 1157 Object
165 BoilerType_OutputPipe_FlowTransmitter2_Output 1158 Variable
166 BoilerType_OutputPipe_FlowTransmitter2_Output_Definition 1159 Variable
167 BoilerType_OutputPipe_FlowTransmitter2_Output_ValuePrecision 1160 Variable
168 BoilerType_OutputPipe_FlowTransmitter2_Output_EURange 1161 Variable
169 BoilerType_OutputPipe_FlowTransmitter2_Output_InstrumentRange 1162 Variable
170 BoilerType_OutputPipe_FlowTransmitter2_Output_EngineeringUnits 1163 Variable
171 BoilerType_FlowController 1164 Object
172 BoilerType_FlowController_Measurement 1165 Variable
173 BoilerType_FlowController_SetPoint 1166 Variable
174 BoilerType_FlowController_ControlOut 1167 Variable
175 BoilerType_LevelController 1168 Object
176 BoilerType_LevelController_Measurement 1169 Variable
177 BoilerType_LevelController_SetPoint 1170 Variable
178 BoilerType_LevelController_ControlOut 1171 Variable
179 BoilerType_CustomController 1172 Object
180 BoilerType_CustomController_Input1 1173 Variable
181 BoilerType_CustomController_Input2 1174 Variable
182 BoilerType_CustomController_Input3 1175 Variable
183 BoilerType_CustomController_ControlOut 1176 Variable
184 BoilerType_CustomController_DescriptionX 1177 Variable
185 BoilerType_Simulation 1178 Object
186 BoilerType_Simulation_CurrentState 1179 Variable
187 BoilerType_Simulation_CurrentState_Id 1180 Variable
188 BoilerType_Simulation_CurrentState_Name 1181 Variable
189 BoilerType_Simulation_CurrentState_Number 1182 Variable
190 BoilerType_Simulation_CurrentState_EffectiveDisplayName 1183 Variable
191 BoilerType_Simulation_LastTransition 1184 Variable
192 BoilerType_Simulation_LastTransition_Id 1185 Variable
193 BoilerType_Simulation_LastTransition_Name 1186 Variable
194 BoilerType_Simulation_LastTransition_Number 1187 Variable
195 BoilerType_Simulation_LastTransition_TransitionTime 1188 Variable
196 BoilerType_Simulation_Deletable 1190 Variable
197 BoilerType_Simulation_AutoDelete 1191 Variable
198 BoilerType_Simulation_RecycleCount 1192 Variable
199 BoilerType_Simulation_FinalResultData 1207 Object
200 BoilerType_Simulation_UpdateRate 1239 Variable
201 Boilers 1240 Object
202 Boilers_Boiler1 1241 Object
203 Boilers_Boiler1_InputPipe 1242 Object
204 Boilers_Boiler1_InputPipe_FlowTransmitter1 1243 Object
205 Boilers_Boiler1_InputPipe_FlowTransmitter1_Output 1244 Variable
206 Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_Definition 1245 Variable
207 Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_ValuePrecision 1246 Variable
208 Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_EURange 1247 Variable
209 Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_InstrumentRange 1248 Variable
210 Boilers_Boiler1_InputPipe_FlowTransmitter1_Output_EngineeringUnits 1249 Variable
211 Boilers_Boiler1_InputPipe_Valve 1250 Object
212 Boilers_Boiler1_InputPipe_Valve_Input 1251 Variable
213 Boilers_Boiler1_InputPipe_Valve_Input_Definition 1252 Variable
214 Boilers_Boiler1_InputPipe_Valve_Input_ValuePrecision 1253 Variable
215 Boilers_Boiler1_InputPipe_Valve_Input_EURange 1254 Variable
216 Boilers_Boiler1_InputPipe_Valve_Input_InstrumentRange 1255 Variable
217 Boilers_Boiler1_InputPipe_Valve_Input_EngineeringUnits 1256 Variable
218 Boilers_Boiler1_Drum 1257 Object
219 Boilers_Boiler1_Drum_LevelIndicator 1258 Object
220 Boilers_Boiler1_Drum_LevelIndicator_Output 1259 Variable
221 Boilers_Boiler1_Drum_LevelIndicator_Output_Definition 1260 Variable
222 Boilers_Boiler1_Drum_LevelIndicator_Output_ValuePrecision 1261 Variable
223 Boilers_Boiler1_Drum_LevelIndicator_Output_EURange 1262 Variable
224 Boilers_Boiler1_Drum_LevelIndicator_Output_InstrumentRange 1263 Variable
225 Boilers_Boiler1_Drum_LevelIndicator_Output_EngineeringUnits 1264 Variable
226 Boilers_Boiler1_OutputPipe 1265 Object
227 Boilers_Boiler1_OutputPipe_FlowTransmitter2 1266 Object
228 Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output 1267 Variable
229 Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_Definition 1268 Variable
230 Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_ValuePrecision 1269 Variable
231 Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_EURange 1270 Variable
232 Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_InstrumentRange 1271 Variable
233 Boilers_Boiler1_OutputPipe_FlowTransmitter2_Output_EngineeringUnits 1272 Variable
234 Boilers_Boiler1_FlowController 1273 Object
235 Boilers_Boiler1_FlowController_Measurement 1274 Variable
236 Boilers_Boiler1_FlowController_SetPoint 1275 Variable
237 Boilers_Boiler1_FlowController_ControlOut 1276 Variable
238 Boilers_Boiler1_LevelController 1277 Object
239 Boilers_Boiler1_LevelController_Measurement 1278 Variable
240 Boilers_Boiler1_LevelController_SetPoint 1279 Variable
241 Boilers_Boiler1_LevelController_ControlOut 1280 Variable
242 Boilers_Boiler1_CustomController 1281 Object
243 Boilers_Boiler1_CustomController_Input1 1282 Variable
244 Boilers_Boiler1_CustomController_Input2 1283 Variable
245 Boilers_Boiler1_CustomController_Input3 1284 Variable
246 Boilers_Boiler1_CustomController_ControlOut 1285 Variable
247 Boilers_Boiler1_CustomController_DescriptionX 1286 Variable
248 Boilers_Boiler1_Simulation 1287 Object
249 Boilers_Boiler1_Simulation_CurrentState 1288 Variable
250 Boilers_Boiler1_Simulation_CurrentState_Id 1289 Variable
251 Boilers_Boiler1_Simulation_CurrentState_Name 1290 Variable
252 Boilers_Boiler1_Simulation_CurrentState_Number 1291 Variable
253 Boilers_Boiler1_Simulation_CurrentState_EffectiveDisplayName 1292 Variable
254 Boilers_Boiler1_Simulation_LastTransition 1293 Variable
255 Boilers_Boiler1_Simulation_LastTransition_Id 1294 Variable
256 Boilers_Boiler1_Simulation_LastTransition_Name 1295 Variable
257 Boilers_Boiler1_Simulation_LastTransition_Number 1296 Variable
258 Boilers_Boiler1_Simulation_LastTransition_TransitionTime 1297 Variable
259 Boilers_Boiler1_Simulation_Deletable 1299 Variable
260 Boilers_Boiler1_Simulation_AutoDelete 1300 Variable
261 Boilers_Boiler1_Simulation_RecycleCount 1301 Variable
262 Boilers_Boiler1_Simulation_FinalResultData 1316 Object
263 Boilers_Boiler1_Simulation_UpdateRate 1348 Variable
264 BoilerStateMachineType_LastTransition_EffectiveTransitionTime 1349 Variable
265 BoilerType_Simulation_LastTransition_EffectiveTransitionTime 1350 Variable
266 Boilers_Boiler1_Simulation_LastTransition_EffectiveTransitionTime 1351 Variable
267 BoilerStateMachineType_AvailableStates 15001 Variable
268 BoilerStateMachineType_AvailableTransitions 15002 Variable
269 BoilerType_Simulation_AvailableStates 15005 Variable
270 BoilerType_Simulation_AvailableTransitions 15006 Variable
271 Boilers_Boiler1_Simulation_AvailableStates 15009 Variable
272 Boilers_Boiler1_Simulation_AvailableTransitions 15010 Variable
273 BoilerType_Simulation_Start 15013 Method
274 BoilerType_Simulation_Suspend 15014 Method
275 BoilerType_Simulation_Resume 15015 Method
276 BoilerType_Simulation_Halt 15016 Method
277 BoilerType_Simulation_Reset 15017 Method
278 Boilers_Boiler1_Simulation_Start 15018 Method
279 Boilers_Boiler1_Simulation_Suspend 15019 Method
280 Boilers_Boiler1_Simulation_Resume 15020 Method
281 Boilers_Boiler1_Simulation_Halt 15021 Method
282 Boilers_Boiler1_Simulation_Reset 15022 Method
283 BoilerStateMachineType_ProgramDiagnostic 15023 Variable
284 BoilerStateMachineType_ProgramDiagnostic_CreateSessionId 15024 Variable
285 BoilerStateMachineType_ProgramDiagnostic_CreateClientName 15025 Variable
286 BoilerStateMachineType_ProgramDiagnostic_InvocationCreationTime 15026 Variable
287 BoilerStateMachineType_ProgramDiagnostic_LastTransitionTime 15027 Variable
288 BoilerStateMachineType_ProgramDiagnostic_LastMethodCall 15028 Variable
289 BoilerStateMachineType_ProgramDiagnostic_LastMethodSessionId 15029 Variable
290 BoilerStateMachineType_ProgramDiagnostic_LastMethodInputArguments 15030 Variable
291 BoilerStateMachineType_ProgramDiagnostic_LastMethodOutputArguments 15031 Variable
292 BoilerStateMachineType_ProgramDiagnostic_LastMethodInputValues 15032 Variable
293 BoilerStateMachineType_ProgramDiagnostic_LastMethodOutputValues 15033 Variable
294 BoilerStateMachineType_ProgramDiagnostic_LastMethodCallTime 15034 Variable
295 BoilerStateMachineType_ProgramDiagnostic_LastMethodReturnStatus 15035 Variable
296 BoilerType_Simulation_ProgramDiagnostic 15036 Variable
297 BoilerType_Simulation_ProgramDiagnostic_CreateSessionId 15037 Variable
298 BoilerType_Simulation_ProgramDiagnostic_CreateClientName 15038 Variable
299 BoilerType_Simulation_ProgramDiagnostic_InvocationCreationTime 15039 Variable
300 BoilerType_Simulation_ProgramDiagnostic_LastTransitionTime 15040 Variable
301 BoilerType_Simulation_ProgramDiagnostic_LastMethodCall 15041 Variable
302 BoilerType_Simulation_ProgramDiagnostic_LastMethodSessionId 15042 Variable
303 BoilerType_Simulation_ProgramDiagnostic_LastMethodInputArguments 15043 Variable
304 BoilerType_Simulation_ProgramDiagnostic_LastMethodOutputArguments 15044 Variable
305 BoilerType_Simulation_ProgramDiagnostic_LastMethodInputValues 15045 Variable
306 BoilerType_Simulation_ProgramDiagnostic_LastMethodOutputValues 15046 Variable
307 BoilerType_Simulation_ProgramDiagnostic_LastMethodCallTime 15047 Variable
308 BoilerType_Simulation_ProgramDiagnostic_LastMethodReturnStatus 15048 Variable
309 Boilers_Boiler1_Simulation_ProgramDiagnostic 15049 Variable
310 Boilers_Boiler1_Simulation_ProgramDiagnostic_CreateSessionId 15050 Variable
311 Boilers_Boiler1_Simulation_ProgramDiagnostic_CreateClientName 15051 Variable
312 Boilers_Boiler1_Simulation_ProgramDiagnostic_InvocationCreationTime 15052 Variable
313 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastTransitionTime 15053 Variable
314 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodCall 15054 Variable
315 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodSessionId 15055 Variable
316 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodInputArguments 15056 Variable
317 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodOutputArguments 15057 Variable
318 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodInputValues 15058 Variable
319 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodOutputValues 15059 Variable
320 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodCallTime 15060 Variable
321 Boilers_Boiler1_Simulation_ProgramDiagnostic_LastMethodReturnStatus 15061 Variable
@@ -0,0 +1,348 @@
<?xml version="1.0" encoding="utf-8" ?>
<opc:ModelDesign
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:opc="http://opcfoundation.org/UA/ModelDesign.xsd"
xmlns:ua="http://opcfoundation.org/UA/"
xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd"
xmlns="http://opcfoundation.org/UA/Boiler/"
TargetNamespace="http://opcfoundation.org/UA/Boiler/"
>
<opc:Namespaces>
<opc:Namespace Name="OpcUa" Prefix="Opc.Ua" InternalPrefix="Opc.Ua.Server" XmlNamespace="http://opcfoundation.org/UA/2008/02/Types.xsd">http://opcfoundation.org/UA/</opc:Namespace>
<opc:Namespace Name="Boiler" Prefix="Boiler">http://opcfoundation.org/UA/Boiler/</opc:Namespace>
</opc:Namespaces>
<opc:ReferenceType SymbolicName="FlowTo" BaseType="ua:NonHierarchicalReferences">
<opc:Description>A reference that indicates a flow between two objects.</opc:Description>
<opc:InverseName>FlowFrom</opc:InverseName>
</opc:ReferenceType>
<opc:ReferenceType SymbolicName="HotFlowTo" BaseType="FlowTo">
<opc:Description>A reference that indicates a high temperature flow between two objects.</opc:Description>
<opc:InverseName>HotFlowFrom</opc:InverseName>
</opc:ReferenceType>
<opc:ReferenceType SymbolicName="SignalTo" BaseType="ua:NonHierarchicalReferences">
<opc:Description>A reference that indicates an electrical signal between two variables.</opc:Description>
<opc:InverseName>SignalFrom</opc:InverseName>
</opc:ReferenceType>
<opc:ObjectType SymbolicName="GenericControllerType" BaseType="ua:BaseObjectType">
<opc:Description>A generic PID controller</opc:Description>
<opc:Children>
<opc:Property SymbolicName="Measurement" DataType="ua:Double" ValueRank="Scalar" />
<opc:Property SymbolicName="SetPoint" DataType="ua:Double" ValueRank="Scalar" AccessLevel="ReadWrite" />
<opc:Property SymbolicName="ControlOut" DataType="ua:Double" ValueRank="Scalar" />
</opc:Children>
</opc:ObjectType>
<opc:ObjectType SymbolicName="GenericSensorType" BaseType="ua:BaseObjectType">
<opc:Description>A generic sensor that read a process value.</opc:Description>
<opc:Children>
<opc:Variable SymbolicName="Output" DataType="ua:Double" ValueRank="Scalar" TypeDefinition="ua:AnalogItemType" />
</opc:Children>
</opc:ObjectType>
<opc:ObjectType SymbolicName="GenericActuatorType" BaseType="ua:BaseObjectType">
<opc:Description>Represents a piece of equipment that causes some action to occur.</opc:Description>
<opc:Children>
<opc:Variable SymbolicName="Input" DataType="ua:Double" ValueRank="Scalar" TypeDefinition="ua:AnalogItemType" AccessLevel="Write" />
</opc:Children>
</opc:ObjectType>
<opc:ObjectType SymbolicName="CustomControllerType" BaseType="ua:BaseObjectType">
<opc:Description>A custom PID controller with 3 inputs</opc:Description>
<opc:Children>
<opc:Property SymbolicName="Input1" DataType="ua:Double" ValueRank="Scalar" AccessLevel="Write" />
<opc:Property SymbolicName="Input2" DataType="ua:Double" ValueRank="Scalar" AccessLevel="Write" />
<opc:Property SymbolicName="Input3" DataType="ua:Double" ValueRank="Scalar" AccessLevel="Write" />
<opc:Property SymbolicName="ControlOut" DataType="ua:Double" />
<opc:Property SymbolicName="DescriptionX" DataType="ua:LocalizedText">
<opc:BrowseName>Description</opc:BrowseName>
</opc:Property>
</opc:Children>
</opc:ObjectType>
<opc:ObjectType SymbolicName="ValveType" BaseType="GenericActuatorType">
<opc:Description>An actuator that controls the flow through a pipe.</opc:Description>
</opc:ObjectType>
<opc:ObjectType SymbolicName="LevelControllerType" BaseType="GenericControllerType">
<opc:Description>A controller for the level of a fluid in a drum.</opc:Description>
</opc:ObjectType>
<opc:ObjectType SymbolicName="FlowControllerType" BaseType="GenericControllerType">
<opc:Description>A controller for the flow of a fluid through a pipe.</opc:Description>
</opc:ObjectType>
<opc:ObjectType SymbolicName="LevelIndicatorType" BaseType="GenericSensorType">
<opc:Description>A sensor that reports the level of a liquid in a tank.</opc:Description>
</opc:ObjectType>
<opc:ObjectType SymbolicName="FlowTransmitterType" BaseType="GenericSensorType">
<opc:Description>A sensor that reports the flow of a liquid through a pipe.</opc:Description>
</opc:ObjectType>
<opc:ObjectType SymbolicName="BoilerStateMachineType" BaseType="ua:ProgramStateMachineType">
<opc:Description>A program that produces simulated values for a running boiler.</opc:Description>
<opc:Children>
<opc:Property SymbolicName="UpdateRate" DataType="ua:UInt32" ValueRank="Scalar" AccessLevel="ReadWrite">
<opc:Description>The rate at which the simulation runs.</opc:Description>
</opc:Property>
<opc:Method SymbolicName="Start" ModellingRule="Mandatory">
<opc:Description>Causes the Program to transition from the Ready state to the Running state.</opc:Description>
</opc:Method>
<opc:Method SymbolicName="Suspend" ModellingRule="Mandatory">
<opc:Description>Causes the Program to transition from the Running state to the Suspended state.</opc:Description>
</opc:Method>
<opc:Method SymbolicName="Resume" ModellingRule="Mandatory">
<opc:Description>Causes the Program to transition from the Suspended state to the Running state.</opc:Description>
</opc:Method>
<opc:Method SymbolicName="Halt" ModellingRule="Mandatory">
<opc:Description>Causes the Program to transition from the Ready, Running or Suspended state to the Halted state.</opc:Description>
</opc:Method>
<opc:Method SymbolicName="Reset" ModellingRule="Mandatory">
<opc:Description>Causes the Program to transition from the Halted state to the Ready state.</opc:Description>
</opc:Method>
</opc:Children>
</opc:ObjectType>
<opc:ObjectType SymbolicName="BoilerInputPipeType" BaseType="ua:FolderType">
<opc:Children>
<opc:Object SymbolicName="FlowTransmitter1" TypeDefinition="FlowTransmitterType" SupportsEvents="true">
<opc:BrowseName>FTX001</opc:BrowseName>
</opc:Object>
<opc:Object SymbolicName="Valve" TypeDefinition="ValveType" SupportsEvents="true">
<opc:BrowseName>ValveX001</opc:BrowseName>
</opc:Object>
</opc:Children>
<opc:References>
<opc:Reference>
<opc:ReferenceType>ua:HasNotifier</opc:ReferenceType>
<opc:TargetId>BoilerInputPipeType_FlowTransmitter1</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:ObjectType>
<opc:ObjectType SymbolicName="BoilerDrumType" BaseType="ua:FolderType">
<opc:Children>
<opc:Object SymbolicName="LevelIndicator" TypeDefinition="LevelIndicatorType" SupportsEvents="true">
<opc:BrowseName>LIX001</opc:BrowseName>
</opc:Object>
</opc:Children>
<opc:References>
<opc:Reference>
<opc:ReferenceType>ua:HasNotifier</opc:ReferenceType>
<opc:TargetId>BoilerDrumType_LevelIndicator</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:ObjectType>
<opc:ObjectType SymbolicName="BoilerOutputPipeType" BaseType="ua:FolderType">
<opc:Children>
<opc:Object SymbolicName="FlowTransmitter2" TypeDefinition="FlowTransmitterType" SupportsEvents="true">
<opc:BrowseName>FTX002</opc:BrowseName>
</opc:Object>
</opc:Children>
<opc:References>
<opc:Reference>
<opc:ReferenceType>ua:HasNotifier</opc:ReferenceType>
<opc:TargetId>BoilerOutputPipeType_FlowTransmitter2</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:ObjectType>
<opc:ObjectType SymbolicName="BoilerType" BaseType="ua:BaseObjectType" SupportsEvents="true">
<opc:Description>A boiler used to produce steam for a turbine.</opc:Description>
<opc:Children>
<opc:Object SymbolicName="InputPipe" TypeDefinition="BoilerInputPipeType" SupportsEvents="true">
<opc:BrowseName>PipeX001</opc:BrowseName>
<opc:Children>
<opc:Object SymbolicName="FlowTransmitter1">
<opc:BrowseName>FTX001</opc:BrowseName>
<opc:Children>
<opc:Variable SymbolicName="Output" />
</opc:Children>
</opc:Object>
<opc:Object SymbolicName="Valve">
<opc:BrowseName>ValveX001</opc:BrowseName>
<opc:Children>
<opc:Variable SymbolicName="Input" />
</opc:Children>
</opc:Object>
</opc:Children>
<opc:References>
<opc:Reference>
<opc:ReferenceType>FlowTo</opc:ReferenceType>
<opc:TargetId>BoilerType_Drum</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Object>
<opc:Object SymbolicName="Drum" TypeDefinition="BoilerDrumType" SupportsEvents="true">
<opc:BrowseName>DrumX001</opc:BrowseName>
<opc:Children>
<opc:Object SymbolicName="LevelIndicator">
<opc:BrowseName>LIX001</opc:BrowseName>
<opc:Children>
<opc:Variable Declaration="GenericSensorType_Output" />
</opc:Children>
</opc:Object>
</opc:Children>
<opc:References>
<opc:Reference>
<opc:ReferenceType>HotFlowTo</opc:ReferenceType>
<opc:TargetId>BoilerType_OutputPipe</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Object>
<opc:Object SymbolicName="OutputPipe" TypeDefinition="BoilerOutputPipeType" SupportsEvents="true">
<opc:BrowseName>PipeX002</opc:BrowseName>
<opc:Children>
<opc:Object SymbolicName="FlowTransmitter2">
<opc:BrowseName>FTX002</opc:BrowseName>
<opc:Children>
<opc:Variable SymbolicName="Output" />
</opc:Children>
</opc:Object>
</opc:Children>
</opc:Object>
<opc:Object SymbolicName="FlowController" TypeDefinition="FlowControllerType">
<opc:BrowseName>FCX001</opc:BrowseName>
<opc:Children>
<opc:Property SymbolicName="Measurement">
<opc:References>
<opc:Reference IsInverse="true">
<opc:ReferenceType>SignalTo</opc:ReferenceType>
<opc:TargetId>BoilerType_InputPipe_FlowTransmitter1_Output</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Property>
<opc:Property SymbolicName="SetPoint" />
<opc:Property SymbolicName="ControlOut">
<opc:References>
<opc:Reference>
<opc:ReferenceType>SignalTo</opc:ReferenceType>
<opc:TargetId>BoilerType_InputPipe_Valve_Input</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Property>
</opc:Children>
</opc:Object>
<opc:Object SymbolicName="LevelController" TypeDefinition="LevelControllerType">
<opc:BrowseName>LCX001</opc:BrowseName>
<opc:Children>
<opc:Property SymbolicName="Measurement">
<opc:References>
<opc:Reference IsInverse="true">
<opc:ReferenceType>SignalTo</opc:ReferenceType>
<opc:TargetId>BoilerType_Drum_LevelIndicator_Output</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Property>
<opc:Property SymbolicName="SetPoint" />
<opc:Property SymbolicName="ControlOut">
<opc:References>
<opc:Reference>
<opc:ReferenceType>SignalTo</opc:ReferenceType>
<opc:TargetId>BoilerType_CustomController_Input1</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Property>
</opc:Children>
</opc:Object>
<opc:Object SymbolicName="CustomController" TypeDefinition="CustomControllerType">
<opc:BrowseName>CCX001</opc:BrowseName>
<opc:Children>
<opc:Property SymbolicName="Input1" />
<opc:Property SymbolicName="Input2">
<opc:References>
<opc:Reference IsInverse="true">
<opc:ReferenceType>SignalTo</opc:ReferenceType>
<opc:TargetId>BoilerType_InputPipe_FlowTransmitter1_Output</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Property>
<opc:Property SymbolicName="Input3">
<opc:References>
<opc:Reference IsInverse="true">
<opc:ReferenceType>SignalTo</opc:ReferenceType>
<opc:TargetId>BoilerType_OutputPipe_FlowTransmitter2_Output</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Property>
<opc:Property SymbolicName="ControlOut">
<opc:References>
<opc:Reference>
<opc:ReferenceType>SignalTo</opc:ReferenceType>
<opc:TargetId>BoilerType_FlowController_SetPoint</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Property>
</opc:Children>
</opc:Object>
<opc:Object SymbolicName="Simulation" TypeDefinition="BoilerStateMachineType" SupportsEvents="true" />
</opc:Children>
<opc:References>
<opc:Reference>
<opc:ReferenceType>ua:HasNotifier</opc:ReferenceType>
<opc:TargetId>BoilerType_InputPipe</opc:TargetId>
</opc:Reference>
<opc:Reference>
<opc:ReferenceType>ua:HasNotifier</opc:ReferenceType>
<opc:TargetId>BoilerType_Drum</opc:TargetId>
</opc:Reference>
<opc:Reference>
<opc:ReferenceType>ua:HasNotifier</opc:ReferenceType>
<opc:TargetId>BoilerType_OutputPipe</opc:TargetId>
</opc:Reference>
<opc:Reference>
<opc:ReferenceType>ua:HasEventSource</opc:ReferenceType>
<opc:TargetId>BoilerType_Simulation</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:ObjectType>
<opc:Object SymbolicName="Boilers" TypeDefinition="ua:FolderType" SupportsEvents="true">
<opc:Children>
<opc:Object SymbolicName="Boiler1" TypeDefinition="BoilerType">
<opc:BrowseName>Boiler #1</opc:BrowseName>
<opc:Children>
<opc:Object SymbolicName="InputPipe">
<opc:DisplayName>Pipe1001</opc:DisplayName>
</opc:Object>
<opc:Object SymbolicName="Drum">
<opc:DisplayName>Drum1001</opc:DisplayName>
</opc:Object>
<opc:Object SymbolicName="OutputPipe">
<opc:DisplayName>Pipe1002</opc:DisplayName>
</opc:Object>
<opc:Object SymbolicName="FlowController">
<opc:DisplayName>FC1001</opc:DisplayName>
</opc:Object>
<opc:Object SymbolicName="LevelController">
<opc:DisplayName>LC1001</opc:DisplayName>
</opc:Object>
<opc:Object SymbolicName="CustomController">
<opc:DisplayName>CC1001</opc:DisplayName>
</opc:Object>
</opc:Children>
</opc:Object>
</opc:Children>
<opc:References>
<opc:Reference>
<opc:ReferenceType>ua:HasNotifier</opc:ReferenceType>
<opc:TargetId>Boilers_Boiler1</opc:TargetId>
</opc:Reference>
<opc:Reference IsInverse="true">
<opc:ReferenceType>ua:Organizes</opc:ReferenceType>
<opc:TargetId>ua:ObjectsFolder</opc:TargetId>
</opc:Reference>
<opc:Reference IsInverse="true">
<opc:ReferenceType>ua:HasNotifier</opc:ReferenceType>
<opc:TargetId>ua:Server</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Object>
</opc:ModelDesign>
@@ -0,0 +1,314 @@
/* ========================================================================
* Copyright (c) 2005-2021 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.Collections.Generic;
using System.Reflection;
using Opc.Ua;
using Opc.Ua.Sample;
using Opc.Ua.Server;
namespace Boiler
{
/// <summary>
/// The factory class to create the boiler node manager.
/// </summary>
public class BoilerNodeManagerFactory : INodeManagerFactory
{
/// <inheritdoc/>
public INodeManager Create(IServerInternal server, ApplicationConfiguration configuration)
{
return new BoilerNodeManager(server, configuration);
}
/// <inheritdoc/>
public StringCollection NamespacesUris
{
get
{
var nameSpaces = new StringCollection {
Namespaces.Boiler,
Namespaces.Boiler + "Instance"
};
return nameSpaces;
}
}
}
/// <summary>
/// A node manager for the boiler exposed by the server.
/// </summary>
public class BoilerNodeManager : SampleNodeManager
{
#region Constructors
/// <summary>
/// Initializes the node manager.
/// </summary>
public BoilerNodeManager(
Opc.Ua.Server.IServerInternal server,
ApplicationConfiguration configuration)
:
base(server)
{
List<string> namespaceUris = new List<string>();
namespaceUris.Add(Namespaces.Boiler);
namespaceUris.Add(Namespaces.Boiler + "Instance");
NamespaceUris = namespaceUris;
m_typeNamespaceIndex = Server.NamespaceUris.GetIndexOrAppend(namespaceUris[0]);
m_namespaceIndex = Server.NamespaceUris.GetIndexOrAppend(namespaceUris[1]);
AddEncodeableNodeManagerTypes(typeof(BoilerNodeManager).Assembly, typeof(BoilerNodeManager).Namespace);
m_lastUsedId = 0;
m_boilers = new List<BoilerState>();
}
#endregion
#region INodeIdFactory Members
/// <summary>
/// Creates the NodeId for the specified node.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="node">The node.</param>
/// <returns>The new NodeId.</returns>
public override NodeId New(ISystemContext context, NodeState node)
{
uint id = Utils.IncrementIdentifier(ref m_lastUsedId);
return new NodeId(id, m_namespaceIndex);
}
#endregion
#region INodeManager Members
/// <summary>
/// Does any initialization required before the address space can be used.
/// </summary>
/// <remarks>
/// The externalReferences is an out parameter that allows the node manager to link to nodes
/// in other node managers. For example, the 'Objects' node is managed by the CoreNodeManager and
/// should have a reference to the root folder node(s) exposed by this node manager.
/// </remarks>
public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
{
lock (Lock)
{
base.CreateAddressSpace(externalReferences);
CreateBoiler(SystemContext, 2);
}
}
/// <summary>
/// Creates a boiler and adds it to the address space.
/// </summary>
/// <param name="context">The context to use.</param>
/// <param name="unitNumber">The unit number for the boiler.</param>
private void CreateBoiler(SystemContext context, int unitNumber)
{
BoilerState boiler = new BoilerState(null);
string name = Utils.Format("Boiler #{0}", unitNumber);
boiler.Create(
context,
null,
new QualifiedName(name, m_namespaceIndex),
null,
true);
NodeState folder = FindPredefinedNode(
ExpandedNodeId.ToNodeId(ObjectIds.Boilers, Server.NamespaceUris),
typeof(NodeState));
folder.AddReference(Opc.Ua.ReferenceTypeIds.Organizes, false, boiler.NodeId);
boiler.AddReference(Opc.Ua.ReferenceTypeIds.Organizes, true, folder.NodeId);
string unitLabel = Utils.Format("{0}0", unitNumber);
UpdateDisplayName(boiler.InputPipe, unitLabel);
UpdateDisplayName(boiler.Drum, unitLabel);
UpdateDisplayName(boiler.OutputPipe, unitLabel);
UpdateDisplayName(boiler.LevelController, unitLabel);
UpdateDisplayName(boiler.FlowController, unitLabel);
UpdateDisplayName(boiler.CustomController, unitLabel);
m_boilers.Add(boiler);
AddPredefinedNode(context, boiler);
// Autostart boiler simulation state machine
MethodState start = boiler.Simulation.Start;
IList<Variant> inputArguments = new List<Variant>();
IList<Variant> outputArguments = new List<Variant>();
List<ServiceResult> errors = new List<ServiceResult>();
start.Call(context, boiler.NodeId, inputArguments, errors, outputArguments);
}
/// <summary>
/// Updates the display name for an instance with the unit label name.
/// </summary>
/// <param name="instance">The instance to update.</param>
/// <param name="unitLabel">The label to apply.</param>
/// <remarks>This method assumes the DisplayName has the form NameX001 where X0 is the unit label placeholder.</remarks>
private static void UpdateDisplayName(BaseInstanceState instance, string unitLabel)
{
LocalizedText displayName = instance.DisplayName;
if (displayName != null)
{
string text = displayName.Text;
if (text != null)
{
text = text.Replace("X0", unitLabel);
}
displayName = new LocalizedText(displayName.Locale, text);
}
instance.DisplayName = displayName;
}
/// <summary>
/// Loads a node set from a file or resource and addes them to the set of predefined nodes.
/// </summary>
protected override NodeStateCollection LoadPredefinedNodes(ISystemContext context)
{
NodeStateCollection predefinedNodes = new NodeStateCollection();
predefinedNodes.LoadFromBinaryResource(context, "Quickstarts.Servers.Boiler.Boiler.PredefinedNodes.uanodes", this.GetType().GetTypeInfo().Assembly, true);
return predefinedNodes;
}
/// <summary>
/// Replaces the generic node with a node specific to the model.
/// </summary>
protected override NodeState AddBehaviourToPredefinedNode(ISystemContext context, NodeState predefinedNode)
{
BaseObjectState passiveNode = predefinedNode as BaseObjectState;
if (passiveNode == null)
{
return predefinedNode;
}
NodeId typeId = passiveNode.TypeDefinitionId;
if (!IsNodeIdInNamespace(typeId) || typeId.IdType != IdType.Numeric)
{
return predefinedNode;
}
switch ((uint)typeId.Identifier)
{
case ObjectTypes.BoilerType:
{
if (passiveNode is BoilerState)
{
break;
}
BoilerState activeNode = new BoilerState(passiveNode.Parent);
activeNode.Create(context, passiveNode);
// replace the node in the parent.
if (passiveNode.Parent != null)
{
passiveNode.Parent.ReplaceChild(context, activeNode);
}
// Autostart boiler simulation state machine
MethodState start = activeNode.Simulation.Start;
IList<Variant> inputArguments = new List<Variant>();
IList<Variant> outputArguments = new List<Variant>();
List<ServiceResult> errors = new List<ServiceResult>();
start.Call(context, activeNode.NodeId, inputArguments, errors, outputArguments);
return activeNode;
}
}
return predefinedNode;
}
/// <summary>
/// Does any processing after a monitored item is created.
/// </summary>
protected override void OnCreateMonitoredItem(
ISystemContext systemContext,
MonitoredItemCreateRequest itemToCreate,
MonitoredNode monitoredNode,
DataChangeMonitoredItem monitoredItem)
{
// TBD
}
/// <summary>
/// Does any processing after a monitored item is created.
/// </summary>
protected override void OnModifyMonitoredItem(
ISystemContext systemContext,
MonitoredItemModifyRequest itemToModify,
MonitoredNode monitoredNode,
DataChangeMonitoredItem monitoredItem,
double previousSamplingInterval)
{
// TBD
}
/// <summary>
/// Does any processing after a monitored item is deleted.
/// </summary>
protected override void OnDeleteMonitoredItem(
ISystemContext systemContext,
MonitoredNode monitoredNode,
DataChangeMonitoredItem monitoredItem)
{
// TBD
}
/// <summary>
/// Does any processing after a monitored item is created.
/// </summary>
protected override void OnSetMonitoringMode(
ISystemContext systemContext,
MonitoredNode monitoredNode,
DataChangeMonitoredItem monitoredItem,
MonitoringMode previousMode,
MonitoringMode currentMode)
{
// TBD
}
#endregion
#region Private Fields
private ushort m_namespaceIndex;
private ushort m_typeNamespaceIndex;
private long m_lastUsedId;
private List<BoilerState> m_boilers;
#endregion
}
}
@@ -0,0 +1,293 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Text;
using System.Threading;
using Opc.Ua;
namespace Boiler
{
public partial class BoilerState
{
#region Initialization
/// <summary>
/// Initializes the object as a collection of counters which change value on read.
/// </summary>
protected override void OnAfterCreate(ISystemContext context, NodeState node)
{
base.OnAfterCreate(context, node);
this.Simulation.OnAfterTransition = OnControlSimulation;
m_random = new Random();
}
#endregion
#region IDisposeable Methods
/// <summary>
/// Cleans up when the object is disposed.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (m_simulationTimer != null)
{
m_simulationTimer.Dispose();
m_simulationTimer = null;
}
}
}
#endregion
#region Private Methods
/// <summary>
/// Changes the state of the simulation.
/// </summary>
private ServiceResult OnControlSimulation(
ISystemContext context,
StateMachineState machine,
uint transitionId,
uint causeId,
IList<object> inputArguments,
IList<object> outputArguments)
{
switch (causeId)
{
case Opc.Ua.Methods.ProgramStateMachineType_Start:
{
if (m_simulationTimer != null)
{
m_simulationTimer.Dispose();
m_simulationTimer = null;
}
uint updateRate = this.Simulation.UpdateRate.Value;
if (updateRate < 100)
{
updateRate = 100;
Simulation.UpdateRate.Value = updateRate;
}
m_simulationContext = context;
m_simulationTimer = new Timer(DoSimulation, null, (int)updateRate, (int)updateRate);
break;
}
case Opc.Ua.Methods.ProgramStateMachineType_Halt:
case Opc.Ua.Methods.ProgramStateMachineType_Suspend:
{
if (m_simulationTimer != null)
{
m_simulationTimer.Dispose();
m_simulationTimer = null;
}
m_simulationContext = context;
break;
}
case Opc.Ua.Methods.ProgramStateMachineType_Reset:
{
if (m_simulationTimer != null)
{
m_simulationTimer.Dispose();
m_simulationTimer = null;
}
m_simulationContext = context;
break;
}
}
return ServiceResult.Good;
}
/// <summary>
/// Rounds a value to the significate digits specified and adds a random perturbation.
/// </summary>
private double RoundAndPerturb(double value, byte significantDigits)
{
double offsetToApply = 0;
if (value != 0)
{
// need to move all significate digits above the decimal point.
double offset = significantDigits - Math.Log10(Math.Abs(value));
offsetToApply = Math.Floor(offset);
if (offsetToApply == offset)
{
offsetToApply -= 1;
}
}
// round value to significant digits.
double perturbedValue = Math.Round(value * Math.Pow(10.0, offsetToApply));
// apply the perturbation.
perturbedValue += (m_random.NextDouble()-0.5)*5;
// restore original exponent.
perturbedValue = Math.Round(perturbedValue)*Math.Pow(10.0, -offsetToApply);
// return value.
return perturbedValue;
}
/// <summary>
/// Moves the value towards the target.
/// </summary>
private double Adjust(double value, double target, double step, Opc.Ua.Range range)
{
// convert percentage step to an absolute step if range is specified.
if (range != null)
{
step = step * range.Magnitude;
}
double difference = target - value;
if (difference < 0)
{
value -= step;
if (value < target)
{
return target;
}
}
else
{
value += step;
if (value > target)
{
return target;
}
}
return value;
}
/// <summary>
/// Returns the value as a percentage of the range.
/// </summary>
private double GetPercentage(AnalogItemState<double> value)
{
double percentage = value.Value;
Opc.Ua.Range range = value.EURange.Value;
if (range != null)
{
percentage /= Math.Abs(range.High - range.Low);
if (Math.Abs(percentage) > 1.0)
{
percentage = 1.0;
}
}
return percentage;
}
/// <summary>
/// Returns the value as a percentage of the range.
/// </summary>
private double GetValue(double value, Opc.Ua.Range range)
{
if (range != null)
{
return value * range.Magnitude;
}
return value;
}
/// <summary>
/// Updates the values for the simulation.
/// </summary>
private void DoSimulation(object state)
{
try
{
// adjust level.
m_drum.LevelIndicator.Output.Value = Adjust(
m_drum.LevelIndicator.Output.Value,
m_levelController.SetPoint.Value,
0.1,
m_drum.LevelIndicator.Output.EURange.Value);
// calculate inputs for custom controller.
m_customController.Input1.Value = m_levelController.UpdateMeasurement(m_drum.LevelIndicator.Output);
m_customController.Input2.Value = GetPercentage(m_inputPipe.FlowTransmitter1.Output);
m_customController.Input3.Value = GetPercentage(m_outputPipe.FlowTransmitter2.Output);
// calculate output for custom controller.
m_customController.ControlOut.Value = (m_customController.Input1.Value + m_customController.Input3.Value - m_customController.Input2.Value)/2;
// update flow controller set point.
m_flowController.SetPoint.Value = GetValue((m_customController.ControlOut.Value+1)/2, m_inputPipe.FlowTransmitter1.Output.EURange.Value);
double error = m_flowController.UpdateMeasurement(m_inputPipe.FlowTransmitter1.Output);
// adjust the input valve.
m_inputPipe.Valve.Input.Value = Adjust(m_inputPipe.Valve.Input.Value, (error>0)?100:0, 10, null);
// adjust the input flow.
m_inputPipe.FlowTransmitter1.Output.Value = Adjust(
m_inputPipe.FlowTransmitter1.Output.Value,
m_flowController.SetPoint.Value,
0.6,
m_inputPipe.FlowTransmitter1.Output.EURange.Value);
// add pertubations.
m_drum.LevelIndicator.Output.Value = RoundAndPerturb(m_drum.LevelIndicator.Output.Value, 3);
m_inputPipe.FlowTransmitter1.Output.Value = RoundAndPerturb(m_inputPipe.FlowTransmitter1.Output.Value, 3);
m_outputPipe.FlowTransmitter2.Output.Value = RoundAndPerturb(m_outputPipe.FlowTransmitter2.Output.Value, 3);
this.ClearChangeMasks(m_simulationContext, true);
}
catch (Exception e)
{
Utils.LogError(e, "Unexpected error during boiler simulation.");
}
}
#endregion
#region Private Fields
private ISystemContext m_simulationContext;
private Timer m_simulationTimer;
private Random m_random;
#endregion
}
}
@@ -0,0 +1,70 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Text;
using System.Threading;
using Opc.Ua;
namespace Boiler
{
public partial class BoilerStateMachineState
{
#region Initialization
/// <summary>
/// Initializes the object as a collection of counters which change value on read.
/// </summary>
protected override void OnAfterCreate(ISystemContext context, NodeState node)
{
base.OnAfterCreate(context, node);
Start.OnCallMethod = OnStart;
Start.OnReadExecutable = IsStartExecutable;
Start.OnReadUserExecutable = IsStartUserExecutable;
Suspend.OnCallMethod = OnSuspend;
Suspend.OnReadExecutable = IsSuspendExecutable;
Suspend.OnReadUserExecutable = IsSuspendUserExecutable;
Resume.OnCallMethod = OnResume;
Resume.OnReadExecutable = IsResumeExecutable;
Resume.OnReadUserExecutable = IsResumeUserExecutable;
Halt.OnCallMethod = OnHalt;
Halt.OnReadExecutable = IsHaltExecutable;
Halt.OnReadUserExecutable = IsHaltUserExecutable;
Reset.OnCallMethod = OnReset;
Reset.OnReadExecutable = IsResetExecutable;
Reset.OnReadUserExecutable = IsResetUserExecutable;
}
#endregion
}
}
@@ -0,0 +1,91 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Runtime.Serialization;
using System.Reflection;
using System.Threading;
using Opc.Ua;
using Opc.Ua.Server;
using Range = Opc.Ua.Range;
namespace Boiler
{
/// <summary>
/// A object representing a generic controller.
/// </summary>
public partial class GenericControllerState
{
#region Public Interface
/// <summary>
/// Updates the measurement and calculates the new control output.
/// </summary>
public double UpdateMeasurement(AnalogItemState<double> source)
{
Range range = source.EURange.Value;
m_measurement.Value = source.Value;
// clamp the setpoint.
if (range != null)
{
if (m_setPoint.Value > range.High)
{
m_setPoint.Value = range.High;
}
if (m_setPoint.Value < range.Low)
{
m_setPoint.Value = range.Low;
}
}
// calculate error.
m_controlOut.Value = m_setPoint.Value - m_measurement.Value;
if (range != null)
{
m_controlOut.Value /= range.Magnitude;
if (Math.Abs(m_controlOut.Value) > 1.0)
{
m_controlOut.Value = (m_controlOut.Value < 0)?-1.0:+1.0;
}
}
// return the new output.
return m_controlOut.Value;
}
#endregion
}
}
@@ -0,0 +1,14 @@
@echo off
setlocal
echo Building TestData
Opc.Ua.ModelCompiler.exe compile -version v104 -d2 ".\TestData\TestDataDesign.xml" -cg ".\TestData\TestDataDesign.csv" -o2 ".\TestData"
echo Success!
echo Building MemoryBuffer
Opc.Ua.ModelCompiler.exe compile -version v104 -d2 ".\MemoryBuffer\MemoryBufferDesign.xml" -cg ".\MemoryBuffer\MemoryBufferDesign.csv" -o2 ".\MemoryBuffer"
echo Success!
echo Building BoilerDesign
Opc.Ua.ModelCompiler.exe compile -version v104 -d2 ".\Boiler\BoilerDesign.xml" -c ".\Boiler\BoilerDesign.csv" -o2 ".\Boiler"
echo Success!
@@ -0,0 +1,390 @@
/* ========================================================================
* Copyright (c) 2005-2021 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 System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Runtime.Serialization;
using Opc.Ua;
namespace MemoryBuffer
{
#region MemoryTagState Class
#if (!OPCUA_EXCLUDE_MemoryTagState)
/// <summary>
/// Stores an instance of the MemoryTagType VariableType.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public partial class MemoryTagState : BaseDataVariableState
{
#region Constructors
/// <summary>
/// Initializes the type with its default attribute values.
/// </summary>
public MemoryTagState(NodeState parent) : base(parent)
{
}
/// <summary>
/// Returns the id of the default type definition node for the instance.
/// </summary>
protected override NodeId GetDefaultTypeDefinitionId(NamespaceTable namespaceUris)
{
return Opc.Ua.NodeId.Create(MemoryBuffer.VariableTypes.MemoryTagType, MemoryBuffer.Namespaces.MemoryBuffer, namespaceUris);
}
/// <summary>
/// Returns the id of the default data type node for the instance.
/// </summary>
protected override NodeId GetDefaultDataTypeId(NamespaceTable namespaceUris)
{
return Opc.Ua.NodeId.Create(Opc.Ua.DataTypes.BaseDataType, Opc.Ua.Namespaces.OpcUa, namespaceUris);
}
/// <summary>
/// Returns the id of the default value rank for the instance.
/// </summary>
protected override int GetDefaultValueRank()
{
return ValueRanks.Scalar;
}
#if (!OPCUA_EXCLUDE_InitializationStrings)
/// <summary>
/// Initializes the instance.
/// </summary>
protected override void Initialize(ISystemContext context)
{
base.Initialize(context);
Initialize(context, InitializationString);
InitializeOptionalChildren(context);
}
/// <summary>
/// Initializes the instance with a node.
/// </summary>
protected override void Initialize(ISystemContext context, NodeState source)
{
InitializeOptionalChildren(context);
base.Initialize(context, source);
}
/// <summary>
/// Initializes the any option children defined for the instance.
/// </summary>
protected override void InitializeOptionalChildren(ISystemContext context)
{
base.InitializeOptionalChildren(context);
}
#region Initialization String
private const string InitializationString =
"AQAAACIAAABodHRwOi8vc2FtcGxlcy5vcmcvVUEvTWVtb3J5QnVmZmVy/////xVggQICAAAAAQAVAAAA" +
"TWVtb3J5VGFnVHlwZUluc3RhbmNlAQH6AwEB+gP6AwAAABgBAf////8AAAAA";
#endregion
#endif
#endregion
#region Public Properties
#endregion
#region Overridden Methods
#endregion
#region Private Fields
#endregion
}
#region MemoryTagState<T> Class
/// <summary>
/// A typed version of the MemoryTagType variable.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public class MemoryTagState<T> : MemoryTagState
{
#region Constructors
/// <summary>
/// Initializes the instance with its defalt attribute values.
/// </summary>
public MemoryTagState(NodeState parent) : base(parent)
{
Value = default(T);
}
/// <summary>
/// Initializes the instance with the default values.
/// </summary>
protected override void Initialize(ISystemContext context)
{
base.Initialize(context);
Value = default(T);
DataType = TypeInfo.GetDataTypeId(typeof(T));
ValueRank = TypeInfo.GetValueRank(typeof(T));
}
/// <summary>
/// Initializes the instance with a node.
/// </summary>
protected override void Initialize(ISystemContext context, NodeState source)
{
InitializeOptionalChildren(context);
base.Initialize(context, source);
}
#endregion
#region Public Members
/// <summary>
/// The value of the variable.
/// </summary>
public new T Value
{
get
{
return CheckTypeBeforeCast<T>(base.Value, true);
}
set
{
base.Value = value;
}
}
#endregion
}
#endregion
#endif
#endregion
#region MemoryBufferState Class
#if (!OPCUA_EXCLUDE_MemoryBufferState)
/// <summary>
/// Stores an instance of the MemoryBufferType ObjectType.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public partial class MemoryBufferState : BaseObjectState
{
#region Constructors
/// <summary>
/// Initializes the type with its default attribute values.
/// </summary>
public MemoryBufferState(NodeState parent) : base(parent)
{
}
/// <summary>
/// Returns the id of the default type definition node for the instance.
/// </summary>
protected override NodeId GetDefaultTypeDefinitionId(NamespaceTable namespaceUris)
{
return Opc.Ua.NodeId.Create(MemoryBuffer.ObjectTypes.MemoryBufferType, MemoryBuffer.Namespaces.MemoryBuffer, namespaceUris);
}
#if (!OPCUA_EXCLUDE_InitializationStrings)
/// <summary>
/// Initializes the instance.
/// </summary>
protected override void Initialize(ISystemContext context)
{
base.Initialize(context);
Initialize(context, InitializationString);
InitializeOptionalChildren(context);
}
/// <summary>
/// Initializes the instance with a node.
/// </summary>
protected override void Initialize(ISystemContext context, NodeState source)
{
InitializeOptionalChildren(context);
base.Initialize(context, source);
}
/// <summary>
/// Initializes the any option children defined for the instance.
/// </summary>
protected override void InitializeOptionalChildren(ISystemContext context)
{
base.InitializeOptionalChildren(context);
}
#region Initialization String
private const string InitializationString =
"AQAAACIAAABodHRwOi8vc2FtcGxlcy5vcmcvVUEvTWVtb3J5QnVmZmVy/////wRggAIBAAAAAQAYAAAA" +
"TWVtb3J5QnVmZmVyVHlwZUluc3RhbmNlAQHoAwEB6APoAwAA/////wIAAAAVYKkKAgAAAAEADAAAAFN0" +
"YXJ0QWRkcmVzcwEB6wMALgBE6wMAAAcAAAAAAAf/////AQH/////AAAAABVgqQoCAAAAAQALAAAAU2l6" +
"ZUluQnl0ZXMBAewDAC4AROwDAAAHABAAAAAH/////wEB/////wAAAAA=";
#endregion
#endif
#endregion
#region Public Properties
/// <remarks />
public PropertyState<uint> StartAddress
{
get
{
return m_startAddress;
}
set
{
if (!Object.ReferenceEquals(m_startAddress, value))
{
ChangeMasks |= NodeStateChangeMasks.Children;
}
m_startAddress = value;
}
}
/// <remarks />
public PropertyState<uint> SizeInBytes
{
get
{
return m_sizeInBytes;
}
set
{
if (!Object.ReferenceEquals(m_sizeInBytes, value))
{
ChangeMasks |= NodeStateChangeMasks.Children;
}
m_sizeInBytes = value;
}
}
#endregion
#region Overridden Methods
/// <summary>
/// Populates a list with the children that belong to the node.
/// </summary>
/// <param name="context">The context for the system being accessed.</param>
/// <param name="children">The list of children to populate.</param>
public override void GetChildren(
ISystemContext context,
IList<BaseInstanceState> children)
{
if (m_startAddress != null)
{
children.Add(m_startAddress);
}
if (m_sizeInBytes != null)
{
children.Add(m_sizeInBytes);
}
base.GetChildren(context, children);
}
/// <summary>
/// Finds the child with the specified browse name.
/// </summary>
protected override BaseInstanceState FindChild(
ISystemContext context,
QualifiedName browseName,
bool createOrReplace,
BaseInstanceState replacement)
{
if (QualifiedName.IsNull(browseName))
{
return null;
}
BaseInstanceState instance = null;
switch (browseName.Name)
{
case MemoryBuffer.BrowseNames.StartAddress:
{
if (createOrReplace)
{
if (StartAddress == null)
{
if (replacement == null)
{
StartAddress = new PropertyState<uint>(this);
}
else
{
StartAddress = (PropertyState<uint>)replacement;
}
}
}
instance = StartAddress;
break;
}
case MemoryBuffer.BrowseNames.SizeInBytes:
{
if (createOrReplace)
{
if (SizeInBytes == null)
{
if (replacement == null)
{
SizeInBytes = new PropertyState<uint>(this);
}
else
{
SizeInBytes = (PropertyState<uint>)replacement;
}
}
}
instance = SizeInBytes;
break;
}
}
if (instance != null)
{
return instance;
}
return base.FindChild(context, browseName, createOrReplace, replacement);
}
#endregion
#region Private Fields
private PropertyState<uint> m_startAddress;
private PropertyState<uint> m_sizeInBytes;
#endregion
}
#endif
#endregion
}
@@ -0,0 +1,227 @@
/* ========================================================================
* Copyright (c) 2005-2021 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 System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Xml;
using System.Runtime.Serialization;
using Opc.Ua;
namespace MemoryBuffer
{
#region Object Identifiers
/// <summary>
/// A class that declares constants for all Objects in the Model Design.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class Objects
{
/// <summary>
/// The identifier for the MemoryBuffers Object.
/// </summary>
public const uint MemoryBuffers = 1025;
}
#endregion
#region ObjectType Identifiers
/// <summary>
/// A class that declares constants for all ObjectTypes in the Model Design.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class ObjectTypes
{
/// <summary>
/// The identifier for the MemoryBufferType ObjectType.
/// </summary>
public const uint MemoryBufferType = 1000;
}
#endregion
#region Variable Identifiers
/// <summary>
/// A class that declares constants for all Variables in the Model Design.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class Variables
{
/// <summary>
/// The identifier for the MemoryBufferType_StartAddress Variable.
/// </summary>
public const uint MemoryBufferType_StartAddress = 1003;
/// <summary>
/// The identifier for the MemoryBufferType_SizeInBytes Variable.
/// </summary>
public const uint MemoryBufferType_SizeInBytes = 1004;
}
#endregion
#region VariableType Identifiers
/// <summary>
/// A class that declares constants for all VariableTypes in the Model Design.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class VariableTypes
{
/// <summary>
/// The identifier for the MemoryTagType VariableType.
/// </summary>
public const uint MemoryTagType = 1018;
}
#endregion
#region Object Node Identifiers
/// <summary>
/// A class that declares constants for all Objects in the Model Design.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class ObjectIds
{
/// <summary>
/// The identifier for the MemoryBuffers Object.
/// </summary>
public static readonly ExpandedNodeId MemoryBuffers = new ExpandedNodeId(MemoryBuffer.Objects.MemoryBuffers, MemoryBuffer.Namespaces.MemoryBuffer);
}
#endregion
#region ObjectType Node Identifiers
/// <summary>
/// A class that declares constants for all ObjectTypes in the Model Design.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class ObjectTypeIds
{
/// <summary>
/// The identifier for the MemoryBufferType ObjectType.
/// </summary>
public static readonly ExpandedNodeId MemoryBufferType = new ExpandedNodeId(MemoryBuffer.ObjectTypes.MemoryBufferType, MemoryBuffer.Namespaces.MemoryBuffer);
}
#endregion
#region Variable Node Identifiers
/// <summary>
/// A class that declares constants for all Variables in the Model Design.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class VariableIds
{
/// <summary>
/// The identifier for the MemoryBufferType_StartAddress Variable.
/// </summary>
public static readonly ExpandedNodeId MemoryBufferType_StartAddress = new ExpandedNodeId(MemoryBuffer.Variables.MemoryBufferType_StartAddress, MemoryBuffer.Namespaces.MemoryBuffer);
/// <summary>
/// The identifier for the MemoryBufferType_SizeInBytes Variable.
/// </summary>
public static readonly ExpandedNodeId MemoryBufferType_SizeInBytes = new ExpandedNodeId(MemoryBuffer.Variables.MemoryBufferType_SizeInBytes, MemoryBuffer.Namespaces.MemoryBuffer);
}
#endregion
#region VariableType Node Identifiers
/// <summary>
/// A class that declares constants for all VariableTypes in the Model Design.
/// </summary>
/// <exclude />
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class VariableTypeIds
{
/// <summary>
/// The identifier for the MemoryTagType VariableType.
/// </summary>
public static readonly ExpandedNodeId MemoryTagType = new ExpandedNodeId(MemoryBuffer.VariableTypes.MemoryTagType, MemoryBuffer.Namespaces.MemoryBuffer);
}
#endregion
#region BrowseName Declarations
/// <summary>
/// Declares all of the BrowseNames used in the Model Design.
/// </summary>
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class BrowseNames
{
/// <summary>
/// The BrowseName for the MemoryBuffers component.
/// </summary>
public const string MemoryBuffers = "MemoryBuffers";
/// <summary>
/// The BrowseName for the MemoryBufferType component.
/// </summary>
public const string MemoryBufferType = "MemoryBufferType";
/// <summary>
/// The BrowseName for the MemoryTagType component.
/// </summary>
public const string MemoryTagType = "MemoryTagType";
/// <summary>
/// The BrowseName for the SizeInBytes component.
/// </summary>
public const string SizeInBytes = "SizeInBytes";
/// <summary>
/// The BrowseName for the StartAddress component.
/// </summary>
public const string StartAddress = "StartAddress";
}
#endregion
#region Namespace Declarations
/// <summary>
/// Defines constants for all namespaces referenced by the model design.
/// </summary>
[System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
public static partial class Namespaces
{
/// <summary>
/// The URI for the OpcUa namespace (.NET code namespace is 'Opc.Ua').
/// </summary>
public const string OpcUa = "http://opcfoundation.org/UA/";
/// <summary>
/// The URI for the OpcUaXsd namespace (.NET code namespace is 'Opc.Ua').
/// </summary>
public const string OpcUaXsd = "http://opcfoundation.org/UA/2008/02/Types.xsd";
/// <summary>
/// The URI for the MemoryBuffer namespace (.NET code namespace is 'MemoryBuffer').
/// </summary>
public const string MemoryBuffer = "http://samples.org/UA/MemoryBuffer";
}
#endregion
}
@@ -1,5 +1,5 @@
/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
@@ -28,16 +28,12 @@
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Runtime.Serialization;
using Opc.Ua;
#if NETFRAMEWORK
namespace Opc.Ua.Core.Tests
namespace MemoryBuffer
{
static class Program
{
// Main Method
static public void Main(String[] args)
{
}
}
}
#endif
@@ -0,0 +1,3 @@
MemoryBuffers,1025,Object
MemoryBufferType,1000,ObjectType
MemoryTagType,1018,VariableType
1 MemoryBuffers 1025 Object
2 MemoryBufferType 1000 ObjectType
3 MemoryTagType 1018 VariableType
@@ -0,0 +1,271 @@
<?xml version="1.0" encoding="utf-8"?>
<NodeSet xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
<NamespaceUris>
<String>http://opcfoundation.org/UA/</String>
<String>http://samples.org/UA/MemoryBuffer</String>
</NamespaceUris>
<ServerUris />
<Nodes>
<Node i:type="ObjectTypeNode">
<NodeId>
<Identifier>ns=1;i=1000</Identifier>
</NodeId>
<NodeClass>ObjectType_8</NodeClass>
<BrowseName>
<NamespaceIndex>1</NamespaceIndex>
<Name>MemoryBufferType</Name>
</BrowseName>
<DisplayName>
<Locale></Locale>
<Text>MemoryBufferType</Text>
</DisplayName>
<Description i:nil="true" />
<WriteMask>0</WriteMask>
<UserWriteMask>0</UserWriteMask>
<RolePermissions />
<UserRolePermissions />
<AccessRestrictions>0</AccessRestrictions>
<References>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=45</Identifier>
</ReferenceTypeId>
<IsInverse>true</IsInverse>
<TargetId>
<Identifier>i=58</Identifier>
</TargetId>
</ReferenceNode>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=46</Identifier>
</ReferenceTypeId>
<IsInverse>false</IsInverse>
<TargetId>
<Identifier>ns=1;i=1003</Identifier>
</TargetId>
</ReferenceNode>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=46</Identifier>
</ReferenceTypeId>
<IsInverse>false</IsInverse>
<TargetId>
<Identifier>ns=1;i=1004</Identifier>
</TargetId>
</ReferenceNode>
</References>
<IsAbstract>false</IsAbstract>
</Node>
<Node i:type="VariableNode">
<NodeId>
<Identifier>ns=1;i=1003</Identifier>
</NodeId>
<NodeClass>Variable_2</NodeClass>
<BrowseName>
<NamespaceIndex>1</NamespaceIndex>
<Name>StartAddress</Name>
</BrowseName>
<DisplayName>
<Locale></Locale>
<Text>StartAddress</Text>
</DisplayName>
<Description i:nil="true" />
<WriteMask>0</WriteMask>
<UserWriteMask>0</UserWriteMask>
<RolePermissions />
<UserRolePermissions />
<AccessRestrictions>0</AccessRestrictions>
<References>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=46</Identifier>
</ReferenceTypeId>
<IsInverse>true</IsInverse>
<TargetId>
<Identifier>ns=1;i=1000</Identifier>
</TargetId>
</ReferenceNode>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=40</Identifier>
</ReferenceTypeId>
<IsInverse>false</IsInverse>
<TargetId>
<Identifier>i=68</Identifier>
</TargetId>
</ReferenceNode>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=37</Identifier>
</ReferenceTypeId>
<IsInverse>false</IsInverse>
<TargetId>
<Identifier>i=78</Identifier>
</TargetId>
</ReferenceNode>
</References>
<Value>
<Value>
<UInt32 xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">0</UInt32>
</Value>
</Value>
<DataType>
<Identifier>i=7</Identifier>
</DataType>
<ValueRank>-1</ValueRank>
<ArrayDimensions />
<AccessLevel>1</AccessLevel>
<UserAccessLevel>1</UserAccessLevel>
<MinimumSamplingInterval>0</MinimumSamplingInterval>
<Historizing>false</Historizing>
<AccessLevelEx>0</AccessLevelEx>
</Node>
<Node i:type="VariableNode">
<NodeId>
<Identifier>ns=1;i=1004</Identifier>
</NodeId>
<NodeClass>Variable_2</NodeClass>
<BrowseName>
<NamespaceIndex>1</NamespaceIndex>
<Name>SizeInBytes</Name>
</BrowseName>
<DisplayName>
<Locale></Locale>
<Text>SizeInBytes</Text>
</DisplayName>
<Description i:nil="true" />
<WriteMask>0</WriteMask>
<UserWriteMask>0</UserWriteMask>
<RolePermissions />
<UserRolePermissions />
<AccessRestrictions>0</AccessRestrictions>
<References>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=46</Identifier>
</ReferenceTypeId>
<IsInverse>true</IsInverse>
<TargetId>
<Identifier>ns=1;i=1000</Identifier>
</TargetId>
</ReferenceNode>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=40</Identifier>
</ReferenceTypeId>
<IsInverse>false</IsInverse>
<TargetId>
<Identifier>i=68</Identifier>
</TargetId>
</ReferenceNode>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=37</Identifier>
</ReferenceTypeId>
<IsInverse>false</IsInverse>
<TargetId>
<Identifier>i=78</Identifier>
</TargetId>
</ReferenceNode>
</References>
<Value>
<Value>
<UInt32 xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">4096</UInt32>
</Value>
</Value>
<DataType>
<Identifier>i=7</Identifier>
</DataType>
<ValueRank>-1</ValueRank>
<ArrayDimensions />
<AccessLevel>1</AccessLevel>
<UserAccessLevel>1</UserAccessLevel>
<MinimumSamplingInterval>0</MinimumSamplingInterval>
<Historizing>false</Historizing>
<AccessLevelEx>0</AccessLevelEx>
</Node>
<Node i:type="VariableTypeNode">
<NodeId>
<Identifier>ns=1;i=1018</Identifier>
</NodeId>
<NodeClass>VariableType_16</NodeClass>
<BrowseName>
<NamespaceIndex>1</NamespaceIndex>
<Name>MemoryTagType</Name>
</BrowseName>
<DisplayName>
<Locale></Locale>
<Text>MemoryTagType</Text>
</DisplayName>
<Description i:nil="true" />
<WriteMask>0</WriteMask>
<UserWriteMask>0</UserWriteMask>
<RolePermissions />
<UserRolePermissions />
<AccessRestrictions>0</AccessRestrictions>
<References>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=45</Identifier>
</ReferenceTypeId>
<IsInverse>true</IsInverse>
<TargetId>
<Identifier>i=63</Identifier>
</TargetId>
</ReferenceNode>
</References>
<Value>
<Value>
<Null xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd" />
</Value>
</Value>
<DataType>
<Identifier>i=24</Identifier>
</DataType>
<ValueRank>-2</ValueRank>
<ArrayDimensions />
<IsAbstract>false</IsAbstract>
</Node>
<Node i:type="ObjectNode">
<NodeId>
<Identifier>ns=1;i=1025</Identifier>
</NodeId>
<NodeClass>Object_1</NodeClass>
<BrowseName>
<NamespaceIndex>1</NamespaceIndex>
<Name>MemoryBuffers</Name>
</BrowseName>
<DisplayName>
<Locale></Locale>
<Text>MemoryBuffers</Text>
</DisplayName>
<Description i:nil="true" />
<WriteMask>0</WriteMask>
<UserWriteMask>0</UserWriteMask>
<RolePermissions />
<UserRolePermissions />
<AccessRestrictions>0</AccessRestrictions>
<References>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=40</Identifier>
</ReferenceTypeId>
<IsInverse>false</IsInverse>
<TargetId>
<Identifier>i=61</Identifier>
</TargetId>
</ReferenceNode>
<ReferenceNode>
<ReferenceTypeId>
<Identifier>i=35</Identifier>
</ReferenceTypeId>
<IsInverse>true</IsInverse>
<TargetId>
<Identifier>i=85</Identifier>
</TargetId>
</ReferenceNode>
</References>
<EventNotifier>0</EventNotifier>
</Node>
</Nodes>
</NodeSet>
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
<NamespaceUris>
<Uri>http://samples.org/UA/MemoryBuffer</Uri>
</NamespaceUris>
<Models>
<Model ModelUri="http://samples.org/UA/MemoryBuffer">
<RequiredModel ModelUri="http://opcfoundation.org/UA/" XmlSchemaUri="http://opcfoundation.org/UA/2008/02/Types.xsd" Version="1.04.10" PublicationDate="2021-09-15T00:00:00Z" />
</Model>
</Models>
<Aliases>
<Alias Alias="Boolean">i=1</Alias>
<Alias Alias="SByte">i=2</Alias>
<Alias Alias="Byte">i=3</Alias>
<Alias Alias="Int16">i=4</Alias>
<Alias Alias="UInt16">i=5</Alias>
<Alias Alias="Int32">i=6</Alias>
<Alias Alias="UInt32">i=7</Alias>
<Alias Alias="Int64">i=8</Alias>
<Alias Alias="UInt64">i=9</Alias>
<Alias Alias="Float">i=10</Alias>
<Alias Alias="Double">i=11</Alias>
<Alias Alias="DateTime">i=13</Alias>
<Alias Alias="String">i=12</Alias>
<Alias Alias="ByteString">i=15</Alias>
<Alias Alias="Guid">i=14</Alias>
<Alias Alias="XmlElement">i=16</Alias>
<Alias Alias="NodeId">i=17</Alias>
<Alias Alias="ExpandedNodeId">i=18</Alias>
<Alias Alias="QualifiedName">i=20</Alias>
<Alias Alias="LocalizedText">i=21</Alias>
<Alias Alias="StatusCode">i=19</Alias>
<Alias Alias="Structure">i=22</Alias>
<Alias Alias="Number">i=26</Alias>
<Alias Alias="Integer">i=27</Alias>
<Alias Alias="UInteger">i=28</Alias>
<Alias Alias="HasComponent">i=47</Alias>
<Alias Alias="HasProperty">i=46</Alias>
<Alias Alias="Organizes">i=35</Alias>
<Alias Alias="HasEventSource">i=36</Alias>
<Alias Alias="HasNotifier">i=48</Alias>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasEncoding">i=38</Alias>
<Alias Alias="HasDescription">i=39</Alias>
<Alias Alias="HasCause">i=53</Alias>
<Alias Alias="ToState">i=52</Alias>
<Alias Alias="FromState">i=51</Alias>
<Alias Alias="HasEffect">i=54</Alias>
<Alias Alias="HasTrueSubState">i=9004</Alias>
<Alias Alias="HasFalseSubState">i=9005</Alias>
<Alias Alias="HasDictionaryEntry">i=17597</Alias>
<Alias Alias="HasCondition">i=9006</Alias>
<Alias Alias="HasGuard">i=15112</Alias>
<Alias Alias="HasAddIn">i=17604</Alias>
<Alias Alias="HasInterface">i=17603</Alias>
</Aliases>
<UAVariableType NodeId="ns=1;i=1018" BrowseName="1:MemoryTagType" ValueRank="-2">
<DisplayName>MemoryTagType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=63</Reference>
</References>
</UAVariableType>
<UAObjectType NodeId="ns=1;i=1000" BrowseName="1:MemoryBufferType">
<DisplayName>MemoryBufferType</DisplayName>
<References>
<Reference ReferenceType="HasProperty">ns=1;i=1003</Reference>
<Reference ReferenceType="HasProperty">ns=1;i=1004</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAVariable NodeId="ns=1;i=1003" BrowseName="1:StartAddress" ParentNodeId="ns=1;i=1000" DataType="UInt32">
<DisplayName>StartAddress</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=1000</Reference>
</References>
<Value>
<UInt32 xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">0</UInt32>
</Value>
</UAVariable>
<UAVariable NodeId="ns=1;i=1004" BrowseName="1:SizeInBytes" ParentNodeId="ns=1;i=1000" DataType="UInt32">
<DisplayName>SizeInBytes</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=1000</Reference>
</References>
<Value>
<UInt32 xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">4096</UInt32>
</Value>
</UAVariable>
<UAObject NodeId="ns=1;i=1025" BrowseName="1:MemoryBuffers">
<DisplayName>MemoryBuffers</DisplayName>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
</UANodeSet>
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8"?>
<uax:ListOfNodeState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd">
<uax:NamespaceUris>
<uax:NamespaceUri>http://samples.org/UA/MemoryBuffer</uax:NamespaceUri>
</uax:NamespaceUris>
<MemoryTagType xmlns="http://samples.org/UA/MemoryBuffer">
<uax:NodeClass>VariableType_16</uax:NodeClass>
<uax:NodeId>
<uax:Identifier>ns=1;i=1018</uax:Identifier>
</uax:NodeId>
<uax:BrowseName>
<uax:NamespaceIndex>1</uax:NamespaceIndex>
<uax:Name>MemoryTagType</uax:Name>
</uax:BrowseName>
<uax:SuperTypeId>
<uax:Identifier>i=63</uax:Identifier>
</uax:SuperTypeId>
<uax:DataType>
<uax:Identifier>i=24</uax:Identifier>
</uax:DataType>
</MemoryTagType>
<MemoryBufferType xmlns="http://samples.org/UA/MemoryBuffer">
<uax:NodeClass>ObjectType_8</uax:NodeClass>
<uax:NodeId>
<uax:Identifier>ns=1;i=1000</uax:Identifier>
</uax:NodeId>
<uax:BrowseName>
<uax:NamespaceIndex>1</uax:NamespaceIndex>
<uax:Name>MemoryBufferType</uax:Name>
</uax:BrowseName>
<uax:SuperTypeId>
<uax:Identifier>i=58</uax:Identifier>
</uax:SuperTypeId>
<StartAddress>
<uax:NodeClass>Variable_2</uax:NodeClass>
<uax:NodeId>
<uax:Identifier>ns=1;i=1003</uax:Identifier>
</uax:NodeId>
<uax:BrowseName>
<uax:NamespaceIndex>1</uax:NamespaceIndex>
<uax:Name>StartAddress</uax:Name>
</uax:BrowseName>
<uax:ReferenceTypeId>
<uax:Identifier>i=46</uax:Identifier>
</uax:ReferenceTypeId>
<uax:TypeDefinitionId>
<uax:Identifier>i=68</uax:Identifier>
</uax:TypeDefinitionId>
<uax:ModellingRuleId>
<uax:Identifier>i=78</uax:Identifier>
</uax:ModellingRuleId>
<uax:NumericId>1003</uax:NumericId>
<uax:Value>
<uax:Value>
<uax:UInt32>0</uax:UInt32>
</uax:Value>
</uax:Value>
<uax:DataType>
<uax:Identifier>i=7</uax:Identifier>
</uax:DataType>
<uax:ValueRank>-1</uax:ValueRank>
<uax:AccessLevel>1</uax:AccessLevel>
<uax:UserAccessLevel>1</uax:UserAccessLevel>
</StartAddress>
<SizeInBytes>
<uax:NodeClass>Variable_2</uax:NodeClass>
<uax:NodeId>
<uax:Identifier>ns=1;i=1004</uax:Identifier>
</uax:NodeId>
<uax:BrowseName>
<uax:NamespaceIndex>1</uax:NamespaceIndex>
<uax:Name>SizeInBytes</uax:Name>
</uax:BrowseName>
<uax:ReferenceTypeId>
<uax:Identifier>i=46</uax:Identifier>
</uax:ReferenceTypeId>
<uax:TypeDefinitionId>
<uax:Identifier>i=68</uax:Identifier>
</uax:TypeDefinitionId>
<uax:ModellingRuleId>
<uax:Identifier>i=78</uax:Identifier>
</uax:ModellingRuleId>
<uax:NumericId>1004</uax:NumericId>
<uax:Value>
<uax:Value>
<uax:UInt32>4096</uax:UInt32>
</uax:Value>
</uax:Value>
<uax:DataType>
<uax:Identifier>i=7</uax:Identifier>
</uax:DataType>
<uax:ValueRank>-1</uax:ValueRank>
<uax:AccessLevel>1</uax:AccessLevel>
<uax:UserAccessLevel>1</uax:UserAccessLevel>
</SizeInBytes>
</MemoryBufferType>
<MemoryBuffers xmlns="http://samples.org/UA/MemoryBuffer">
<uax:NodeClass>Object_1</uax:NodeClass>
<uax:NodeId>
<uax:Identifier>ns=1;i=1025</uax:Identifier>
</uax:NodeId>
<uax:BrowseName>
<uax:NamespaceIndex>1</uax:NamespaceIndex>
<uax:Name>MemoryBuffers</uax:Name>
</uax:BrowseName>
<uax:ReferenceTypeId>
<uax:Identifier>i=47</uax:Identifier>
</uax:ReferenceTypeId>
<uax:TypeDefinitionId>
<uax:Identifier>i=61</uax:Identifier>
</uax:TypeDefinitionId>
<uax:NumericId>1025</uax:NumericId>
<uax:References>
<uax:Reference>
<uax:ReferenceTypeId>
<uax:Identifier>i=35</uax:Identifier>
</uax:ReferenceTypeId>
<uax:IsInverse>true</uax:IsInverse>
<uax:TargetId>
<uax:Identifier>i=85</uax:Identifier>
</uax:TargetId>
</uax:Reference>
</uax:References>
</MemoryBuffers>
</uax:ListOfNodeState>
@@ -0,0 +1,11 @@
<opc:TypeDictionary
xmlns:opc="http://opcfoundation.org/BinarySchema/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ua="http://opcfoundation.org/UA/"
xmlns:tns="http://samples.org/UA/MemoryBuffer"
DefaultByteOrder="LittleEndian"
TargetNamespace="http://samples.org/UA/MemoryBuffer"
>
<opc:Import Namespace="http://opcfoundation.org/UA/" Location="Opc.Ua.BinarySchema.bsd"/>
</opc:TypeDictionary>
@@ -0,0 +1,10 @@
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
xmlns:tns="http://samples.org/UA/MemoryBuffer"
targetNamespace="http://samples.org/UA/MemoryBuffer"
elementFormDefault="qualified"
>
<xs:import namespace="http://opcfoundation.org/UA/2008/02/Types.xsd" />
</xs:schema>
@@ -0,0 +1,217 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Threading;
using Opc.Ua;
using Opc.Ua.Server;
namespace MemoryBuffer
{
/// <summary>
/// A class to browse the references for a memory buffer.
/// </summary>
public class MemoryBufferBrowser : NodeBrowser
{
#region Constructors
/// <summary>
/// Creates a new browser object with a set of filters.
/// </summary>
public MemoryBufferBrowser(
ISystemContext context,
ViewDescription view,
NodeId referenceType,
bool includeSubtypes,
BrowseDirection browseDirection,
QualifiedName browseName,
IEnumerable<IReference> additionalReferences,
bool internalOnly,
MemoryBufferState buffer)
:
base(
context,
view,
referenceType,
includeSubtypes,
browseDirection,
browseName,
additionalReferences,
internalOnly)
{
m_buffer = buffer;
m_stage = Stage.Begin;
}
#endregion
#region Overridden Methods
/// <summary>
/// Returns the next reference.
/// </summary>
/// <returns></returns>
public override IReference Next()
{
lock (DataLock)
{
IReference reference = null;
// enumerate pre-defined references.
// always call first to ensure any pushed-back references are returned first.
reference = base.Next();
if (reference != null)
{
return reference;
}
if (m_stage == Stage.Begin)
{
m_stage = Stage.Components;
m_position = 0;
}
// don't start browsing huge number of references when only internal references are requested.
if (InternalOnly)
{
return null;
}
// enumerate components.
if (m_stage == Stage.Components)
{
if (IsRequired(ReferenceTypeIds.HasComponent, false))
{
reference = NextChild();
if (reference != null)
{
return reference;
}
}
m_stage = Stage.ModelParents;
m_position = 0;
}
// all done.
return null;
}
}
#endregion
#region Private Methods
/// <summary>
/// Returns the next child.
/// </summary>
private IReference NextChild()
{
MemoryTagState tag = null;
// check if a specific browse name is requested.
if (!QualifiedName.IsNull(base.BrowseName))
{
// check if match found previously.
if (m_position == UInt32.MaxValue)
{
return null;
}
// browse name must be qualified by the correct namespace.
if (m_buffer.TypeDefinitionId.NamespaceIndex != base.BrowseName.NamespaceIndex)
{
return null;
}
string name = base.BrowseName.Name;
for (int ii = 0; ii < name.Length; ii++)
{
if ("0123456789ABCDEF".IndexOf(name[ii]) == -1)
{
return null;
}
}
m_position = Convert.ToUInt32(name, 16);
// check for memory overflow.
if (m_position >= m_buffer.SizeInBytes.Value)
{
return null;
}
tag = new MemoryTagState(m_buffer, m_position);
m_position = UInt32.MaxValue;
}
// return the child at the next position.
else
{
if (m_position >= m_buffer.SizeInBytes.Value)
{
return null;
}
tag = new MemoryTagState(m_buffer, m_position);
m_position += m_buffer.ElementSize;
// check for memory overflow.
if (m_position >= m_buffer.SizeInBytes.Value)
{
return null;
}
}
return new NodeStateReference(ReferenceTypeIds.HasComponent, false, tag);
}
#endregion
#region Stage Enumeration
/// <summary>
/// The stages available in a browse operation.
/// </summary>
private enum Stage
{
Begin,
Components,
ModelParents,
Done
}
#endregion
#region Private Fields
private Stage m_stage;
private uint m_position;
private MemoryBufferState m_buffer;
#endregion
}
}
@@ -0,0 +1,171 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Runtime.Serialization;
using System.Collections.Generic;
using Opc.Ua.Server;
namespace MemoryBuffer
{
/// <summary>
/// Stores the configuration the test node manager
/// </summary>
[DataContract(Namespace = Namespaces.MemoryBuffer)]
public class MemoryBufferConfiguration
{
#region Constructors
/// <summary>
/// The default constructor.
/// </summary>
public MemoryBufferConfiguration()
{
Initialize();
}
/// <summary>
/// Initializes the object during deserialization.
/// </summary>
[OnDeserializing()]
private void Initialize(StreamingContext context)
{
Initialize();
}
/// <summary>
/// Sets private members to default values.
/// </summary>
private void Initialize()
{
m_buffers = null;
}
#endregion
#region Public Properties
/// <summary>
/// The buffers exposed by the memory
/// </summary>
[DataMember(Order = 1)]
public MemoryBufferInstanceCollection Buffers
{
get { return m_buffers; }
set { m_buffers = value; }
}
#endregion
#region Private Members
private MemoryBufferInstanceCollection m_buffers;
#endregion
}
/// <summary>
/// Stores the configuration for a memory buffer instance.
/// </summary>
[DataContract(Namespace = Namespaces.MemoryBuffer)]
public class MemoryBufferInstance
{
#region Constructors
/// <summary>
/// The default constructor.
/// </summary>
public MemoryBufferInstance()
{
Initialize();
}
/// <summary>
/// Initializes the object during deserialization.
/// </summary>
[OnDeserializing()]
private void Initialize(StreamingContext context)
{
Initialize();
}
/// <summary>
/// Sets private members to default values.
/// </summary>
private void Initialize()
{
m_name = null;
m_tagCount = 0;
m_dataType = null;
}
#endregion
#region Public Properties
/// <summary>
/// The browse name for the instance.
/// </summary>
[DataMember(Order = 1)]
public string Name
{
get { return m_name; }
set { m_name = value; }
}
/// <summary>
/// The number of tags in the buffer.
/// </summary>
[DataMember(Order = 2)]
public int TagCount
{
get { return m_tagCount; }
set { m_tagCount = value; }
}
/// <summary>
/// The data type of the tags in the buffer.
/// </summary>
[DataMember(Order = 3)]
public string DataType
{
get { return m_dataType; }
set { m_dataType = value; }
}
#endregion
#region Private Members
private string m_name;
private int m_tagCount;
private string m_dataType;
#endregion
}
#region MemoryBufferInstanceCollection Class
/// <summary>
/// A collection of MemoryBufferInstances.
/// </summary>
[CollectionDataContract(Name = "ListOfMemoryBufferInstance", Namespace = Namespaces.MemoryBuffer, ItemName = "MemoryBufferInstance")]
public partial class MemoryBufferInstanceCollection : List<MemoryBufferInstance>
{
}
#endregion
}
@@ -0,0 +1,5 @@
MemoryBufferType,1000,ObjectType
MemoryBufferType_StartAddress,1003,Variable
MemoryBufferType_SizeInBytes,1004,Variable
MemoryTagType,1018,VariableType
MemoryBuffers,1025,Object
1 MemoryBufferType 1000 ObjectType
2 MemoryBufferType_StartAddress 1003 Variable
3 MemoryBufferType_SizeInBytes 1004 Variable
4 MemoryTagType 1018 VariableType
5 MemoryBuffers 1025 Object
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8" ?>
<opc:ModelDesign
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:opc="http://opcfoundation.org/UA/ModelDesign.xsd"
xmlns:ua="http://opcfoundation.org/UA/"
xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd"
xmlns="http://samples.org/UA/MemoryBuffer"
TargetNamespace="http://samples.org/UA/MemoryBuffer"
>
<opc:Namespaces>
<opc:Namespace Name="OpcUa" Prefix="Opc.Ua" XmlNamespace="http://opcfoundation.org/UA/2008/02/Types.xsd">http://opcfoundation.org/UA/</opc:Namespace>
<opc:Namespace Name="MemoryBuffer" Prefix="MemoryBuffer">http://samples.org/UA/MemoryBuffer</opc:Namespace>
</opc:Namespaces>
<opc:VariableType SymbolicName="MemoryTagType" BaseType="ua:BaseDataVariableType">
</opc:VariableType>
<opc:ObjectType SymbolicName="MemoryBufferType" BaseType="ua:BaseObjectType">
<opc:Children>
<opc:Property SymbolicName="StartAddress" DataType="ua:UInt32">
<opc:DefaultValue>
<uax:UInt32>0</uax:UInt32>
</opc:DefaultValue>
</opc:Property>
<opc:Property SymbolicName="SizeInBytes" DataType="ua:UInt32">
<opc:DefaultValue>
<uax:UInt32>4096</uax:UInt32>
</opc:DefaultValue>
</opc:Property>
</opc:Children>
</opc:ObjectType>
<opc:Object SymbolicName="MemoryBuffers" TypeDefinition="ua:FolderType">
<opc:References>
<opc:Reference IsInverse="true">
<opc:ReferenceType>ua:Organizes</opc:ReferenceType>
<opc:TargetId>ua:ObjectsFolder</opc:TargetId>
</opc:Reference>
</opc:References>
</opc:Object>
</opc:ModelDesign>
@@ -0,0 +1,121 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Text;
using Opc.Ua;
using Opc.Ua.Server;
namespace MemoryBuffer
{
/// <summary>
/// Provides a basic monitored item implementation which does not support queuing.
/// </summary>
public class MemoryBufferMonitoredItem : MonitoredItem
{
/// <summary>
/// Initializes the object with its node type.
/// </summary>
public MemoryBufferMonitoredItem(
IServerInternal server,
INodeManager nodeManager,
object mangerHandle,
uint offset,
uint subscriptionId,
uint id,
ReadValueId itemToMonitor,
DiagnosticsMasks diagnosticsMasks,
TimestampsToReturn timestampsToReturn,
MonitoringMode monitoringMode,
uint clientHandle,
MonitoringFilter originalFilter,
MonitoringFilter filterToUse,
Opc.Ua.Range range,
double samplingInterval,
uint queueSize,
bool discardOldest,
double minimumSamplingInterval)
:
base(
server,
nodeManager,
mangerHandle,
subscriptionId,
id,
itemToMonitor,
diagnosticsMasks,
timestampsToReturn,
monitoringMode,
clientHandle,
originalFilter,
filterToUse,
range,
samplingInterval,
queueSize,
discardOldest,
minimumSamplingInterval)
{
m_offset = offset;
}
/// <summary>
/// Modifies the monitored item parameters,
/// </summary>
public ServiceResult Modify(
DiagnosticsMasks diagnosticsMasks,
TimestampsToReturn timestampsToReturn,
uint clientHandle,
double samplingInterval)
{
return base.ModifyAttributes(diagnosticsMasks,
timestampsToReturn,
clientHandle,
null,
null,
null,
samplingInterval,
0,
false);
}
/// <summary>
/// The offset in the memory buffer.
/// </summary>
public uint Offset
{
get
{
return m_offset;
}
}
private uint m_offset;
}
}
@@ -0,0 +1,550 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Xml;
using System.IO;
using System.Threading;
using Opc.Ua;
using Opc.Ua.Server;
using Opc.Ua.Sample;
using System.Reflection;
namespace MemoryBuffer
{
/// <summary>
/// The factory to create the node manager for memory buffers.
/// </summary>
public class MemoryBufferNodeManagerFactory : INodeManagerFactory
{
/// <inheritdoc/>
public INodeManager Create(IServerInternal server, ApplicationConfiguration configuration)
{
return new MemoryBufferNodeManager(server, configuration);
}
/// <inheritdoc/>
public StringCollection NamespacesUris
{
get
{
var nameSpaces = new StringCollection {
Namespaces.MemoryBuffer,
Namespaces.MemoryBuffer + "/Instance"
};
return nameSpaces;
}
}
}
/// <summary>
/// A node manager for a variety of memory buffers.
/// </summary>
public class MemoryBufferNodeManager : SampleNodeManager
{
#region Constructors
/// <summary>
/// Initializes the node manager.
/// </summary>
public MemoryBufferNodeManager(IServerInternal server, ApplicationConfiguration configuration)
:
base(server)
{
List<string> namespaceUris = new List<string>();
namespaceUris.Add(Namespaces.MemoryBuffer);
namespaceUris.Add(Namespaces.MemoryBuffer + "/Instance");
NamespaceUris = namespaceUris;
AddEncodeableNodeManagerTypes(typeof(MemoryBufferNodeManager).Assembly, typeof(MemoryBufferNodeManager).Namespace);
// get the configuration for the node manager.
m_configuration = configuration.ParseExtension<MemoryBufferConfiguration>();
// use suitable defaults if no configuration exists.
if (m_configuration == null)
{
m_configuration = new MemoryBufferConfiguration();
}
m_buffers = new Dictionary<string, MemoryBufferState>();
}
#endregion
#region INodeManager Members
/// <summary>
/// Does any initialization required before the address space can be used.
/// </summary>
/// <remarks>
/// The externalReferences is an out parameter that allows the node manager to link to nodes
/// in other node managers. For example, the 'Objects' node is managed by the CoreNodeManager and
/// should have a reference to the root folder node(s) exposed by this node manager.
/// </remarks>
public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
{
lock (Lock)
{
base.CreateAddressSpace(externalReferences);
// create the nodes from configuration.
ushort namespaceIndex = Server.NamespaceUris.GetIndexOrAppend(Namespaces.MemoryBuffer);
BaseInstanceState root = (BaseInstanceState)FindPredefinedNode(
new NodeId(Objects.MemoryBuffers, namespaceIndex),
typeof(BaseInstanceState));
// create the nodes from configuration.
namespaceIndex = Server.NamespaceUris.GetIndexOrAppend(Namespaces.MemoryBuffer + "/Instance");
if (m_configuration != null && m_configuration.Buffers != null)
{
for (int ii = 0; ii < m_configuration.Buffers.Count; ii++)
{
MemoryBufferInstance instance = m_configuration.Buffers[ii];
// create a new buffer.
MemoryBufferState bufferNode = new MemoryBufferState(SystemContext, instance);
// assign node ids.
bufferNode.Create(
SystemContext,
new NodeId(bufferNode.SymbolicName, namespaceIndex),
new QualifiedName(bufferNode.SymbolicName, namespaceIndex),
null,
true);
bufferNode.CreateBuffer(instance.DataType, instance.TagCount);
bufferNode.InitializeMonitoring(Server, this);
// save the buffers for easy look up later.
m_buffers[bufferNode.SymbolicName] = bufferNode;
// link to root.
root.AddChild(bufferNode);
}
}
}
}
/// <summary>
/// Loads a node set from a file or resource and addes them to the set of predefined nodes.
/// </summary>
protected override NodeStateCollection LoadPredefinedNodes(ISystemContext context)
{
NodeStateCollection predefinedNodes = new NodeStateCollection();
predefinedNodes.LoadFromBinaryResource(context, "Quickstarts.Servers.MemoryBuffer.MemoryBuffer.PredefinedNodes.uanodes", this.GetType().GetTypeInfo().Assembly, true);
return predefinedNodes;
}
/// <summary>
/// Frees any resources allocated for the address space.
/// </summary>
public override void DeleteAddressSpace()
{
lock (Lock)
{
base.DeleteAddressSpace();
}
}
/// <summary>
/// Returns a unique handle for the node.
/// </summary>
/// <remarks>
/// This must efficiently determine whether the node belongs to the node manager. If it does belong to
/// NodeManager it should return a handle that does not require the NodeId to be validated again when
/// the handle is passed into other methods such as 'Read' or 'Write'.
/// </remarks>
protected override object GetManagerHandle(ISystemContext context, NodeId nodeId, IDictionary<NodeId,NodeState> cache)
{
lock (Lock)
{
if (!IsNodeIdInNamespace(nodeId))
{
return null;
}
string id = nodeId.Identifier as string;
if (id != null)
{
// check for a reference to the buffer.
MemoryBufferState buffer = null;
if (m_buffers.TryGetValue(id, out buffer))
{
return buffer;
}
// tag ids have the syntax <bufferName>[<address>]
if (id[id.Length-1] != ']')
{
return null;
}
int index = id.IndexOf('[');
if (index == -1)
{
return null;
}
string bufferName = id.Substring(0, index);
// verify the buffer.
if (!m_buffers.TryGetValue(bufferName, out buffer))
{
return null;
}
// validate the address.
string offsetText = id.Substring(index+1, id.Length-index-2);
for (int ii = 0; ii < offsetText.Length; ii++)
{
if (!Char.IsDigit(offsetText[ii]))
{
return null;
}
}
// check range on offset.
uint offset = Convert.ToUInt32(offsetText);
if (offset >= buffer.SizeInBytes.Value)
{
return null;
}
// the tags contain all of the metadata required to support the UA
// operations and pointers to functions in the buffer object that
// allow the value to be accessed. These tags are ephemeral and are
// discarded after the operation completes. This design pattern allows
// the server to expose potentially millions of UA nodes without
// creating millions of objects that reside in memory.
return new MemoryTagState(buffer, offset);
}
return base.GetManagerHandle(context, nodeId, cache);
}
}
/// <summary>
/// Creates a new set of monitored items for a set of variables.
/// </summary>
/// <remarks>
/// This method only handles data change subscriptions. Event subscriptions are created by the SDK.
/// </remarks>
protected override ServiceResult CreateMonitoredItem(
ISystemContext context,
NodeState source,
uint subscriptionId,
double publishingInterval,
DiagnosticsMasks diagnosticsMasks,
TimestampsToReturn timestampsToReturn,
MonitoredItemCreateRequest itemToCreate,
ref long globalIdCounter,
out MonitoringFilterResult filterError,
out IMonitoredItem monitoredItem)
{
filterError = null;
monitoredItem = null;
MemoryTagState tag = source as MemoryTagState;
// use default behavior for non-tag sources.
if (tag == null)
{
return base.CreateMonitoredItem(
context,
source,
subscriptionId,
publishingInterval,
diagnosticsMasks,
timestampsToReturn,
itemToCreate,
ref globalIdCounter,
out filterError,
out monitoredItem);
}
// validate parameters.
MonitoringParameters parameters = itemToCreate.RequestedParameters;
// no filters supported at this time.
MonitoringFilter filter = (MonitoringFilter)ExtensionObject.ToEncodeable(parameters.Filter);
if (filter != null)
{
return StatusCodes.BadFilterNotAllowed;
}
// index range not supported.
if (itemToCreate.ItemToMonitor.ParsedIndexRange != NumericRange.Empty)
{
return StatusCodes.BadIndexRangeInvalid;
}
// data encoding not supported.
if (!QualifiedName.IsNull(itemToCreate.ItemToMonitor.DataEncoding))
{
return StatusCodes.BadDataEncodingUnsupported;
}
// read initial value.
DataValue initialValue = new DataValue();
initialValue.Value = null;
initialValue.ServerTimestamp = DateTime.UtcNow;
initialValue.SourceTimestamp = DateTime.MinValue;
initialValue.StatusCode = StatusCodes.Good;
ServiceResult error = source.ReadAttribute(
context,
itemToCreate.ItemToMonitor.AttributeId,
itemToCreate.ItemToMonitor.ParsedIndexRange,
itemToCreate.ItemToMonitor.DataEncoding,
initialValue);
if (ServiceResult.IsBad(error))
{
return error;
}
// get the monitored node for the containing buffer.
MemoryBufferState buffer = tag.Parent as MemoryBufferState;
if (buffer == null)
{
return StatusCodes.BadInternalError;
}
// create a globally unique identifier.
uint monitoredItemId = Utils.IncrementIdentifier(ref globalIdCounter);
// determine the sampling interval.
double samplingInterval = itemToCreate.RequestedParameters.SamplingInterval;
if (samplingInterval < 0)
{
samplingInterval = publishingInterval;
}
// create the item.
MemoryBufferMonitoredItem datachangeItem = buffer.CreateDataChangeItem(
context as ServerSystemContext,
tag,
subscriptionId,
monitoredItemId,
itemToCreate.ItemToMonitor,
diagnosticsMasks,
timestampsToReturn,
itemToCreate.MonitoringMode,
itemToCreate.RequestedParameters.ClientHandle,
samplingInterval);
// report the initial value.
datachangeItem.QueueValue(initialValue, null);
// update monitored item list.
monitoredItem = datachangeItem;
return ServiceResult.Good;
}
/// <summary>
/// Modifies the parameters for a monitored item.
/// </summary>
protected override ServiceResult ModifyMonitoredItem(
ISystemContext context,
DiagnosticsMasks diagnosticsMasks,
TimestampsToReturn timestampsToReturn,
IMonitoredItem monitoredItem,
MonitoredItemModifyRequest itemToModify,
out MonitoringFilterResult filterError)
{
filterError = null;
// check for valid handle.
MemoryBufferState buffer = monitoredItem.ManagerHandle as MemoryBufferState;
if (buffer == null)
{
return base.ModifyMonitoredItem(
context,
diagnosticsMasks,
timestampsToReturn,
monitoredItem,
itemToModify,
out filterError);
}
// owned by this node manager.
itemToModify.Processed = true;
// get the monitored item.
MemoryBufferMonitoredItem datachangeItem = monitoredItem as MemoryBufferMonitoredItem;
if (datachangeItem == null)
{
return StatusCodes.BadMonitoredItemIdInvalid;
}
// validate parameters.
MonitoringParameters parameters = itemToModify.RequestedParameters;
// no filters supported at this time.
MonitoringFilter filter = (MonitoringFilter)ExtensionObject.ToEncodeable(parameters.Filter);
if (filter != null)
{
return StatusCodes.BadFilterNotAllowed;
}
// modify the monitored item parameters.
ServiceResult error = datachangeItem.Modify(
diagnosticsMasks,
timestampsToReturn,
itemToModify.RequestedParameters.ClientHandle,
itemToModify.RequestedParameters.SamplingInterval);
return ServiceResult.Good;
}
/// <summary>
/// Deletes a monitored item.
/// </summary>
protected override ServiceResult DeleteMonitoredItem(
ISystemContext context,
IMonitoredItem monitoredItem,
out bool processed)
{
processed = false;
// check for valid handle.
MemoryBufferState buffer = monitoredItem.ManagerHandle as MemoryBufferState;
if (buffer == null)
{
return base.DeleteMonitoredItem(
context,
monitoredItem,
out processed);
}
// owned by this node manager.
processed = true;
// get the monitored item.
MemoryBufferMonitoredItem datachangeItem = monitoredItem as MemoryBufferMonitoredItem;
if (datachangeItem == null)
{
return StatusCodes.BadMonitoredItemIdInvalid;
}
// delete the item.
buffer.DeleteItem(datachangeItem);
return ServiceResult.Good;
}
/// <summary>
/// Changes the monitoring mode for an item.
/// </summary>
protected override ServiceResult SetMonitoringMode(
ISystemContext context,
IMonitoredItem monitoredItem,
MonitoringMode monitoringMode,
out bool processed)
{
processed = false;
// check for valid handle.
MemoryBufferState buffer = monitoredItem.ManagerHandle as MemoryBufferState;
if (buffer == null)
{
return base.SetMonitoringMode(
context,
monitoredItem,
monitoringMode,
out processed);
}
// owned by this node manager.
processed = true;
// get the monitored item.
MemoryBufferMonitoredItem datachangeItem = monitoredItem as MemoryBufferMonitoredItem;
if (datachangeItem == null)
{
return StatusCodes.BadMonitoredItemIdInvalid;
}
// delete the item.
MonitoringMode previousMode = datachangeItem.SetMonitoringMode(monitoringMode);
// need to provide an immediate update after enabling.
if (previousMode == MonitoringMode.Disabled && monitoringMode != MonitoringMode.Disabled)
{
DataValue initialValue = new DataValue();
initialValue.Value = null;
initialValue.ServerTimestamp = DateTime.UtcNow;
initialValue.SourceTimestamp = DateTime.MinValue;
initialValue.StatusCode = StatusCodes.Good;
MemoryTagState tag = new MemoryTagState(buffer, datachangeItem.Offset);
ServiceResult error = tag.ReadAttribute(
context,
datachangeItem.AttributeId,
NumericRange.Empty,
null,
initialValue);
datachangeItem.QueueValue(initialValue, error);
}
return ServiceResult.Good;
}
#endregion
#region Private Fields
private MemoryBufferConfiguration m_configuration;
private Dictionary<string, MemoryBufferState> m_buffers;
#endregion
}
}
@@ -0,0 +1,696 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Threading;
using Opc.Ua;
using Opc.Ua.Server;
using System.Diagnostics;
namespace MemoryBuffer
{
public partial class MemoryBufferState
{
#region Constructors
/// <summary>
/// Initializes the buffer from the configuration.
/// </summary>
public MemoryBufferState(ISystemContext context, MemoryBufferInstance configuration) : base(null)
{
Initialize(context);
string dataType = "UInt32";
string name = dataType;
int count = 10;
if (configuration != null)
{
count = configuration.TagCount;
if (!String.IsNullOrEmpty(configuration.DataType))
{
dataType = configuration.DataType;
}
if (!String.IsNullOrEmpty(configuration.Name))
{
name = dataType;
}
}
this.SymbolicName = name;
BuiltInType elementType = BuiltInType.UInt32;
switch (dataType)
{
case "Double":
{
elementType = BuiltInType.Double;
break;
}
}
CreateBuffer(elementType, count);
}
#endregion
#region Public Properties
/// <summary>
/// The server that the buffer belongs to.
/// </summary>
public IServerInternal Server
{
get { return m_server; }
}
/// <summary>
/// The node manager that the buffer belongs to.
/// </summary>
public INodeManager NodeManager
{
get { return m_nodeManager; }
}
/// <summary>
/// The built-in type for the values stored in the buffer.
/// </summary>
public BuiltInType ElementType
{
get { return m_elementType; }
}
/// <summary>
/// The size of each element in the buffer.
/// </summary>
public uint ElementSize
{
get { return (uint)m_elementSize; }
}
/// <summary>
/// The rate at which the buffer is scanned.
/// </summary>
public int MaximumScanRate
{
get { return m_maximumScanRate; }
}
#endregion
#region Public Methods
/// <summary>
/// Initializes the buffer with enough space to hold the specified number of elements.
/// </summary>
/// <param name="elementName">The type of element.</param>
/// <param name="noOfElements">The number of elements.</param>
public void CreateBuffer(string elementName, int noOfElements)
{
if (String.IsNullOrEmpty(elementName))
{
elementName = "UInt32";
}
BuiltInType elementType = BuiltInType.UInt32;
switch (elementName)
{
case "Double":
{
elementType = BuiltInType.Double;
break;
}
}
CreateBuffer(elementType, noOfElements);
}
/// <summary>
/// Initializes the buffer with enough space to hold the specified number of elements.
/// </summary>
/// <param name="elementType">The type of element.</param>
/// <param name="noOfElements">The number of elements.</param>
public void CreateBuffer(BuiltInType elementType, int noOfElements)
{
lock (m_dataLock)
{
m_elementType = elementType;
m_elementSize = 1;
switch (m_elementType)
{
case BuiltInType.UInt32:
{
m_elementSize = 4;
break;
}
case BuiltInType.Double:
{
m_elementSize = 8;
break;
}
}
m_lastScanTime = DateTime.UtcNow;
m_maximumScanRate = 1000;
m_buffer = new byte[m_elementSize * noOfElements];
SizeInBytes.Value = (uint)m_buffer.Length;
}
}
/// <summary>
/// Creates an object which can browser the tags in the buffer.
/// </summary>
public override INodeBrowser CreateBrowser(
ISystemContext context,
ViewDescription view,
NodeId referenceType,
bool includeSubtypes,
BrowseDirection browseDirection,
QualifiedName browseName,
IEnumerable<IReference> additionalReferences,
bool internalOnly)
{
NodeBrowser browser = new MemoryBufferBrowser(
context,
view,
referenceType,
includeSubtypes,
browseDirection,
browseName,
additionalReferences,
internalOnly,
this);
PopulateBrowser(context, browser);
return browser;
}
/// <summary>
/// Handles the read operation for an invidual tag.
/// </summary>
public ServiceResult ReadTagValue(
ISystemContext context,
NodeState node,
NumericRange indexRange,
QualifiedName dataEncoding,
ref object value,
ref StatusCode statusCode,
ref DateTime timestamp)
{
MemoryTagState tag = node as MemoryTagState;
if (tag == null)
{
return StatusCodes.BadNodeIdUnknown;
}
if (NumericRange.Empty != indexRange)
{
return StatusCodes.BadIndexRangeInvalid;
}
if (!QualifiedName.IsNull(dataEncoding))
{
return StatusCodes.BadDataEncodingUnsupported;
}
int offset = (int)tag.Offset;
lock (m_dataLock)
{
if (offset < 0 || offset >= m_buffer.Length)
{
return StatusCodes.BadNodeIdUnknown;
}
if (m_buffer == null)
{
return StatusCodes.BadOutOfService;
}
value = GetValueAtOffset(offset).Value;
}
statusCode = StatusCodes.Good;
timestamp = m_lastScanTime;
return ServiceResult.Good;
}
/// <summary>
/// Handles a write operation for an individual tag.
/// </summary>
public ServiceResult WriteTagValue(
ISystemContext context,
NodeState node,
NumericRange indexRange,
QualifiedName dataEncoding,
ref object value,
ref StatusCode statusCode,
ref DateTime timestamp)
{
MemoryTagState tag = node as MemoryTagState;
if (tag == null)
{
return StatusCodes.BadNodeIdUnknown;
}
if (NumericRange.Empty != indexRange)
{
return StatusCodes.BadIndexRangeInvalid;
}
if (!QualifiedName.IsNull(dataEncoding))
{
return StatusCodes.BadDataEncodingUnsupported;
}
if (statusCode != StatusCodes.Good)
{
return StatusCodes.BadWriteNotSupported;
}
if (timestamp != DateTime.MinValue)
{
return StatusCodes.BadWriteNotSupported;
}
bool changed = false;
int offset = (int)tag.Offset;
lock (m_dataLock)
{
if (offset < 0 || offset >= m_buffer.Length)
{
return StatusCodes.BadNodeIdUnknown;
}
if (m_buffer == null)
{
return StatusCodes.BadOutOfService;
}
byte[] bytes = null;
switch (m_elementType)
{
case BuiltInType.UInt32:
{
uint? valueToWrite = value as uint?;
if (valueToWrite == null)
{
return StatusCodes.BadTypeMismatch;
}
bytes = BitConverter.GetBytes(valueToWrite.Value);
break;
}
case BuiltInType.Double:
{
double? valueToWrite = value as double?;
if (valueToWrite == null)
{
return StatusCodes.BadTypeMismatch;
}
bytes = BitConverter.GetBytes(valueToWrite.Value);
break;
}
default:
{
return StatusCodes.BadNodeIdUnknown;
}
}
for (int ii = 0; ii < bytes.Length; ii++)
{
if (!changed)
{
if (m_buffer[offset + ii] != bytes[ii])
{
changed = true;
}
}
m_buffer[offset + ii] = bytes[ii];
}
}
if (changed)
{
OnBufferChanged(offset);
}
return ServiceResult.Good;
}
/// <summary>
/// Returns the value at the specified offset.
/// </summary>
public Variant GetValueAtOffset(int offset)
{
lock (m_dataLock)
{
if (offset < 0 || offset >= m_buffer.Length)
{
return Variant.Null;
}
if (m_buffer == null)
{
return Variant.Null;
}
switch (m_elementType)
{
case BuiltInType.UInt32:
{
return new Variant(BitConverter.ToUInt32(m_buffer, offset));
}
case BuiltInType.Double:
{
return new Variant(BitConverter.ToDouble(m_buffer, offset));
}
}
return Variant.Null;
}
}
#endregion
#region Monitoring Support Functions
/// <summary>
/// Initializes the instance with the context for the node being monitored.
/// </summary>
public void InitializeMonitoring(
IServerInternal server,
INodeManager nodeManager)
{
lock (m_dataLock)
{
m_server = server;
m_nodeManager = nodeManager;
m_nonValueMonitoredItems = new Dictionary<uint, MemoryBufferMonitoredItem>();
}
}
/// <summary>
/// Creates a new data change monitored item.
/// </summary>
public MemoryBufferMonitoredItem CreateDataChangeItem(
ServerSystemContext context,
MemoryTagState tag,
uint subscriptionId,
uint monitoredItemId,
ReadValueId itemToMonitor,
DiagnosticsMasks diagnosticsMasks,
TimestampsToReturn timestampsToReturn,
MonitoringMode monitoringMode,
uint clientHandle,
double samplingInterval)
/*
ISystemContext context,
MemoryTagState tag,
uint monitoredItemId,
uint attributeId,
DiagnosticsMasks diagnosticsMasks,
TimestampsToReturn timestampsToReturn,
MonitoringMode monitoringMode,
uint clientHandle,
double samplingInterval)*/
{
lock (m_dataLock)
{
MemoryBufferMonitoredItem monitoredItem = new MemoryBufferMonitoredItem(
m_server,
m_nodeManager,
this,
tag.Offset,
0,
monitoredItemId,
itemToMonitor,
diagnosticsMasks,
timestampsToReturn,
monitoringMode,
clientHandle,
null,
null,
null,
samplingInterval,
0,
false,
0);
/*
MemoryBufferMonitoredItem monitoredItem = new MemoryBufferMonitoredItem(
this,
monitoredItemId,
tag.Offset,
attributeId,
diagnosticsMasks,
timestampsToReturn,
monitoringMode,
clientHandle,
samplingInterval);
*/
if (itemToMonitor.AttributeId != Attributes.Value)
{
m_nonValueMonitoredItems.Add(monitoredItem.Id, monitoredItem);
return monitoredItem;
}
int elementCount = (int)(SizeInBytes.Value / ElementSize);
if (m_monitoringTable == null)
{
m_monitoringTable = new MemoryBufferMonitoredItem[elementCount][];
m_scanTimer = new Timer(DoScan, null, 100, 100);
}
int elementOffet = (int)(tag.Offset / ElementSize);
MemoryBufferMonitoredItem[] monitoredItems = m_monitoringTable[elementOffet];
if (monitoredItems == null)
{
monitoredItems = new MemoryBufferMonitoredItem[1];
}
else
{
monitoredItems = new MemoryBufferMonitoredItem[monitoredItems.Length + 1];
m_monitoringTable[elementOffet].CopyTo(monitoredItems, 0);
}
monitoredItems[monitoredItems.Length - 1] = monitoredItem;
m_monitoringTable[elementOffet] = monitoredItems;
m_itemCount++;
return monitoredItem;
}
}
/// <summary>
/// Scans the buffer and updates every other element.
/// </summary>
void DoScan(object state)
{
DateTime start1 = DateTime.UtcNow;
lock (m_dataLock)
{
for (int ii = 0; ii < m_buffer.Length; ii += m_elementSize)
{
m_buffer[ii]++;
// notify any monitored items that the value has changed.
OnBufferChanged(ii);
}
m_lastScanTime = DateTime.UtcNow;
}
DateTime end1 = DateTime.UtcNow;
double delta1 = ((double)(end1.Ticks - start1.Ticks)) / TimeSpan.TicksPerMillisecond;
if (delta1 > 100)
{
Utils.LogWarning("{0} SAMPLING DELAY ({1}ms)", nameof(MemoryBufferState), delta1);
}
}
/// <summary>
/// Deletes the monitored item.
/// </summary>
public void DeleteItem(MemoryBufferMonitoredItem monitoredItem)
{
lock (m_dataLock)
{
if (monitoredItem.AttributeId != Attributes.Value)
{
m_nonValueMonitoredItems.Remove(monitoredItem.Id);
return;
}
if (m_monitoringTable != null)
{
int elementOffet = (int)(monitoredItem.Offset / ElementSize);
MemoryBufferMonitoredItem[] monitoredItems = m_monitoringTable[elementOffet];
if (monitoredItems != null)
{
int index = -1;
for (int ii = 0; ii < monitoredItems.Length; ii++)
{
if (Object.ReferenceEquals(monitoredItems[ii], monitoredItem))
{
index = ii;
break;
}
}
if (index >= 0)
{
m_itemCount--;
if (monitoredItems.Length == 1)
{
monitoredItems = null;
}
else
{
monitoredItems = new MemoryBufferMonitoredItem[monitoredItems.Length - 1];
Array.Copy(m_monitoringTable[elementOffet], 0, monitoredItems, 0, index);
Array.Copy(m_monitoringTable[elementOffet], index + 1, monitoredItems, index, monitoredItems.Length - index);
}
m_monitoringTable[elementOffet] = monitoredItems;
}
}
}
}
}
/// <summary>
/// Handles change events raised by the node.
/// </summary>
public void OnBufferChanged(int offset)
{
lock (m_dataLock)
{
if (m_monitoringTable != null)
{
int elementOffet = (int)(offset / ElementSize);
MemoryBufferMonitoredItem[] monitoredItems = m_monitoringTable[elementOffet];
if (monitoredItems != null)
{
DataValue value = new DataValue();
value.WrappedValue = GetValueAtOffset(offset);
value.StatusCode = StatusCodes.Good;
value.ServerTimestamp = DateTime.UtcNow;
value.SourceTimestamp = m_lastScanTime;
for (int ii = 0; ii < monitoredItems.Length; ii++)
{
monitoredItems[ii].QueueValue(value, null);
m_updateCount++;
}
}
}
}
}
void ScanTimer_Tick(object sender, EventArgs e)
{
DoScan(null);
}
void PublishTimer_Tick(object sender, EventArgs e)
{
DateTime start1 = DateTime.UtcNow;
lock (m_dataLock)
{
if (m_itemCount > 0 && m_updateCount < m_itemCount)
{
Utils.LogInfo("{0:HH:mm:ss.fff} MEMORYBUFFER Reported {1}/{2} items ***.", DateTime.Now, m_updateCount, m_itemCount);
}
m_updateCount = 0;
}
DateTime end1 = DateTime.UtcNow;
double delta1 = ((double)(end1.Ticks - start1.Ticks)) / TimeSpan.TicksPerMillisecond;
if (delta1 > 100)
{
Utils.LogInfo("{0} ****** PUBLISH DELAY ({1}ms) ******", nameof(MemoryBufferState), delta1);
}
}
#endregion
#region Private Fields
private object m_dataLock = new object();
private IServerInternal m_server;
private INodeManager m_nodeManager;
private MemoryBufferMonitoredItem[][] m_monitoringTable;
private Dictionary<uint, MemoryBufferMonitoredItem> m_nonValueMonitoredItems;
private BuiltInType m_elementType;
private int m_elementSize;
private DateTime m_lastScanTime;
private int m_maximumScanRate;
private byte[] m_buffer;
private Timer m_scanTimer;
private int m_updateCount;
private int m_itemCount;
#endregion
}
}
@@ -0,0 +1,93 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Threading;
using Opc.Ua;
using Opc.Ua.Server;
namespace MemoryBuffer
{
public partial class MemoryTagState
{
#region Constructors
/// <summary>
/// Initializes a memory tag for a buffer.
/// </summary>
/// <param name="parent">The buffer that owns the tag.</param>
/// <param name="offet">The offset of the tag address in the memory buffer.</param>
public MemoryTagState(MemoryBufferState parent, uint offet) : base(parent)
{
// these objects are created an discarded during each operation.
// the metadata is derived from the parameters passed to constructors.
NodeId = new NodeId(Utils.Format("{0}[{1}]", parent.SymbolicName, offet), parent.NodeId.NamespaceIndex);
BrowseName = new QualifiedName(Utils.Format("{1:X8}", parent.SymbolicName, offet), parent.TypeDefinitionId.NamespaceIndex);
DisplayName = BrowseName.Name;
Description = null;
WriteMask = AttributeWriteMask.None;
UserWriteMask = AttributeWriteMask.None;
ReferenceTypeId = Opc.Ua.ReferenceTypeIds.HasComponent;
TypeDefinitionId = new NodeId(VariableTypes.MemoryTagType, parent.TypeDefinitionId.NamespaceIndex);
ModellingRuleId = null;
NumericId = offet;
DataType = new NodeId((uint)parent.ElementType);
ValueRank = ValueRanks.Scalar;
ArrayDimensions = null;
AccessLevel = AccessLevels.CurrentReadOrWrite;
UserAccessLevel = AccessLevels.CurrentReadOrWrite;
MinimumSamplingInterval = parent.MaximumScanRate;
Historizing = false;
// re-direct read and write operations to the parent.
OnReadValue = parent.ReadTagValue;
OnWriteValue = parent.WriteTagValue;
m_offset = offet;
}
#endregion
#region Public Properties
/// <summary>
/// The offset of the tag address in the memory buffer.
/// </summary>
public uint Offset
{
get { return m_offset; }
}
#endregion
#region Private Fields
private uint m_offset;
#endregion
}
}
@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DefineConstants>$(DefineConstants);NET_STANDARD</DefineConstants>
<AssemblyName>Quickstarts.Servers</AssemblyName>
<TargetFrameworks>$(LibTargetFrameworks)</TargetFrameworks>
<PackageId>OPCFoundation.NetStandard.Opc.Ua.Quickstarts.Servers</PackageId>
<RootNamespace>Quickstarts.Servers</RootNamespace>
<Description>OPC UA Quickstarts Servers Class Library</Description>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<NoWarn>CS1591;CS1573;RCS1139</NoWarn>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<PackageId>$(PackageId).Debug</PackageId>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="TestData\TestData.PredefinedNodes.uanodes;MemoryBuffer\MemoryBuffer.PredefinedNodes.uanodes;Boiler\Boiler.PredefinedNodes.uanodes" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Properties\**" />
<EmbeddedResource Remove="Properties\**" />
<None Remove="Properties\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Server\Opc.Ua.Server.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(SignAssembly)' == 'true'">
<DefineConstants>$(DefineConstants);SIGNASSEMBLY</DefineConstants>
</PropertyGroup>
<Target Name="GetPackagingOutputs" />
</Project>
@@ -27,20 +27,16 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.Text;
namespace Quickstarts.ReferenceServer
{
/// <summary>
/// Defines constants for namespaces used by the application.
/// Defines constants for namespaces used by the servers.
/// </summary>
public static partial class Namespaces
{
/// <summary>
/// The namespace for the nodes provided by the server.
/// The namespace for the nodes provided by the reference server.
/// </summary>
public const string ReferenceApplications = "http://opcfoundation.org/Quickstarts/ReferenceApplications";
public const string ReferenceServer = "http://opcfoundation.org/Quickstarts/ReferenceServer";
}
}
@@ -28,6 +28,7 @@
* ======================================================================*/
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Xml;
using System.Threading;
@@ -35,6 +36,7 @@ using System.Numerics;
using Opc.Ua;
using Opc.Ua.Server;
using Range = Opc.Ua.Range;
using Opc.Ua.Test;
namespace Quickstarts.ReferenceServer
{
@@ -48,7 +50,7 @@ namespace Quickstarts.ReferenceServer
/// Initializes the node manager.
/// </summary>
public ReferenceNodeManager(IServerInternal server, ApplicationConfiguration configuration)
: base(server, configuration, Namespaces.ReferenceApplications)
: base(server, configuration, Namespaces.ReferenceServer)
{
SystemContext.NodeIdFactory = this;
@@ -75,6 +77,7 @@ namespace Quickstarts.ReferenceServer
{
// TBD
}
base.Dispose(disposing);
}
#endregion
@@ -193,6 +196,7 @@ namespace Quickstarts.ReferenceServer
try
{
#region Scalar_Static
ResetRandomGenerator(1);
FolderState scalarFolder = CreateFolder(root, "Scalar", "Scalar");
BaseDataVariableState scalarInstructions = CreateVariable(scalarFolder, "Scalar_Instructions", "Scalar_Instructions", DataTypeIds.String, ValueRanks.Scalar);
scalarInstructions.Value = "A library of Read/Write Variables of all supported data-types.";
@@ -206,7 +210,7 @@ namespace Quickstarts.ReferenceServer
variables.Add(CreateVariable(staticFolder, scalarStatic + "DateTime", "DateTime", DataTypeIds.DateTime, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Double", "Double", DataTypeIds.Double, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Duration", "Duration", DataTypeIds.Duration, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Float", "Float", DataTypeIds.Float, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Float", "Float", DataTypeIds.Float, ValueRanks.Scalar).MinimumSamplingInterval(100));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Guid", "Guid", DataTypeIds.Guid, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Int16", "Int16", DataTypeIds.Int16, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Int32", "Int32", DataTypeIds.Int32, ValueRanks.Scalar));
@@ -219,26 +223,27 @@ namespace Quickstarts.ReferenceServer
variables.Add(CreateVariable(staticFolder, scalarStatic + "QualifiedName", "QualifiedName", DataTypeIds.QualifiedName, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "SByte", "SByte", DataTypeIds.SByte, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "String", "String", DataTypeIds.String, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Time", "Time", DataTypeIds.Time, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "UInteger", "UInteger", DataTypeIds.UInteger, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "UtcTime", "UtcTime", DataTypeIds.UtcTime, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "Variant", "Variant", BuiltInType.Variant, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "XmlElement", "XmlElement", DataTypeIds.XmlElement, ValueRanks.Scalar));
variables.Add(CreateVariable(staticFolder, scalarStatic + "XmlElement", "XmlElement", DataTypeIds.XmlElement, ValueRanks.Scalar).MinimumSamplingInterval(1000));
BaseDataVariableState decimalVariable = CreateVariable(staticFolder, scalarStatic + "Decimal", "Decimal", DataTypeIds.DecimalDataType, ValueRanks.Scalar);
// Set an arbitrary precision decimal value.
BigInteger largeInteger = BigInteger.Parse("1234567890123546789012345678901234567890123456789012345");
DecimalDataType decimalValue = new DecimalDataType();
decimalValue.Scale = 100;
decimalValue.Value = largeInteger.ToByteArray();
DecimalDataType decimalValue = new DecimalDataType {
Scale = 100,
Value = largeInteger.ToByteArray()
};
decimalVariable.Value = decimalValue;
variables.Add(decimalVariable);
#endregion
#region Scalar_Static_Arrays
ResetRandomGenerator(2);
FolderState arraysFolder = CreateFolder(staticFolder, "Scalar_Static_Arrays", "Arrays");
const string staticArrays = "Scalar_Static_Arrays_";
@@ -293,7 +298,6 @@ namespace Quickstarts.ReferenceServer
"龙_ 绵羊 大象 芒果; 猫'" };
variables.Add(stringArrayVar);
variables.Add(CreateVariable(arraysFolder, staticArrays + "Time", "Time", DataTypeIds.Time, ValueRanks.OneDimension));
variables.Add(CreateVariable(arraysFolder, staticArrays + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.OneDimension));
variables.Add(CreateVariable(arraysFolder, staticArrays + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.OneDimension));
variables.Add(CreateVariable(arraysFolder, staticArrays + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.OneDimension));
@@ -304,6 +308,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region Scalar_Static_Arrays2D
ResetRandomGenerator(3);
FolderState arrays2DFolder = CreateFolder(staticFolder, "Scalar_Static_Arrays2D", "Arrays2D");
const string staticArrays2D = "Scalar_Static_Arrays2D_";
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "Boolean", "Boolean", DataTypeIds.Boolean, ValueRanks.TwoDimensions));
@@ -319,55 +324,55 @@ namespace Quickstarts.ReferenceServer
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "Int64", "Int64", DataTypeIds.Int64, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "Integer", "Integer", DataTypeIds.Integer, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "LocaleId", "LocaleId", DataTypeIds.LocaleId, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "LocalizedText", "LocalizedText", DataTypeIds.LocalizedText, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "LocalizedText", "LocalizedText", DataTypeIds.LocalizedText, ValueRanks.TwoDimensions).MinimumSamplingInterval(1000));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "NodeId", "NodeId", DataTypeIds.NodeId, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "Number", "Number", DataTypeIds.Number, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "QualifiedName", "QualifiedName", DataTypeIds.QualifiedName, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "SByte", "SByte", DataTypeIds.SByte, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "String", "String", DataTypeIds.String, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "Time", "Time", DataTypeIds.Time, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "UInteger", "UInteger", DataTypeIds.UInteger, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "UtcTime", "UtcTime", DataTypeIds.UtcTime, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "Variant", "Variant", BuiltInType.Variant, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "XmlElement", "XmlElement", DataTypeIds.XmlElement, ValueRanks.TwoDimensions));
variables.Add(CreateVariable(arrays2DFolder, staticArrays2D + "XmlElement", "XmlElement", DataTypeIds.XmlElement, ValueRanks.TwoDimensions).MinimumSamplingInterval(1000));
#endregion
#region Scalar_Static_ArrayDynamic
FolderState arrayDymnamicFolder = CreateFolder(staticFolder, "Scalar_Static_ArrayDymamic", "ArrayDymamic");
ResetRandomGenerator(4);
FolderState arrayDynamicFolder = CreateFolder(staticFolder, "Scalar_Static_ArrayDynamic", "ArrayDynamic");
const string staticArraysDynamic = "Scalar_Static_ArrayDynamic_";
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Boolean", "Boolean", DataTypeIds.Boolean, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Byte", "Byte", DataTypeIds.Byte, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "ByteString", "ByteString", DataTypeIds.ByteString, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "DateTime", "DateTime", DataTypeIds.DateTime, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Double", "Double", DataTypeIds.Double, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Duration", "Duration", DataTypeIds.Duration, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Float", "Float", DataTypeIds.Float, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Guid", "Guid", DataTypeIds.Guid, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Int16", "Int16", DataTypeIds.Int16, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Int32", "Int32", DataTypeIds.Int32, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Int64", "Int64", DataTypeIds.Int64, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Integer", "Integer", DataTypeIds.Integer, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "LocaleId", "LocaleId", DataTypeIds.LocaleId, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "LocalizedText", "LocalizedText", DataTypeIds.LocalizedText, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "NodeId", "NodeId", DataTypeIds.NodeId, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Number", "Number", DataTypeIds.Number, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "QualifiedName", "QualifiedName", DataTypeIds.QualifiedName, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "SByte", "SByte", DataTypeIds.SByte, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "String", "String", DataTypeIds.String, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Time", "Time", DataTypeIds.Time, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "UInteger", "UInteger", DataTypeIds.UInteger, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "UtcTime", "UtcTime", DataTypeIds.UtcTime, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "Variant", "Variant", BuiltInType.Variant, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDymnamicFolder, staticArraysDynamic + "XmlElement", "XmlElement", DataTypeIds.XmlElement, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Boolean", "Boolean", DataTypeIds.Boolean, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Byte", "Byte", DataTypeIds.Byte, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "ByteString", "ByteString", DataTypeIds.ByteString, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "DateTime", "DateTime", DataTypeIds.DateTime, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Double", "Double", DataTypeIds.Double, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Duration", "Duration", DataTypeIds.Duration, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Float", "Float", DataTypeIds.Float, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Guid", "Guid", DataTypeIds.Guid, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Int16", "Int16", DataTypeIds.Int16, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Int32", "Int32", DataTypeIds.Int32, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Int64", "Int64", DataTypeIds.Int64, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Integer", "Integer", DataTypeIds.Integer, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "LocaleId", "LocaleId", DataTypeIds.LocaleId, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "LocalizedText", "LocalizedText", DataTypeIds.LocalizedText, ValueRanks.OneOrMoreDimensions).MinimumSamplingInterval(1000));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "NodeId", "NodeId", DataTypeIds.NodeId, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Number", "Number", DataTypeIds.Number, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "QualifiedName", "QualifiedName", DataTypeIds.QualifiedName, ValueRanks.OneOrMoreDimensions).MinimumSamplingInterval(1000));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "SByte", "SByte", DataTypeIds.SByte, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "String", "String", DataTypeIds.String, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "UInteger", "UInteger", DataTypeIds.UInteger, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "UtcTime", "UtcTime", DataTypeIds.UtcTime, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "Variant", "Variant", BuiltInType.Variant, ValueRanks.OneOrMoreDimensions));
variables.Add(CreateVariable(arrayDynamicFolder, staticArraysDynamic + "XmlElement", "XmlElement", DataTypeIds.XmlElement, ValueRanks.OneOrMoreDimensions).MinimumSamplingInterval(1000));
#endregion
#region Scalar_Static_Mass
ResetRandomGenerator(5);
// create 100 instances of each static scalar type
FolderState massFolder = CreateFolder(staticFolder, "Scalar_Static_Mass", "Mass");
const string staticMass = "Scalar_Static_Mass_";
@@ -388,7 +393,6 @@ namespace Quickstarts.ReferenceServer
variables.AddRange(CreateVariables(massFolder, staticMass + "Number", "Number", DataTypeIds.Number, ValueRanks.Scalar, 100));
variables.AddRange(CreateVariables(massFolder, staticMass + "SByte", "SByte", DataTypeIds.SByte, ValueRanks.Scalar, 100));
variables.AddRange(CreateVariables(massFolder, staticMass + "String", "String", DataTypeIds.String, ValueRanks.Scalar, 100));
variables.AddRange(CreateVariables(massFolder, staticMass + "Time", "Time", DataTypeIds.Time, ValueRanks.Scalar, 100));
variables.AddRange(CreateVariables(massFolder, staticMass + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.Scalar, 100));
variables.AddRange(CreateVariables(massFolder, staticMass + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.Scalar, 100));
variables.AddRange(CreateVariables(massFolder, staticMass + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.Scalar, 100));
@@ -399,6 +403,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region Scalar_Simulation
ResetRandomGenerator(6);
FolderState simulationFolder = CreateFolder(scalarFolder, "Scalar_Simulation", "Simulation");
const string scalarSimulation = "Scalar_Simulation_";
CreateDynamicVariable(simulationFolder, scalarSimulation + "Boolean", "Boolean", DataTypeIds.Boolean, ValueRanks.Scalar);
@@ -420,7 +425,6 @@ namespace Quickstarts.ReferenceServer
CreateDynamicVariable(simulationFolder, scalarSimulation + "QualifiedName", "QualifiedName", DataTypeIds.QualifiedName, ValueRanks.Scalar);
CreateDynamicVariable(simulationFolder, scalarSimulation + "SByte", "SByte", DataTypeIds.SByte, ValueRanks.Scalar);
CreateDynamicVariable(simulationFolder, scalarSimulation + "String", "String", DataTypeIds.String, ValueRanks.Scalar);
CreateDynamicVariable(simulationFolder, scalarSimulation + "Time", "Time", DataTypeIds.Time, ValueRanks.Scalar);
CreateDynamicVariable(simulationFolder, scalarSimulation + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.Scalar);
CreateDynamicVariable(simulationFolder, scalarSimulation + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.Scalar);
CreateDynamicVariable(simulationFolder, scalarSimulation + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.Scalar);
@@ -439,6 +443,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region Scalar_Simulation_Arrays
ResetRandomGenerator(7);
FolderState arraysSimulationFolder = CreateFolder(simulationFolder, "Scalar_Simulation_Arrays", "Arrays");
const string simulationArrays = "Scalar_Simulation_Arrays_";
CreateDynamicVariable(arraysSimulationFolder, simulationArrays + "Boolean", "Boolean", DataTypeIds.Boolean, ValueRanks.OneDimension);
@@ -460,7 +465,6 @@ namespace Quickstarts.ReferenceServer
CreateDynamicVariable(arraysSimulationFolder, simulationArrays + "QualifiedName", "QualifiedName", DataTypeIds.QualifiedName, ValueRanks.OneDimension);
CreateDynamicVariable(arraysSimulationFolder, simulationArrays + "SByte", "SByte", DataTypeIds.SByte, ValueRanks.OneDimension);
CreateDynamicVariable(arraysSimulationFolder, simulationArrays + "String", "String", DataTypeIds.String, ValueRanks.OneDimension);
CreateDynamicVariable(arraysSimulationFolder, simulationArrays + "Time", "Time", DataTypeIds.Time, ValueRanks.OneDimension);
CreateDynamicVariable(arraysSimulationFolder, simulationArrays + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.OneDimension);
CreateDynamicVariable(arraysSimulationFolder, simulationArrays + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.OneDimension);
CreateDynamicVariable(arraysSimulationFolder, simulationArrays + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.OneDimension);
@@ -471,6 +475,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region Scalar_Simulation_Mass
ResetRandomGenerator(8);
FolderState massSimulationFolder = CreateFolder(simulationFolder, "Scalar_Simulation_Mass", "Mass");
const string massSimulation = "Scalar_Simulation_Mass_";
CreateDynamicVariables(massSimulationFolder, massSimulation + "Boolean", "Boolean", DataTypeIds.Boolean, ValueRanks.Scalar, 100);
@@ -492,7 +497,6 @@ namespace Quickstarts.ReferenceServer
CreateDynamicVariables(massSimulationFolder, massSimulation + "QualifiedName", "QualifiedName", DataTypeIds.QualifiedName, ValueRanks.Scalar, 100);
CreateDynamicVariables(massSimulationFolder, massSimulation + "SByte", "SByte", DataTypeIds.SByte, ValueRanks.Scalar, 100);
CreateDynamicVariables(massSimulationFolder, massSimulation + "String", "String", DataTypeIds.String, ValueRanks.Scalar, 100);
CreateDynamicVariables(massSimulationFolder, massSimulation + "Time", "Time", DataTypeIds.Time, ValueRanks.Scalar, 100);
CreateDynamicVariables(massSimulationFolder, massSimulation + "UInt16", "UInt16", DataTypeIds.UInt16, ValueRanks.Scalar, 100);
CreateDynamicVariables(massSimulationFolder, massSimulation + "UInt32", "UInt32", DataTypeIds.UInt32, ValueRanks.Scalar, 100);
CreateDynamicVariables(massSimulationFolder, massSimulation + "UInt64", "UInt64", DataTypeIds.UInt64, ValueRanks.Scalar, 100);
@@ -503,6 +507,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region DataAccess_DataItem
ResetRandomGenerator(9);
FolderState daFolder = CreateFolder(root, "DataAccess", "DataAccess");
BaseDataVariableState daInstructions = CreateVariable(daFolder, "DataAccess_Instructions", "Instructions", DataTypeIds.String, ValueRanks.Scalar);
daInstructions.Value = "A library of Read/Write Variables of all supported data-types.";
@@ -524,6 +529,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region DataAccess_AnalogType
ResetRandomGenerator(10);
FolderState analogItemFolder = CreateFolder(daFolder, "DataAccess_AnalogType", "AnalogType");
const string daAnalogItem = "DataAccess_AnalogType_";
@@ -546,11 +552,18 @@ namespace Quickstarts.ReferenceServer
item.EURange.Value.High = 0;
item.EURange.Value.Low = 0;
}
//set default value for Definition property
if (item.Definition != null)
{
item.Definition.Value = String.Empty;
}
}
}
#endregion
#region DataAccess_AnalogType_Array
ResetRandomGenerator(11);
FolderState analogArrayFolder = CreateFolder(analogItemFolder, "DataAccess_AnalogType_Array", "Array");
const string daAnalogArray = "DataAccess_AnalogType_Array_";
@@ -570,10 +583,9 @@ namespace Quickstarts.ReferenceServer
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "LocalizedText", "LocalizedText", BuiltInType.LocalizedText, ValueRanks.OneDimension, new LocalizedText[] { new LocalizedText("en", "Hello World1"), new LocalizedText("en", "Hello World2"), new LocalizedText("en", "Hello World3"), new LocalizedText("en", "Hello World4"), new LocalizedText("en", "Hello World5"), new LocalizedText("en", "Hello World6"), new LocalizedText("en", "Hello World7"), new LocalizedText("en", "Hello World8"), new LocalizedText("en", "Hello World9"), new LocalizedText("en", "Hello World10") });
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "NodeId", "NodeId", BuiltInType.NodeId, ValueRanks.OneDimension, new NodeId[] { new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()), new NodeId(Guid.NewGuid()) });
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "Number", "Number", BuiltInType.Number, ValueRanks.OneDimension, new Int16[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "QualifiedName", "QualifiedName", BuiltInType.QualifiedName, ValueRanks.OneDimension, new QualifiedName[] { "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9"});
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "QualifiedName", "QualifiedName", BuiltInType.QualifiedName, ValueRanks.OneDimension, new QualifiedName[] { "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9" });
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "SByte", "SByte", BuiltInType.SByte, ValueRanks.OneDimension, new SByte[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 });
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "String", "String", BuiltInType.String, ValueRanks.OneDimension, new String[] { "a00", "b10", "c20", "d30", "e40", "f50", "g60", "h70", "i80", "j90" });
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "Time", "Time", DataTypeIds.Time, ValueRanks.OneDimension, new String[] { DateTime.MinValue.ToString(), DateTime.MaxValue.ToString(), DateTime.MinValue.ToString(), DateTime.MaxValue.ToString(), DateTime.MinValue.ToString(), DateTime.MaxValue.ToString(), DateTime.MinValue.ToString(), DateTime.MaxValue.ToString(), DateTime.MinValue.ToString(), DateTime.MaxValue.ToString() }, null);
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "UInt16", "UInt16", BuiltInType.UInt16, ValueRanks.OneDimension, new UInt16[] { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 });
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "UInt32", "UInt32", BuiltInType.UInt32, ValueRanks.OneDimension, new UInt32[] { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 });
CreateAnalogItemVariable(analogArrayFolder, daAnalogArray + "UInt64", "UInt64", BuiltInType.UInt64, ValueRanks.OneDimension, new UInt64[] { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 });
@@ -585,6 +597,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region DataAccess_DiscreteType
ResetRandomGenerator(12);
FolderState discreteTypeFolder = CreateFolder(daFolder, "DataAccess_DiscreteType", "DiscreteType");
FolderState twoStateDiscreteFolder = CreateFolder(discreteTypeFolder, "DataAccess_TwoStateDiscreteType", "TwoStateDiscreteType");
const string daTwoStateDiscrete = "DataAccess_TwoStateDiscreteType_";
@@ -608,6 +621,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region DataAccess_MultiStateValueDiscreteType
ResetRandomGenerator(13);
FolderState multiStateValueDiscreteFolder = CreateFolder(discreteTypeFolder, "DataAccess_MultiStateValueDiscreteType", "MultiStateValueDiscreteType");
const string daMultiStateValueDiscrete = "DataAccess_MultiStateValueDiscreteType_";
@@ -631,6 +645,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region References
ResetRandomGenerator(14);
FolderState referencesFolder = CreateFolder(root, "References", "References");
const string referencesPrefix = "References_";
@@ -677,6 +692,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region AccessRights
ResetRandomGenerator(15);
FolderState folderAccessRights = CreateFolder(root, "AccessRights", "AccessRights");
const string accessRights = "AccessRights_";
@@ -829,6 +845,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region NodeIds
ResetRandomGenerator(16);
FolderState nodeIdsFolder = CreateFolder(root, "NodeIds", "NodeIds");
const string nodeIds = "NodeIds_";
@@ -852,6 +869,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region Methods
ResetRandomGenerator(17);
FolderState methodsFolder = CreateFolder(root, "Methods", "Methods");
const string methods = "Methods_";
@@ -1084,14 +1102,20 @@ namespace Quickstarts.ReferenceServer
#endregion
#region Views
ResetRandomGenerator(18);
FolderState viewsFolder = CreateFolder(root, "Views", "Views");
const string views = "Views_";
ViewState viewStateOperations = CreateView(viewsFolder, externalReferences, views + "Operations", "Operations");
viewStateOperations.AddReference(ReferenceTypes.Organizes, false, massFolder.NodeId);
massFolder.AddReference(ReferenceTypes.Organizes, true, viewStateOperations.NodeId);
ViewState viewStateEngineering = CreateView(viewsFolder, externalReferences, views + "Engineering", "Engineering");
viewStateEngineering.AddReference(ReferenceTypes.Organizes, false, simulationFolder.NodeId);
simulationFolder.AddReference(ReferenceTypes.Organizes, true, viewStateEngineering.NodeId);
#endregion
#region Locales
ResetRandomGenerator(19);
FolderState localesFolder = CreateFolder(root, "Locales", "Locales");
const string locales = "Locales_";
@@ -1178,6 +1202,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region Attributes
ResetRandomGenerator(20);
FolderState folderAttributes = CreateFolder(root, "Attributes", "Attributes");
#region AccessAll
@@ -1414,6 +1439,7 @@ namespace Quickstarts.ReferenceServer
#endregion
#region MyCompany
ResetRandomGenerator(21);
FolderState myCompanyFolder = CreateFolder(root, "MyCompany", "MyCompany");
const string myCompany = "MyCompany_";
@@ -1424,10 +1450,13 @@ namespace Quickstarts.ReferenceServer
}
catch (Exception e)
{
Utils.Trace(e, "Error creating the address space.");
Utils.LogError(e, "Error creating the ReferenceNodeManager address space.");
}
AddPredefinedNode(SystemContext, root);
// reset random generator and generate boundary values
ResetRandomGenerator(100, 1);
m_simulationTimer = new Timer(DoSimulation, null, 1000, 1000);
}
}
@@ -1447,7 +1476,7 @@ namespace Quickstarts.ReferenceServer
}
catch (Exception e)
{
Utils.Trace(e, "Error writing Interval variable.");
Utils.LogError(e, "Error writing Interval variable.");
return ServiceResult.Create(e, StatusCodes.Bad, "Error writing Interval variable.");
}
}
@@ -1471,7 +1500,7 @@ namespace Quickstarts.ReferenceServer
}
catch (Exception e)
{
Utils.Trace(e, "Error writing Enabled variable.");
Utils.LogError(e, "Error writing Enabled variable.");
return ServiceResult.Create(e, StatusCodes.Bad, "Error writing Enabled variable.");
}
}
@@ -1995,7 +2024,7 @@ namespace Quickstarts.ReferenceServer
double number = Convert.ToDouble(value);
if (number >= variable.EnumStrings.Value.Length | number < 0)
if (number >= variable.EnumStrings.Value.Length || number < 0)
{
return StatusCodes.BadOutOfRange;
}
@@ -2166,7 +2195,6 @@ namespace Quickstarts.ReferenceServer
private BaseDataVariableState CreateVariable(NodeState parent, string path, string name, NodeId dataType, int valueRank)
{
BaseDataVariableState variable = new BaseDataVariableState(parent);
variable.SymbolicName = name;
variable.ReferenceTypeId = ReferenceTypes.Organizes;
variable.TypeDefinitionId = VariableTypeIds.BaseDataVariableType;
@@ -2615,13 +2643,16 @@ namespace Quickstarts.ReferenceServer
}
}
private void ResetRandomGenerator(int seed, int boundaryValueFrequency = 0)
{
m_randomSource = new RandomSource(seed);
m_generator = new DataGenerator(m_randomSource);
m_generator.BoundaryValueFrequency = boundaryValueFrequency;
}
private object GetNewValue(BaseVariableState variable)
{
if (m_generator == null)
{
m_generator = new Opc.Ua.Test.DataGenerator(null);
m_generator.BoundaryValueFrequency = 0;
}
Debug.Assert(m_generator != null, "Need a random generator!");
object value = null;
int retryCount = 0;
@@ -2629,6 +2660,14 @@ namespace Quickstarts.ReferenceServer
while (value == null && retryCount < 10)
{
value = m_generator.GetRandom(variable.DataType, variable.ValueRank, new uint[] { 10 }, Server.TypeTree);
// skip Variant Null
if (value is Variant variant)
{
if (variant.Value == null)
{
value = null;
}
}
retryCount++;
}
@@ -2641,17 +2680,18 @@ namespace Quickstarts.ReferenceServer
{
lock (Lock)
{
var timeStamp = DateTime.UtcNow;
foreach (BaseDataVariableState variable in m_dynamicNodes)
{
variable.Value = GetNewValue(variable);
variable.Timestamp = DateTime.UtcNow;
variable.Timestamp = timeStamp;
variable.ClearChangeMasks(SystemContext, false);
}
}
}
catch (Exception e)
{
Utils.Trace(e, "Unexpected error doing simulation.");
Utils.LogError(e, "Unexpected error doing simulation.");
}
}
@@ -2727,11 +2767,21 @@ namespace Quickstarts.ReferenceServer
#region Private Fields
private ReferenceServerConfiguration m_configuration;
private Opc.Ua.Test.DataGenerator m_generator;
private RandomSource m_randomSource;
private DataGenerator m_generator;
private Timer m_simulationTimer;
private UInt16 m_simulationInterval = 1000;
private bool m_simulationEnabled = true;
private List<BaseDataVariableState> m_dynamicNodes;
#endregion
}
public static class VariableExtensions
{
public static BaseDataVariableState MinimumSamplingInterval(this BaseDataVariableState variable, int minimumSamplingInterval)
{
variable.MinimumSamplingInterval = minimumSamplingInterval;
return variable;
}
}
}
@@ -29,6 +29,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using Opc.Ua;
using Opc.Ua.Server;
@@ -36,7 +38,7 @@ using Opc.Ua.Server;
namespace Quickstarts.ReferenceServer
{
/// <summary>
/// Implements a basic Quickstart Server.
/// Implements the Quickstart Reference Server.
/// </summary>
/// <remarks>
/// Each server instance must have one instance of a StandardServer object which is
@@ -59,13 +61,18 @@ namespace Quickstarts.ReferenceServer
/// </remarks>
protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
{
Utils.Trace("Creating the Node Managers.");
Utils.LogInfo(Utils.TraceMasks.StartStop, "Creating the Reference Server Node Manager.");
List<INodeManager> nodeManagers = new List<INodeManager>();
IList<INodeManager> nodeManagers = new List<INodeManager>();
// create the custom node managers.
// create the custom node manager.
nodeManagers.Add(new ReferenceNodeManager(server, configuration));
foreach (var nodeManagerFactory in NodeManagerFactories)
{
nodeManagers.Add(nodeManagerFactory.Create(server, configuration));
}
// create master node manager.
return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray());
}
@@ -78,14 +85,14 @@ namespace Quickstarts.ReferenceServer
/// </remarks>
protected override ServerProperties LoadServerProperties()
{
ServerProperties properties = new ServerProperties();
properties.ManufacturerName = "OPC Foundation";
properties.ProductName = "Quickstart Reference Server";
properties.ProductUri = "http://opcfoundation.org/Quickstart/ReferenceServer/v1.04";
properties.SoftwareVersion = Utils.GetAssemblySoftwareVersion();
properties.BuildNumber = Utils.GetAssemblyBuildNumber();
properties.BuildDate = Utils.GetAssemblyTimestamp();
ServerProperties properties = new ServerProperties {
ManufacturerName = "OPC Foundation",
ProductName = "Quickstart Reference Server",
ProductUri = "http://opcfoundation.org/Quickstart/ReferenceServer/v1.04",
SoftwareVersion = Utils.GetAssemblySoftwareVersion(),
BuildNumber = Utils.GetAssemblyBuildNumber(),
BuildDate = Utils.GetAssemblyTimestamp()
};
return properties;
}
@@ -121,7 +128,7 @@ namespace Quickstarts.ReferenceServer
/// </remarks>
protected override void OnServerStarting(ApplicationConfiguration configuration)
{
Utils.Trace("The server is starting.");
Utils.LogInfo(Utils.TraceMasks.StartStop, "The server is starting.");
base.OnServerStarting(configuration);
@@ -142,13 +149,45 @@ namespace Quickstarts.ReferenceServer
try
{
// allow a faster sampling interval for CurrentTime node.
ServerInternal.Status.Variable.CurrentTime.MinimumSamplingInterval = 250;
lock (ServerInternal.Status.Lock)
{
// allow a faster sampling interval for CurrentTime node.
ServerInternal.Status.Variable.CurrentTime.MinimumSamplingInterval = 250;
}
}
catch
{ }
}
/// <summary>
/// Override some of the default user token policies for some endpoints.
/// </summary>
/// <remarks>
/// Sample to show how to override default user token policies.
/// </remarks>
public override UserTokenPolicyCollection GetUserTokenPolicies(ApplicationConfiguration configuration, EndpointDescription description)
{
var policies = base.GetUserTokenPolicies(configuration, description);
// sample how to modify default user token policies
if (description.SecurityPolicyUri == SecurityPolicies.Aes256_Sha256_RsaPss &&
description.SecurityMode == MessageSecurityMode.SignAndEncrypt)
{
policies = new UserTokenPolicyCollection(policies.Where(u => u.TokenType != UserTokenType.Certificate));
}
else if (description.SecurityPolicyUri == SecurityPolicies.Aes128_Sha256_RsaOaep &&
description.SecurityMode == MessageSecurityMode.Sign)
{
policies = new UserTokenPolicyCollection(policies.Where(u => u.TokenType != UserTokenType.Anonymous));
}
else if (description.SecurityPolicyUri == SecurityPolicies.Aes128_Sha256_RsaOaep &&
description.SecurityMode == MessageSecurityMode.SignAndEncrypt)
{
policies = new UserTokenPolicyCollection(policies.Where(u => u.TokenType != UserTokenType.UserName));
}
return policies;
}
#endregion
#region User Validation Functions
@@ -193,6 +232,8 @@ namespace Quickstarts.ReferenceServer
{
args.Identity = VerifyPassword(userNameToken);
Utils.LogInfo(Utils.TraceMasks.Security, "Username Token Accepted: {0}", args.Identity?.DisplayName);
// set AuthenticatedUser role for accepted user/password authentication
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_AuthenticatedUser);
@@ -213,7 +254,7 @@ namespace Quickstarts.ReferenceServer
{
VerifyUserTokenCertificate(x509Token.Certificate);
args.Identity = new UserIdentity(x509Token);
Utils.Trace("X509 Token Accepted: {0}", args.Identity.DisplayName);
Utils.LogInfo(Utils.TraceMasks.Security, "X509 Token Accepted: {0}", args.Identity?.DisplayName);
// set AuthenticatedUser role for accepted certificate authentication
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_AuthenticatedUser);
@@ -221,9 +262,19 @@ namespace Quickstarts.ReferenceServer
return;
}
// allow anonymous authentication and set Anonymous role for this authentication
args.Identity = new UserIdentity();
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_Anonymous);
// check for anonymous token.
if (args.NewIdentity is AnonymousIdentityToken || args.NewIdentity == null)
{
// allow anonymous authentication and set Anonymous role for this authentication
args.Identity = new UserIdentity();
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_Anonymous);
return;
}
// unsuported identity token type.
throw ServiceResultException.Create(StatusCodes.BadIdentityTokenInvalid,
"Not supported user token type: {0}.", args.NewIdentity);
}
/// <summary>
@@ -27,18 +27,14 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.Collections.Generic;
using Opc.Ua.Server;
namespace Quickstarts.ReferenceServer
{
/// <summary>
/// Stores the configuration the data access node manager.
/// </summary>
[DataContract(Namespace=Namespaces.ReferenceApplications)]
[DataContract(Namespace = Namespaces.ReferenceServer)]
public class ReferenceServerConfiguration
{
#region Constructors
@@ -62,7 +58,7 @@ namespace Quickstarts.ReferenceServer
/// <summary>
/// Sets private members to default values.
/// </summary>
private void Initialize()
private static void Initialize()
{
}
#endregion
@@ -1,5 +1,5 @@
/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
* Copyright (c) 2005-2019 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
@@ -29,15 +29,14 @@
using System;
using System.Collections.Generic;
using System.Text;
using Opc.Ua.Server;
namespace Opc.Ua.Server
namespace Opc.Ua.Sample
{
/// <summary>
/// Provides a basic monitored item implementation which does not support queuing.
/// </summary>
[Obsolete("Class replaced by Opc.Ua.Server.MonitoredItem")]
public class DataChangeMonitoredItem : IDataChangeMonitoredItem
public class DataChangeMonitoredItem : IDataChangeMonitoredItem2
{
#region Constructors
/// <summary>
@@ -66,7 +65,7 @@ namespace Opc.Ua.Server
m_monitoringMode = monitoringMode;
m_clientHandle = clientHandle;
m_samplingInterval = samplingInterval;
m_nextSampleTime = HiResClock.TickCount64;
m_nextSampleTime = DateTime.UtcNow.Ticks;
m_readyToPublish = false;
m_readyToTrigger = false;
m_alwaysReportUpdates = alwaysReportUpdates;
@@ -102,7 +101,7 @@ namespace Opc.Ua.Server
m_monitoringMode = monitoringMode;
m_clientHandle = clientHandle;
m_samplingInterval = samplingInterval;
m_nextSampleTime = HiResClock.TickCount64;
m_nextSampleTime = DateTime.UtcNow.Ticks;
m_readyToPublish = false;
m_readyToTrigger = false;
m_queue = null;
@@ -172,14 +171,14 @@ namespace Opc.Ua.Server
return Int32.MaxValue;
}
var now = HiResClock.TickCount64;
DateTime now = DateTime.UtcNow;
if (m_nextSampleTime <= now)
if (m_nextSampleTime <= now.Ticks)
{
return 0;
}
return (int)(m_nextSampleTime - now);
return (int)((m_nextSampleTime - now.Ticks) / TimeSpan.TicksPerMillisecond);
}
}
}
@@ -241,7 +240,7 @@ namespace Opc.Ua.Server
m_clientHandle = clientHandle;
// subtract the previous sampling interval.
long oldSamplingInterval = (long)m_samplingInterval;
long oldSamplingInterval = (long)(m_samplingInterval * TimeSpan.TicksPerMillisecond);
if (oldSamplingInterval < m_nextSampleTime)
{
@@ -251,7 +250,7 @@ namespace Opc.Ua.Server
m_samplingInterval = samplingInterval;
// calculate the next sampling interval.
long newSamplingInterval = (long)m_samplingInterval;
long newSamplingInterval = (long)(m_samplingInterval * TimeSpan.TicksPerMillisecond);
if (m_samplingInterval > 0)
{
@@ -307,7 +306,7 @@ namespace Opc.Ua.Server
value.ServerTimestamp = DateTime.UtcNow;
QueueValue(value, error);
QueueValue(value, error, false);
}
#endregion
@@ -365,7 +364,7 @@ namespace Opc.Ua.Server
}
/// <summary>
/// The identifier for the client handle assigned to the monitored item.
/// The client handle.
/// </summary>
public uint ClientHandle
{
@@ -426,7 +425,7 @@ namespace Opc.Ua.Server
}
// re-queue if too little time has passed since the last publish.
long now = HiResClock.TickCount64;
long now = DateTime.UtcNow.Ticks;
if (m_nextSampleTime > now)
{
@@ -519,6 +518,16 @@ namespace Opc.Ua.Server
/// Queues a new data change.
/// </summary>
public void QueueValue(DataValue value, ServiceResult error)
{
QueueValue(value, error, false);
}
#endregion
#region IDataChangeMonitoredItem2 Members
/// <summary>
/// Queues a new data change.
/// </summary>
public void QueueValue(DataValue value, ServiceResult error, bool ignoreFilters)
{
lock (m_lock)
{
@@ -611,7 +620,7 @@ namespace Opc.Ua.Server
if (previousMode == MonitoringMode.Disabled)
{
m_nextSampleTime = HiResClock.TickCount64;
m_nextSampleTime = DateTime.UtcNow.Ticks;
m_lastError = null;
m_lastValue = null;
}
@@ -642,8 +651,8 @@ namespace Opc.Ua.Server
private void IncrementSampleTime()
{
// update next sample time.
long now = HiResClock.TickCount64;
long samplingInterval = (long)m_samplingInterval;
long now = DateTime.UtcNow.Ticks;
long samplingInterval = (long)(m_samplingInterval * TimeSpan.TicksPerMillisecond);
if (m_nextSampleTime > 0)
{
@@ -1,5 +1,5 @@
/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
* Copyright (c) 2005-2019 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
@@ -29,14 +29,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using Opc.Ua.Server;
namespace Opc.Ua.Server
namespace Opc.Ua.Sample
{
/// <summary>
/// Keeps track of the monitored items for a single node.
/// </summary>
[Obsolete("Class replaced by Opc.Ua.Server.MonitoredNode2")]
public class MonitoredNode
{
#region Constructors
@@ -247,7 +246,7 @@ namespace Opc.Ua.Server
// check if the node has been deleted.
if ((masks & NodeStateChangeMasks.Deleted) != 0)
{
monitoredItem.QueueValue(null, StatusCodes.BadNodeIdUnknown);
monitoredItem.QueueValue(null, StatusCodes.BadNodeIdUnknown, false);
continue;
}
@@ -1,5 +1,5 @@
/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
* Copyright (c) 2005-2022 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
@@ -29,26 +29,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Xml;
using System.IO;
using System.Threading;
using System.Reflection;
using Opc.Ua.Server;
using System.Linq;
using System.Diagnostics;
namespace Opc.Ua.Server
namespace Opc.Ua.Sample
{
/// <summary>
/// A node manager for a variety of test data.
/// </summary>
[Obsolete("Class replaced by Opc.Ua.Server.CustomNodeManager2")]
public class CustomNodeManager : INodeManager, INodeIdFactory, IDisposable
public class SampleNodeManager : INodeManager, INodeIdFactory, IDisposable
{
#region Constructors
/// <summary>
/// Initializes the node manager.
/// </summary>
public CustomNodeManager(IServerInternal server)
public SampleNodeManager(IServerInternal server)
{
// save a reference to the server that owns the node manager.
m_server = server;
@@ -74,6 +72,7 @@ namespace Opc.Ua.Server
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
@@ -88,12 +87,9 @@ namespace Opc.Ua.Server
Utils.SilentDispose(m_samplingTimer);
m_samplingTimer = null;
if (m_predefinedNodes != null)
foreach (NodeState node in m_predefinedNodes.Values)
{
foreach (NodeState node in m_predefinedNodes.Values)
{
Utils.SilentDispose(node);
}
Utils.SilentDispose(node);
}
}
}
@@ -297,6 +293,16 @@ namespace Opc.Ua.Server
return found;
}
/// <summary>
/// Adds all encodeable types defined in a node manager to the server factory.
/// </summary>
/// <param name="assembly">The assembly which contains the encodeable types.</param>
/// <param name="filter">A filter with which the FullName of the type must start.</param>
protected void AddEncodeableNodeManagerTypes(Assembly assembly, string filter)
{
Server.Factory.AddEncodeableTypes(assembly.GetExportedTypes().Where(t => t.FullName.StartsWith(filter)));
}
#endregion
#region INodeManager Members
@@ -359,7 +365,7 @@ namespace Opc.Ua.Server
ISystemContext context,
Assembly assembly,
string resourcePath,
IDictionary<NodeId,IList<IReference>> externalReferences)
IDictionary<NodeId, IList<IReference>> externalReferences)
{
// load the predefined nodes from an XML document.
NodeStateCollection predefinedNodes = new NodeStateCollection();
@@ -371,7 +377,7 @@ namespace Opc.Ua.Server
AddPredefinedNode(context, predefinedNodes[ii]);
}
// ensure the reverse references exist.
// ensure the reverse refernces exist.
AddReverseReferences(externalReferences);
}
@@ -388,7 +394,7 @@ namespace Opc.Ua.Server
/// </summary>
protected virtual void LoadPredefinedNodes(
ISystemContext context,
IDictionary<NodeId,IList<IReference>> externalReferences)
IDictionary<NodeId, IList<IReference>> externalReferences)
{
// load the predefined nodes from an XML document.
NodeStateCollection predefinedNodes = LoadPredefinedNodes(context);
@@ -399,7 +405,7 @@ namespace Opc.Ua.Server
AddPredefinedNode(context, predefinedNodes[ii]);
}
// ensure the reverse references exist.
// ensure the reverse refernces exist.
AddReverseReferences(externalReferences);
}
@@ -582,6 +588,7 @@ namespace Opc.Ua.Server
}
// add reference from supertype for type nodes.
/*
BaseTypeState type = source as BaseTypeState;
if (type != null && !NodeId.IsNull(type.SuperTypeId))
@@ -596,6 +603,7 @@ namespace Opc.Ua.Server
externalReferences);
}
}
*/
IList<IReference> references = new List<IReference>();
source.GetReferences(SystemContext, references);
@@ -779,7 +787,7 @@ namespace Opc.Ua.Server
/// NodeManager it should return a handle that does not require the NodeId to be validated again when
/// the handle is passed into other methods such as 'Read' or 'Write'.
/// </remarks>
protected virtual object GetManagerHandle(ISystemContext context, NodeId nodeId, IDictionary<NodeId,NodeState> cache)
protected virtual object GetManagerHandle(ISystemContext context, NodeId nodeId, IDictionary<NodeId, NodeState> cache)
{
lock (Lock)
{
@@ -834,11 +842,11 @@ namespace Opc.Ua.Server
/// This method is used to delete bi-directional references to nodes from other node managers.
/// </summary>
public virtual ServiceResult DeleteReference(
object sourceHandle,
NodeId referenceTypeId,
bool isInverse,
object sourceHandle,
NodeId referenceTypeId,
bool isInverse,
ExpandedNodeId targetId,
bool deleteBiDirectional)
bool deleteBidirectional)
{
lock (Lock)
{
@@ -852,7 +860,7 @@ namespace Opc.Ua.Server
source.RemoveReference(referenceTypeId, isInverse, targetId);
if (deleteBiDirectional)
if (deleteBidirectional)
{
// check if the target is also managed by the node manager.
if (!targetId.IsAbsolute)
@@ -878,7 +886,7 @@ namespace Opc.Ua.Server
/// </remarks>
public virtual NodeMetadata GetNodeMetadata(
OperationContext context,
object targetHandle,
object targetHandle,
BrowseResultMask resultMask)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
@@ -972,8 +980,8 @@ namespace Opc.Ua.Server
/// The node manager can store its state information in the Data and Index properties.
/// </remarks>
public virtual void Browse(
OperationContext context,
ref ContinuationPoint continuationPoint,
OperationContext context,
ref ContinuationPoint continuationPoint,
IList<ReferenceDescription> references)
{
if (continuationPoint == null) throw new ArgumentNullException(nameof(continuationPoint));
@@ -1145,14 +1153,14 @@ namespace Opc.Ua.Server
/// browse name.
/// </remarks>
public virtual void TranslateBrowsePath(
OperationContext context,
object sourceHandle,
RelativePathElement relativePath,
OperationContext context,
object sourceHandle,
RelativePathElement relativePath,
IList<ExpandedNodeId> targetIds,
IList<NodeId> unresolvedTargetIds)
IList<NodeId> unresolvedTargetIds)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>();
lock (Lock)
{
@@ -1225,6 +1233,7 @@ namespace Opc.Ua.Server
// check browse name.
if (target.BrowseName == relativePath.TargetName)
{
// ensure duplicate node ids are not added.
if (!targetIds.Contains(reference.TargetId))
{
targetIds.Add(reference.TargetId);
@@ -1243,14 +1252,14 @@ namespace Opc.Ua.Server
/// Reads the value for the specified attribute.
/// </summary>
public virtual void Read(
OperationContext context,
double maxAge,
IList<ReadValueId> nodesToRead,
IList<DataValue> values,
OperationContext context,
double maxAge,
IList<ReadValueId> nodesToRead,
IList<DataValue> values,
IList<ServiceResult> errors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>();
List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>();
lock (Lock)
@@ -1279,10 +1288,10 @@ namespace Opc.Ua.Server
// create an initial value.
DataValue value = values[ii] = new DataValue();
value.Value = null;
value.Value = null;
value.ServerTimestamp = DateTime.UtcNow;
value.SourceTimestamp = DateTime.MinValue;
value.StatusCode = StatusCodes.Good;
value.StatusCode = StatusCodes.Good;
// check if the node is ready for reading.
if (source.ValidationRequired)
@@ -1366,16 +1375,16 @@ namespace Opc.Ua.Server
/// Reads the history for the specified nodes.
/// </summary>
public virtual void HistoryRead(
OperationContext context,
HistoryReadDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
OperationContext context,
HistoryReadDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
IList<HistoryReadValueId> nodesToRead,
IList<HistoryReadResult> results,
IList<ServiceResult> errors)
IList<HistoryReadResult> results,
IList<ServiceResult> errors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>();
List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>();
List<ReadWriteOperationState> readsToComplete = new List<ReadWriteOperationState>();
@@ -1466,12 +1475,12 @@ namespace Opc.Ua.Server
/// </summary>
protected virtual ServiceResult HistoryRead(
ISystemContext context,
NodeState source,
HistoryReadDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
HistoryReadValueId nodesToRead,
HistoryReadResult result)
NodeState source,
HistoryReadDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
HistoryReadValueId nodesToRead,
HistoryReadResult result)
{
// check for variable.
BaseVariableState variable = source as BaseVariableState;
@@ -1543,12 +1552,12 @@ namespace Opc.Ua.Server
/// </summary>
protected virtual ServiceResult HistoryReadRaw(
ISystemContext context,
BaseVariableState source,
BaseVariableState source,
ReadRawModifiedDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
HistoryReadValueId nodeToRead,
HistoryReadResult result)
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
HistoryReadValueId nodeToRead,
HistoryReadResult result)
{
return StatusCodes.BadHistoryOperationUnsupported;
}
@@ -1558,12 +1567,12 @@ namespace Opc.Ua.Server
/// </summary>
protected virtual ServiceResult HistoryReadProcessed(
ISystemContext context,
BaseVariableState source,
ReadProcessedDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
HistoryReadValueId nodeToRead,
HistoryReadResult result)
BaseVariableState source,
ReadProcessedDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
HistoryReadValueId nodeToRead,
HistoryReadResult result)
{
return StatusCodes.BadHistoryOperationUnsupported;
}
@@ -1573,12 +1582,12 @@ namespace Opc.Ua.Server
/// </summary>
protected virtual ServiceResult HistoryReadAtTime(
ISystemContext context,
BaseVariableState source,
ReadAtTimeDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
HistoryReadValueId nodeToRead,
HistoryReadResult result)
BaseVariableState source,
ReadAtTimeDetails details,
TimestampsToReturn timestampsToReturn,
bool releaseContinuationPoints,
HistoryReadValueId nodeToRead,
HistoryReadResult result)
{
return StatusCodes.BadHistoryOperationUnsupported;
}
@@ -1588,12 +1597,12 @@ namespace Opc.Ua.Server
/// Writes the value for the specified attributes.
/// </summary>
public virtual void Write(
OperationContext context,
IList<WriteValue> nodesToWrite,
OperationContext context,
IList<WriteValue> nodesToWrite,
IList<ServiceResult> errors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>();
List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>();
lock (Lock)
@@ -1622,7 +1631,7 @@ namespace Opc.Ua.Server
// index range is not supported.
if (!String.IsNullOrEmpty(nodeToWrite.IndexRange))
{
errors[ii] = StatusCodes.BadIndexRangeInvalid;
errors[ii] = StatusCodes.BadWriteNotSupported;
continue;
}
@@ -1688,14 +1697,14 @@ namespace Opc.Ua.Server
/// Updates the history for the specified nodes.
/// </summary>
public virtual void HistoryUpdate(
OperationContext context,
Type detailsType,
OperationContext context,
Type detailsType,
IList<HistoryUpdateDetails> nodesToUpdate,
IList<HistoryUpdateResult> results,
IList<ServiceResult> errors)
IList<HistoryUpdateResult> results,
IList<ServiceResult> errors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>();
List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>();
lock (Lock)
@@ -1767,13 +1776,13 @@ namespace Opc.Ua.Server
/// Calls a method on the specified nodes.
/// </summary>
public virtual void Call(
OperationContext context,
OperationContext context,
IList<CallMethodRequest> methodsToCall,
IList<CallMethodResult> results,
IList<ServiceResult> errors)
IList<CallMethodResult> results,
IList<ServiceResult> errors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>();
List<CallOperationState> nodesToValidate = new List<CallOperationState>();
lock (Lock)
@@ -1799,20 +1808,22 @@ namespace Opc.Ua.Server
// owned by this node manager.
methodToCall.Processed = true;
// check for valid method.
MethodState method = GetManagerHandle(systemContext, methodToCall.MethodId, operationCache) as MethodState;
// find the method.
MethodState method = source.FindMethod(systemContext, methodToCall.MethodId);
if (method == null)
{
errors[ii] = StatusCodes.BadMethodInvalid;
continue;
}
// check for loose coupling.
if (source.ReferenceExists(ReferenceTypeIds.HasComponent, false, methodToCall.MethodId))
{
method = (MethodState)FindPredefinedNode(methodToCall.MethodId, typeof(MethodState));
}
// check if method belongs to the object.
if (!Object.ReferenceEquals(method.Parent, source))
{
errors[ii] = StatusCodes.BadMethodInvalid;
continue;
if (method == null)
{
errors[ii] = StatusCodes.BadMethodInvalid;
continue;
}
}
CallMethodResult result = results[ii] = new CallMethodResult();
@@ -1970,14 +1981,13 @@ namespace Opc.Ua.Server
/// the notifier hierarchy.
/// </remarks>
public virtual ServiceResult SubscribeToEvents(
OperationContext context,
object sourceId,
uint subscriptionId,
OperationContext context,
object sourceId,
uint subscriptionId,
IEventMonitoredItem monitoredItem,
bool unsubscribe)
bool unsubscribe)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
lock (Lock)
{
@@ -2036,13 +2046,12 @@ namespace Opc.Ua.Server
/// manager must start/stop reporting events for all objects that it manages.
/// </remarks>
public virtual ServiceResult SubscribeToAllEvents(
OperationContext context,
uint subscriptionId,
OperationContext context,
uint subscriptionId,
IEventMonitoredItem monitoredItem,
bool unsubscribe)
bool unsubscribe)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
lock (Lock)
{
@@ -2064,10 +2073,10 @@ namespace Opc.Ua.Server
/// Subscribes/unsubscribes to all events produced by the specified node.
/// </summary>
protected void SubscribeToAllEvents(
ISystemContext systemContext,
ISystemContext systemContext,
IEventMonitoredItem monitoredItem,
bool unsubscribe,
NodeState source)
bool unsubscribe,
NodeState source)
{
MonitoredNode monitoredNode = source.Handle as MonitoredNode;
@@ -2127,7 +2136,7 @@ namespace Opc.Ua.Server
/// The node manager must create a refresh event for each condition monitored by the subscription.
/// </remarks>
public virtual ServiceResult ConditionRefresh(
OperationContext context,
OperationContext context,
IList<IEventMonitoredItem> monitoredItems)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
@@ -2162,7 +2171,7 @@ namespace Opc.Ua.Server
// check for subscription to local node.
else
{
NodeState source = IsHandleInNamespace(monitoredItem.ManagerHandle );
NodeState source = IsHandleInNamespace(monitoredItem.ManagerHandle);
if (source == null)
{
@@ -2191,18 +2200,18 @@ namespace Opc.Ua.Server
/// This method only handles data change subscriptions. Event subscriptions are created by the SDK.
/// </remarks>
public virtual void CreateMonitoredItems(
OperationContext context,
uint subscriptionId,
double publishingInterval,
TimestampsToReturn timestampsToReturn,
OperationContext context,
uint subscriptionId,
double publishingInterval,
TimestampsToReturn timestampsToReturn,
IList<MonitoredItemCreateRequest> itemsToCreate,
IList<ServiceResult> errors,
IList<MonitoringFilterResult> filterErrors,
IList<IMonitoredItem> monitoredItems,
ref long globalIdCounter)
IList<ServiceResult> errors,
IList<MonitoringFilterResult> filterErrors,
IList<IMonitoredItem> monitoredItems,
ref long globalIdCounter)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
IDictionary<NodeId, NodeState> operationCache = new NodeIdDictionary<NodeState>();
List<ReadWriteOperationState> nodesToValidate = new List<ReadWriteOperationState>();
lock (Lock)
@@ -2321,6 +2330,38 @@ namespace Opc.Ua.Server
}
}
/// <summary>
/// Reads the initial value for a monitored item.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="node">The monitored node.</param>
/// <param name="monitoredItem">The monitored item.</param>
/// <param name="ignoreFilters">If the filters should be ignored.</param>
protected virtual ServiceResult ReadInitialValue(
ISystemContext context,
MonitoredNode node,
IDataChangeMonitoredItem2 monitoredItem,
bool ignoreFilters)
{
DataValue initialValue = new DataValue {
Value = null,
ServerTimestamp = DateTime.UtcNow,
SourceTimestamp = DateTime.MinValue,
StatusCode = StatusCodes.BadWaitingForInitialData
};
ServiceResult error = node.Node.ReadAttribute(
context,
monitoredItem.AttributeId,
monitoredItem.IndexRange,
monitoredItem.DataEncoding,
initialValue);
monitoredItem.QueueValue(initialValue, error, ignoreFilters);
return error;
}
/// <summary>
/// Validates a data change filter provided by the client.
/// </summary>
@@ -2383,7 +2424,7 @@ namespace Opc.Ua.Server
return error;
}
if (filter.DeadbandType ==(uint)DeadbandType.Percent)
if (filter.DeadbandType == (uint)DeadbandType.Percent)
{
BaseVariableState euRange = variable.FindChild(context, BrowseNames.EURange) as BaseVariableState;
@@ -2427,12 +2468,12 @@ namespace Opc.Ua.Server
ServiceResult error = null;
// read initial value.
DataValue initialValue = new DataValue();
initialValue.Value = null;
initialValue.ServerTimestamp = DateTime.UtcNow;
initialValue.SourceTimestamp = DateTime.MinValue;
initialValue.StatusCode = StatusCodes.Good;
DataValue initialValue = new DataValue {
Value = null,
ServerTimestamp = DateTime.UtcNow,
SourceTimestamp = DateTime.MinValue,
StatusCode = StatusCodes.BadWaitingForInitialData
};
error = source.ReadAttribute(
context,
@@ -2443,7 +2484,15 @@ namespace Opc.Ua.Server
if (ServiceResult.IsBad(error))
{
return error;
if (error.StatusCode == StatusCodes.BadAttributeIdInvalid ||
error.StatusCode == StatusCodes.BadDataEncodingInvalid ||
error.StatusCode == StatusCodes.BadDataEncodingUnsupported)
{
return error;
}
initialValue.StatusCode = error.StatusCode;
error = ServiceResult.Good;
}
// validate parameters.
@@ -2526,7 +2575,7 @@ namespace Opc.Ua.Server
}
// report the initial value.
datachangeItem.QueueValue(initialValue, null);
datachangeItem.QueueValue(initialValue, null, true);
// do any post processing.
OnCreateMonitoredItem(context, itemToCreate, monitoredNode, datachangeItem);
@@ -2549,7 +2598,7 @@ namespace Opc.Ua.Server
if ((samplingInterval % m_minimumSamplingInterval) != 0)
{
samplingInterval = Math.Truncate(samplingInterval/m_minimumSamplingInterval);
samplingInterval = Math.Truncate(samplingInterval / m_minimumSamplingInterval);
samplingInterval += 1;
samplingInterval *= m_minimumSamplingInterval;
}
@@ -2616,7 +2665,7 @@ namespace Opc.Ua.Server
}
catch (Exception e)
{
Utils.Trace(e, "Unexpected error during diagnostics scan.");
Utils.LogError(e, "Unexpected error during diagnostics scan.");
}
}
@@ -2636,12 +2685,12 @@ namespace Opc.Ua.Server
/// Modifies the parameters for a set of monitored items.
/// </summary>
public virtual void ModifyMonitoredItems(
OperationContext context,
TimestampsToReturn timestampsToReturn,
IList<IMonitoredItem> monitoredItems,
OperationContext context,
TimestampsToReturn timestampsToReturn,
IList<IMonitoredItem> monitoredItems,
IList<MonitoredItemModifyRequest> itemsToModify,
IList<ServiceResult> errors,
IList<MonitoringFilterResult> filterErrors)
IList<ServiceResult> errors,
IList<MonitoringFilterResult> filterErrors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
@@ -2652,7 +2701,7 @@ namespace Opc.Ua.Server
MonitoredItemModifyRequest itemToModify = itemsToModify[ii];
// skip items that have already been processed.
if (itemToModify.Processed || monitoredItems[ii] == null)
if (itemToModify.Processed)
{
continue;
}
@@ -2784,9 +2833,9 @@ namespace Opc.Ua.Server
/// Deletes a set of monitored items.
/// </summary>
public virtual void DeleteMonitoredItems(
OperationContext context,
OperationContext context,
IList<IMonitoredItem> monitoredItems,
IList<bool> processedItems,
IList<bool> processedItems,
IList<ServiceResult> errors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
@@ -2796,7 +2845,7 @@ namespace Opc.Ua.Server
for (int ii = 0; ii < monitoredItems.Count; ii++)
{
// skip items that have already been processed.
if (processedItems[ii] || monitoredItems[ii] == null)
if (processedItems[ii])
{
continue;
}
@@ -2878,14 +2927,86 @@ namespace Opc.Ua.Server
// does nothing.
}
/// <summary>
/// Transfers a set of monitored items.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="sendInitialValues">Whether the subscription should send initial values after transfer.</param>
/// <param name="monitoredItems">The set of monitoring items to update.</param>
/// <param name="processedItems">The list of bool with items that were already processed.</param>
/// <param name="errors">Any errors.</param>
public virtual void TransferMonitoredItems(
OperationContext context,
bool sendInitialValues,
IList<IMonitoredItem> monitoredItems,
IList<bool> processedItems,
IList<ServiceResult> errors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
IList<IMonitoredItem> transferredItems = new List<IMonitoredItem>();
lock (Lock)
{
for (int ii = 0; ii < monitoredItems.Count; ii++)
{
// skip items that have already been processed.
if (processedItems[ii] || monitoredItems[ii] == null)
{
continue;
}
// check handle.
// check for valid handle.
MonitoredNode monitoredNode = monitoredItems[ii].ManagerHandle as MonitoredNode;
if (monitoredNode == null)
{
continue;
}
// owned by this node manager.
processedItems[ii] = true;
var monitoredItem = monitoredItems[ii];
transferredItems.Add(monitoredItem);
if (sendInitialValues && !monitoredItem.IsReadyToPublish)
{
if (monitoredItem is DataChangeMonitoredItem dataChangeMonitoredItem)
{
errors[ii] = ReadInitialValue(systemContext, monitoredNode, dataChangeMonitoredItem, true);
}
}
else
{
errors[ii] = StatusCodes.Good;
}
}
}
// do any post processing.
OnMonitoredItemsTransferred(systemContext, transferredItems);
}
/// <summary>
/// Called after transfer of MonitoredItems.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="monitoredItems">The transferred monitored items.</param>
protected virtual void OnMonitoredItemsTransferred(
ServerSystemContext context,
IList<IMonitoredItem> monitoredItems
)
{
// does nothing.
}
/// <summary>
/// Changes the monitoring mode for a set of monitored items.
/// </summary>
public virtual void SetMonitoringMode(
OperationContext context,
MonitoringMode monitoringMode,
OperationContext context,
MonitoringMode monitoringMode,
IList<IMonitoredItem> monitoredItems,
IList<bool> processedItems,
IList<bool> processedItems,
IList<ServiceResult> errors)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);
@@ -2895,7 +3016,7 @@ namespace Opc.Ua.Server
for (int ii = 0; ii < monitoredItems.Count; ii++)
{
// skip items that have already been processed.
if (processedItems[ii] || monitoredItems[ii] == null)
if (processedItems[ii])
{
continue;
}
@@ -2951,21 +3072,7 @@ namespace Opc.Ua.Server
// need to provide an immediate update after enabling.
if (previousMode == MonitoringMode.Disabled && monitoringMode != MonitoringMode.Disabled)
{
DataValue initialValue = new DataValue();
initialValue.Value = null;
initialValue.ServerTimestamp = DateTime.UtcNow;
initialValue.SourceTimestamp = DateTime.MinValue;
initialValue.StatusCode = StatusCodes.Good;
ServiceResult error = monitoredNode.Node.ReadAttribute(
context,
datachangeItem.AttributeId,
datachangeItem.IndexRange,
datachangeItem.DataEncoding,
initialValue);
datachangeItem.QueueValue(initialValue, error);
ReadInitialValue(context, monitoredNode, datachangeItem, false);
}
// do any post processing.
@@ -0,0 +1,101 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using Opc.Ua;
namespace TestData
{
public partial class AnalogArrayValueObjectState
{
#region Initialization
/// <summary>
/// Initializes the object as a collection of counters which change value on read.
/// </summary>
protected override void OnAfterCreate(ISystemContext context, NodeState node)
{
base.OnAfterCreate(context, node);
InitializeVariable(context, SByteValue, TestData.Variables.AnalogArrayValueObjectType_SByteValue);
InitializeVariable(context, ByteValue, TestData.Variables.AnalogArrayValueObjectType_ByteValue);
InitializeVariable(context, Int16Value, TestData.Variables.AnalogArrayValueObjectType_Int16Value);
InitializeVariable(context, UInt16Value, TestData.Variables.AnalogArrayValueObjectType_UInt16Value);
InitializeVariable(context, Int32Value, TestData.Variables.AnalogArrayValueObjectType_Int32Value);
InitializeVariable(context, UInt32Value, TestData.Variables.AnalogArrayValueObjectType_UInt32Value);
InitializeVariable(context, Int64Value, TestData.Variables.AnalogArrayValueObjectType_Int64Value);
InitializeVariable(context, UInt64Value, TestData.Variables.AnalogArrayValueObjectType_UInt64Value);
InitializeVariable(context, FloatValue, TestData.Variables.AnalogArrayValueObjectType_FloatValue);
InitializeVariable(context, DoubleValue, TestData.Variables.AnalogArrayValueObjectType_DoubleValue);
InitializeVariable(context, NumberValue, TestData.Variables.AnalogArrayValueObjectType_NumberValue);
InitializeVariable(context, IntegerValue, TestData.Variables.AnalogArrayValueObjectType_IntegerValue);
InitializeVariable(context, UIntegerValue, TestData.Variables.AnalogArrayValueObjectType_UIntegerValue);
}
#endregion
#region Protected Methods
/// <summary>
/// Handles the generate values method.
/// </summary>
protected override ServiceResult OnGenerateValues(
ISystemContext context,
MethodState method,
NodeId objectId,
uint count)
{
TestDataSystem system = context.SystemHandle as TestDataSystem;
if (system == null)
{
return StatusCodes.BadOutOfService;
}
GenerateValue(system, SByteValue);
GenerateValue(system, ByteValue);
GenerateValue(system, Int16Value);
GenerateValue(system, UInt16Value);
GenerateValue(system, Int32Value);
GenerateValue(system, UInt32Value);
GenerateValue(system, UInt32Value);
GenerateValue(system, Int64Value);
GenerateValue(system, UInt64Value);
GenerateValue(system, FloatValue);
GenerateValue(system, DoubleValue);
GenerateValue(system, NumberValue);
GenerateValue(system, IntegerValue);
GenerateValue(system, UIntegerValue);
return base.OnGenerateValues(context, method, objectId, count);
}
#endregion
}
}
@@ -0,0 +1,101 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using Opc.Ua;
namespace TestData
{
public partial class AnalogScalarValueObjectState
{
#region Initialization
/// <summary>
/// Initializes the object as a collection of counters which change value on read.
/// </summary>
protected override void OnAfterCreate(ISystemContext context, NodeState node)
{
base.OnAfterCreate(context, node);
InitializeVariable(context, SByteValue, TestData.Variables.AnalogScalarValueObjectType_SByteValue);
InitializeVariable(context, ByteValue, TestData.Variables.AnalogScalarValueObjectType_ByteValue);
InitializeVariable(context, Int16Value, TestData.Variables.AnalogScalarValueObjectType_Int16Value);
InitializeVariable(context, UInt16Value, TestData.Variables.AnalogScalarValueObjectType_UInt16Value);
InitializeVariable(context, Int32Value, TestData.Variables.AnalogScalarValueObjectType_Int32Value);
InitializeVariable(context, UInt32Value, TestData.Variables.AnalogScalarValueObjectType_UInt32Value);
InitializeVariable(context, Int64Value, TestData.Variables.AnalogScalarValueObjectType_Int64Value);
InitializeVariable(context, UInt64Value, TestData.Variables.AnalogScalarValueObjectType_UInt64Value);
InitializeVariable(context, FloatValue, TestData.Variables.AnalogScalarValueObjectType_FloatValue);
InitializeVariable(context, DoubleValue, TestData.Variables.AnalogScalarValueObjectType_DoubleValue);
InitializeVariable(context, NumberValue, TestData.Variables.AnalogScalarValueObjectType_NumberValue);
InitializeVariable(context, IntegerValue, TestData.Variables.AnalogScalarValueObjectType_IntegerValue);
InitializeVariable(context, UIntegerValue, TestData.Variables.AnalogScalarValueObjectType_UIntegerValue);
}
#endregion
#region Protected Methods
/// <summary>
/// Handles the generate values method.
/// </summary>
protected override ServiceResult OnGenerateValues(
ISystemContext context,
MethodState method,
NodeId objectId,
uint count)
{
TestDataSystem system = context.SystemHandle as TestDataSystem;
if (system == null)
{
return StatusCodes.BadOutOfService;
}
GenerateValue(system, SByteValue);
GenerateValue(system, ByteValue);
GenerateValue(system, Int16Value);
GenerateValue(system, UInt16Value);
GenerateValue(system, Int32Value);
GenerateValue(system, UInt32Value);
GenerateValue(system, UInt32Value);
GenerateValue(system, Int64Value);
GenerateValue(system, UInt64Value);
GenerateValue(system, FloatValue);
GenerateValue(system, DoubleValue);
GenerateValue(system, NumberValue);
GenerateValue(system, IntegerValue);
GenerateValue(system, UIntegerValue);
return base.OnGenerateValues(context, method, objectId, count);
}
#endregion
}
}
@@ -0,0 +1,129 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using Opc.Ua;
namespace TestData
{
public partial class ArrayValueObjectState
{
#region Initialization
/// <summary>
/// Initializes the object as a collection of counters which change value on read.
/// </summary>
protected override void OnAfterCreate(ISystemContext context, NodeState node)
{
base.OnAfterCreate(context, node);
InitializeVariable(context, BooleanValue, TestData.Variables.ArrayValueObjectType_BooleanValue);
InitializeVariable(context, SByteValue, TestData.Variables.ArrayValueObjectType_SByteValue);
InitializeVariable(context, ByteValue, TestData.Variables.ArrayValueObjectType_ByteValue);
InitializeVariable(context, Int16Value, TestData.Variables.ArrayValueObjectType_Int16Value);
InitializeVariable(context, UInt16Value, TestData.Variables.ArrayValueObjectType_UInt16Value);
InitializeVariable(context, Int32Value, TestData.Variables.ArrayValueObjectType_Int32Value);
InitializeVariable(context, UInt32Value, TestData.Variables.ArrayValueObjectType_UInt32Value);
InitializeVariable(context, Int64Value, TestData.Variables.ArrayValueObjectType_Int64Value);
InitializeVariable(context, UInt64Value, TestData.Variables.ArrayValueObjectType_UInt64Value);
InitializeVariable(context, FloatValue, TestData.Variables.ArrayValueObjectType_FloatValue);
InitializeVariable(context, DoubleValue, TestData.Variables.ArrayValueObjectType_DoubleValue);
InitializeVariable(context, StringValue, TestData.Variables.ArrayValueObjectType_StringValue);
InitializeVariable(context, DateTimeValue, TestData.Variables.ArrayValueObjectType_DateTimeValue);
InitializeVariable(context, GuidValue, TestData.Variables.ArrayValueObjectType_GuidValue);
InitializeVariable(context, ByteStringValue, TestData.Variables.ArrayValueObjectType_ByteStringValue);
InitializeVariable(context, XmlElementValue, TestData.Variables.ArrayValueObjectType_XmlElementValue);
InitializeVariable(context, NodeIdValue, TestData.Variables.ArrayValueObjectType_NodeIdValue);
InitializeVariable(context, ExpandedNodeIdValue, TestData.Variables.ArrayValueObjectType_ExpandedNodeIdValue);
InitializeVariable(context, QualifiedNameValue, TestData.Variables.ArrayValueObjectType_QualifiedNameValue);
InitializeVariable(context, LocalizedTextValue, TestData.Variables.ArrayValueObjectType_LocalizedTextValue);
InitializeVariable(context, StatusCodeValue, TestData.Variables.ArrayValueObjectType_StatusCodeValue);
InitializeVariable(context, VariantValue, TestData.Variables.ArrayValueObjectType_VariantValue);
InitializeVariable(context, EnumerationValue, TestData.Variables.ArrayValueObjectType_EnumerationValue);
InitializeVariable(context, StructureValue, TestData.Variables.ArrayValueObjectType_StructureValue);
InitializeVariable(context, NumberValue, TestData.Variables.ArrayValueObjectType_NumberValue);
InitializeVariable(context, IntegerValue, TestData.Variables.ArrayValueObjectType_IntegerValue);
InitializeVariable(context, UIntegerValue, TestData.Variables.ArrayValueObjectType_UIntegerValue);
}
#endregion
#region Protected Methods
/// <summary>
/// Handles the generate values method.
/// </summary>
protected override ServiceResult OnGenerateValues(
ISystemContext context,
MethodState method,
NodeId objectId,
uint count)
{
TestDataSystem system = context.SystemHandle as TestDataSystem;
if (system == null)
{
return StatusCodes.BadOutOfService;
}
GenerateValue(system, BooleanValue);
GenerateValue(system, SByteValue);
GenerateValue(system, ByteValue);
GenerateValue(system, Int16Value);
GenerateValue(system, UInt16Value);
GenerateValue(system, Int32Value);
GenerateValue(system, UInt32Value);
GenerateValue(system, UInt32Value);
GenerateValue(system, Int64Value);
GenerateValue(system, UInt64Value);
GenerateValue(system, FloatValue);
GenerateValue(system, DoubleValue);
GenerateValue(system, StringValue);
GenerateValue(system, DateTimeValue);
GenerateValue(system, GuidValue);
GenerateValue(system, ByteStringValue);
GenerateValue(system, XmlElementValue);
GenerateValue(system, NodeIdValue);
GenerateValue(system, ExpandedNodeIdValue);
GenerateValue(system, QualifiedNameValue);
GenerateValue(system, LocalizedTextValue);
GenerateValue(system, StatusCodeValue);
GenerateValue(system, VariantValue);
GenerateValue(system, EnumerationValue);
GenerateValue(system, StructureValue);
GenerateValue(system, NumberValue);
GenerateValue(system, IntegerValue);
GenerateValue(system, UIntegerValue);
return base.OnGenerateValues(context, method, objectId, count);
}
#endregion
}
}
@@ -0,0 +1,221 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Xml;
using System.IO;
using Opc.Ua;
namespace TestData
{
/// <summary>
/// A class that provides access to archived data.
/// </summary>
internal class HistoryArchive : IDisposable
{
#region IDisposable Members
/// <summary>
/// Frees any unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
}
/// <summary>
/// An overrideable version of the Dispose.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (m_updateTimer != null)
{
m_updateTimer.Dispose();
m_updateTimer = null;
}
}
}
#endregion
#region Public Interface
/// <summary>
/// Returns an object that can be used to browse the archive.
/// </summary>
public HistoryFile GetHistoryFile(NodeId nodeId)
{
lock (m_lock)
{
if (m_records == null)
{
return null;
}
HistoryRecord record = null;
if (!m_records.TryGetValue(nodeId, out record))
{
return null;
}
return new HistoryFile(m_lock, record.RawData);
}
}
/// <summary>
/// Creates a new record in the archive.
/// </summary>
public void CreateRecord(NodeId nodeId, BuiltInType dataType)
{
lock (m_lock)
{
HistoryRecord record = new HistoryRecord();
record.RawData = new List<HistoryEntry>();
record.Historizing = true;
record.DataType = dataType;
DateTime now = DateTime.UtcNow;
for (int ii = 1000; ii >= 0; ii--)
{
HistoryEntry entry = new HistoryEntry();
entry.Value = new DataValue();
entry.Value.ServerTimestamp = now.AddSeconds(-(ii*10));
entry.Value.SourceTimestamp = entry.Value.ServerTimestamp.AddMilliseconds(1234);
entry.IsModified = false;
switch (dataType)
{
case BuiltInType.Int32:
{
entry.Value.Value = ii;
break;
}
}
record.RawData.Add(entry);
}
if (m_records == null)
{
m_records = new Dictionary<NodeId,HistoryRecord>();
}
m_records[nodeId] = record;
if (m_updateTimer == null)
{
m_updateTimer = new Timer(OnUpdate, null, 10000, 10000);
}
}
}
#endregion
#region Private Methods
/// <summary>
/// Periodically adds new values into the archive.
/// </summary>
private void OnUpdate(object state)
{
try
{
DateTime now = DateTime.UtcNow;
lock (m_lock)
{
foreach (HistoryRecord record in m_records.Values)
{
if (!record.Historizing || record.RawData.Count >= 2000)
{
continue;
}
HistoryEntry entry = new HistoryEntry();
entry.Value = new DataValue();
entry.Value.ServerTimestamp = now;
entry.Value.SourceTimestamp = entry.Value.ServerTimestamp.AddMilliseconds(-4567);
entry.IsModified = false;
switch (record.DataType)
{
case BuiltInType.Int32:
{
int lastValue = (int)record.RawData[record.RawData.Count-1].Value.Value;
entry.Value.Value = lastValue+1;
break;
}
}
record.RawData.Add(entry);
}
}
}
catch (Exception e)
{
Utils.LogError(e, "Unexpected error updating history.");
}
}
#endregion
#region Private Fields
private object m_lock = new object();
private Timer m_updateTimer;
private Dictionary<NodeId,HistoryRecord> m_records;
#endregion
}
#region HistoryEntry Class
/// <summary>
/// A single entry in the archive.
/// </summary>
internal class HistoryEntry
{
public DataValue Value;
public bool IsModified;
}
#endregion
#region HistoryRecord Class
/// <summary>
/// A record in the archive.
/// </summary>
internal class HistoryRecord
{
public List<HistoryEntry> RawData;
public bool Historizing;
public BuiltInType DataType;
}
#endregion
}
@@ -0,0 +1,272 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Globalization;
using Opc.Ua;
using Opc.Ua.Server;
namespace TestData
{
/// <summary>
/// A class used to read values from a history data source.
/// </summary>
public class HistoryDataReader : IDisposable
{
#region Constructors
/// <summary>
/// Constructs a reader for the source.
/// </summary>
/// <param name="source">The source of the history data.</param>
public HistoryDataReader(NodeId variableId, IHistoryDataSource source)
{
m_id = Guid.NewGuid();
m_variableId = variableId;
m_source = source;
}
#endregion
#region IDisposable Members
/// <summary>
/// Frees any unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
}
/// <summary>
/// An overrideable version of the Dispose.
/// </summary>
protected virtual void Dispose(bool disposing)
{
// nothing to do.
}
#endregion
#region Public Interface
/// <summary>
/// A globally unique identifier for the instance.
/// </summary>
public Guid Id
{
get { return m_id; }
}
/// <summary>
/// The identifier for the variable being read.
/// </summary>
public NodeId VariableId
{
get { return m_variableId; }
}
/// <summary>
/// Starts reading raw values.
/// </summary>
/// <param name="context">The context for the operation.</param>
/// <param name="request">The request parameters.</param>
/// <param name="timestampsToReturn">The timestamps to return with the value.</param>
/// <param name="indexRange">The range to return for array values.</param>
/// <param name="dataEncoding">The data encoding to use for structured values.</param>
/// <param name="values">The values to return.</param>
public void BeginReadRaw(
ServerSystemContext context,
ReadRawModifiedDetails request,
TimestampsToReturn timestampsToReturn,
NumericRange indexRange,
QualifiedName dataEncoding,
DataValueCollection values)
{
m_request = request;
// initialize start and end.
m_startTime = m_request.StartTime;
m_endTime = m_request.EndTime;
if (m_endTime == DateTime.MinValue)
{
m_endTime = DateTime.MaxValue;
}
// check the direction.
m_isForward = m_startTime < m_endTime;
m_position = -1;
DataValue value = null;
// get first bound.
if (m_request.ReturnBounds)
{
value = m_source.FirstRaw(m_startTime, !m_isForward, m_request.IsReadModified, out m_position);
if (value != null)
{
AddValue(timestampsToReturn, indexRange, dataEncoding, values, value);
}
}
}
/// <summary>
/// Continues a read raw operation.
/// </summary>
/// <param name="context">The context for the operation.</param>
/// <param name="timestampsToReturn">The timestamps to return with the value.</param>
/// <param name="indexRange">The range to return for array values.</param>
/// <param name="dataEncoding">The data encoding to use for structured values.</param>
/// <param name="values">The values to return.</param>
/// <returns>False if the operation halted because the maximum number of values was discovered.</returns>
public bool NextReadRaw(
ServerSystemContext context,
TimestampsToReturn timestampsToReturn,
NumericRange indexRange,
QualifiedName dataEncoding,
DataValueCollection values)
{
DataValue value = null;
do
{
// check for limit.
if (m_request.NumValuesPerNode > 0 && values.Count >= m_request.NumValuesPerNode)
{
return false;
}
value = m_source.NextRaw(m_lastTime, m_isForward, m_request.IsReadModified, ref m_position);
// no more data.
if (value == null)
{
return true;
}
// check for bound.
if ((m_isForward && value.ServerTimestamp >= m_endTime) || (!m_isForward && value.ServerTimestamp <= m_endTime))
{
if (m_request.ReturnBounds)
{
AddValue(timestampsToReturn, indexRange, dataEncoding, values, value);
return true;
}
}
// add value.
AddValue(timestampsToReturn, indexRange, dataEncoding, values, value);
}
while (value != null);
return true;
}
#endregion
#region Private Methods
/// <summary>
/// Adds a DataValue to a list of values to return.
/// </summary>
private void AddValue(
TimestampsToReturn timestampsToReturn,
NumericRange indexRange,
QualifiedName dataEncoding,
DataValueCollection values,
DataValue value)
{
// ignore invalid case.
if (value == null)
{
return;
}
// save the last timestamp returned.
m_lastTime = value.ServerTimestamp;
// check if the index range or data encoding can be applied.
if (StatusCode.IsGood(value.StatusCode))
{
object valueToReturn = value.Value;
// apply the index range.
if (indexRange != NumericRange.Empty)
{
StatusCode error = indexRange.ApplyRange(ref valueToReturn);
if (StatusCode.IsBad(error))
{
value.Value = null;
value.StatusCode = error;
}
else
{
value.Value = valueToReturn;
}
}
// apply the data encoding.
if (!QualifiedName.IsNull(dataEncoding))
{
value.Value = null;
value.StatusCode = StatusCodes.BadDataEncodingUnsupported;
}
}
// apply the timestamps filter.
if (timestampsToReturn == TimestampsToReturn.Neither || timestampsToReturn == TimestampsToReturn.Server)
{
value.SourceTimestamp = DateTime.MinValue;
}
if (timestampsToReturn == TimestampsToReturn.Neither || timestampsToReturn == TimestampsToReturn.Source)
{
value.ServerTimestamp = DateTime.MinValue;
}
// add result.
values.Add(value);
}
#endregion
#region Private Fields
private Guid m_id;
private NodeId m_variableId;
private IHistoryDataSource m_source;
private ReadRawModifiedDetails m_request;
private DateTime m_startTime;
private DateTime m_endTime;
private bool m_isForward;
private int m_position;
private DateTime m_lastTime;
#endregion
}
}
@@ -0,0 +1,153 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Globalization;
using Opc.Ua;
using Opc.Ua.Server;
namespace TestData
{
/// <summary>
/// Wraps a file which contains a list of historical values.
/// </summary>
internal class HistoryFile : IHistoryDataSource
{
#region Constructors
/// <summary>
/// Creates a new file.
/// </summary>
internal HistoryFile(object dataLock, List<HistoryEntry> entries)
{
m_lock = dataLock;
m_entries = entries;
}
#endregion
#region IHistoryReader Members
/// <summary>
/// Returns the next value in the archive.
/// </summary>
/// <param name="startTime">The starting time for the search.</param>
/// <param name="isForward">Whether to search forward in time.</param>
/// <param name="isReadModified">Whether to return modified data.</param>
/// <param name="position">A index that must be passed to the NextRaw call. </param>
/// <returns>The DataValue.</returns>
public DataValue FirstRaw(DateTime startTime, bool isForward, bool isReadModified, out int position)
{
position = -1;
lock (m_lock)
{
if (isForward)
{
for (int ii = 0; ii < m_entries.Count; ii++)
{
if (m_entries[ii].Value.ServerTimestamp >= startTime)
{
position = ii;
break;
}
}
}
else
{
for (int ii = m_entries.Count-1; ii >= 0; ii--)
{
if (m_entries[ii].Value.ServerTimestamp <= startTime)
{
position = ii;
break;
}
}
}
if (position < 0 || position >= m_entries.Count)
{
return null;
}
HistoryEntry entry = m_entries[position];
DataValue value = new DataValue();
value.Value = entry.Value.Value;
value.ServerTimestamp = entry.Value.ServerTimestamp;
value.SourceTimestamp = entry.Value.SourceTimestamp;
value.StatusCode = entry.Value.StatusCode;
return value;
}
}
/// <summary>
/// Returns the next value in the archive.
/// </summary>
/// <param name="lastTime">The timestamp of the last value returned.</param>
/// <param name="isForward">Whether to search forward in time.</param>
/// <param name="isReadModified">Whether to return modified data.</param>
/// <param name="position">A index previously returned by the reader.</param>
/// <returns>The DataValue.</returns>
public DataValue NextRaw(DateTime lastTime, bool isForward, bool isReadModified, ref int position)
{
position++;
lock (m_lock)
{
if (position < 0 || position >= m_entries.Count)
{
return null;
}
HistoryEntry entry = m_entries[position];
DataValue value = new DataValue();
value.Value = entry.Value.Value;
value.ServerTimestamp = entry.Value.ServerTimestamp;
value.SourceTimestamp = entry.Value.SourceTimestamp;
value.StatusCode = entry.Value.StatusCode;
return value;
}
}
#endregion
#region Private Fields
private object m_lock = new object();
private List<HistoryEntry> m_entries;
#endregion
}
}
@@ -0,0 +1,68 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Globalization;
using Opc.Ua;
using Opc.Ua.Server;
namespace TestData
{
/// <summary>
/// An interface to an object which can access historical data for a variable.
/// </summary>
public interface IHistoryDataSource
{
/// <summary>
/// Returns the next value in the archive.
/// </summary>
/// <param name="startTime">The starting time for the search.</param>
/// <param name="isForward">Whether to search forward in time.</param>
/// <param name="isReadModified">Whether to return modified data.</param>
/// <param name="position">A index that must be passed to the NextRaw call. </param>
/// <returns>The DataValue.</returns>
DataValue FirstRaw(DateTime startTime, bool isForward, bool isReadModified, out int position);
/// <summary>
/// Returns the next value in the archive.
/// </summary>
/// <param name="lastTime">The timestamp of the last value returned.</param>
/// <param name="isForward">Whether to search forward in time.</param>
/// <param name="isReadModified">Whether to return modified data.</param>
/// <param name="position">A index previously returned by the reader.</param>
/// <returns>The DataValue.</returns>
DataValue NextRaw(DateTime lastTime, bool isForward, bool isReadModified, ref int position);
}
}
@@ -0,0 +1,428 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using Opc.Ua;
namespace TestData
{
public partial class MethodTestState
{
#region Initialization
/// <summary>
/// Initializes the object as a collection of counters which change value on read.
/// </summary>
protected override void OnAfterCreate(ISystemContext context, NodeState node)
{
base.OnAfterCreate(context, node);
this.ScalarMethod1.OnCall = OnScalarValue1;
this.ScalarMethod2.OnCall = OnScalarValue2;
this.ScalarMethod3.OnCall = OnScalarValue3;
this.ArrayMethod1.OnCall = OnArrayValue1;
this.ArrayMethod2.OnCall = OnArrayValue2;
this.ArrayMethod3.OnCall = OnArrayValue3;
this.UserScalarMethod1.OnCall = OnUserScalarValue1;
this.UserScalarMethod2.OnCall = OnUserScalarValue2;
this.UserArrayMethod1.OnCall = OnUserArrayValue1;
this.UserArrayMethod2.OnCall = OnUserArrayValue2;
}
#endregion
private ServiceResult OnScalarValue1(
ISystemContext context,
MethodState method,
NodeId objectId,
bool booleanIn,
sbyte sByteIn,
byte byteIn,
short int16In,
ushort uInt16In,
int int32In,
uint uInt32In,
long int64In,
ulong uInt64In,
float floatIn,
double doubleIn,
ref bool booleanOut,
ref sbyte sByteOut,
ref byte byteOut,
ref short int16Out,
ref ushort uInt16Out,
ref int int32Out,
ref uint uInt32Out,
ref long int64Out,
ref ulong uInt64Out,
ref float floatOut,
ref double doubleOut)
{
booleanOut = booleanIn;
sByteOut = sByteIn;
byteOut = byteIn;
int16Out = int16In;
uInt16Out = uInt16In;
int32Out = int32In;
uInt32Out = uInt32In;
int64Out = int64In;
uInt64Out = uInt64In;
floatOut = floatIn;
doubleOut = doubleIn;
return ServiceResult.Good;
}
private ServiceResult OnScalarValue2(
ISystemContext context,
MethodState method,
NodeId objectId,
string stringIn,
DateTime dateTimeIn,
Uuid guidIn,
byte[] byteStringIn,
XmlElement xmlElementIn,
NodeId nodeIdIn,
ExpandedNodeId expandedNodeIdIn,
QualifiedName qualifiedNameIn,
LocalizedText localizedTextIn,
StatusCode statusCodeIn,
ref string stringOut,
ref DateTime dateTimeOut,
ref Uuid guidOut,
ref byte[] byteStringOut,
ref XmlElement xmlElementOut,
ref NodeId nodeIdOut,
ref ExpandedNodeId expandedNodeIdOut,
ref QualifiedName qualifiedNameOut,
ref LocalizedText localizedTextOut,
ref StatusCode statusCodeOut)
{
stringOut = stringIn;
dateTimeOut = dateTimeIn;
guidOut = guidIn;
byteStringOut = byteStringIn;
xmlElementOut = xmlElementIn;
nodeIdOut = nodeIdIn;
expandedNodeIdOut = expandedNodeIdIn;
qualifiedNameOut = qualifiedNameIn;
localizedTextOut = localizedTextIn;
statusCodeOut = statusCodeIn;
return ServiceResult.Good;
}
private ServiceResult OnScalarValue3(
ISystemContext context,
MethodState method,
NodeId objectId,
object variantIn,
int enumerationIn,
ExtensionObject structureIn,
ref object variantOut,
ref int enumerationOut,
ref ExtensionObject structureOut)
{
variantOut = variantIn;
enumerationOut = enumerationIn;
structureOut = structureIn;
return ServiceResult.Good;
}
private ServiceResult OnArrayValue1(
ISystemContext context,
MethodState method,
NodeId objectId,
bool[] booleanIn,
sbyte[] sByteIn,
byte[] byteIn,
short[] int16In,
ushort[] uInt16In,
int[] int32In,
uint[] uInt32In,
long[] int64In,
ulong[] uInt64In,
float[] floatIn,
double[] doubleIn,
ref bool[] booleanOut,
ref sbyte[] sByteOut,
ref byte[] byteOut,
ref short[] int16Out,
ref ushort[] uInt16Out,
ref int[] int32Out,
ref uint[] uInt32Out,
ref long[] int64Out,
ref ulong[] uInt64Out,
ref float[] floatOut,
ref double[] doubleOut)
{
booleanOut = booleanIn;
sByteOut = sByteIn;
byteOut = byteIn;
int16Out = int16In;
uInt16Out = uInt16In;
int32Out = int32In;
uInt32Out = uInt32In;
int64Out = int64In;
uInt64Out = uInt64In;
floatOut = floatIn;
doubleOut = doubleIn;
return ServiceResult.Good;
}
private ServiceResult OnArrayValue2(
ISystemContext context,
MethodState method,
NodeId objectId,
string[] stringIn,
DateTime[] dateTimeIn,
Uuid[] guidIn,
byte[][] byteStringIn,
XmlElement[] xmlElementIn,
NodeId[] nodeIdIn,
ExpandedNodeId[] expandedNodeIdIn,
QualifiedName[] qualifiedNameIn,
LocalizedText[] localizedTextIn,
StatusCode[] statusCodeIn,
ref string[] stringOut,
ref DateTime[] dateTimeOut,
ref Uuid[] guidOut,
ref byte[][] byteStringOut,
ref XmlElement[] xmlElementOut,
ref NodeId[] nodeIdOut,
ref ExpandedNodeId[] expandedNodeIdOut,
ref QualifiedName[] qualifiedNameOut,
ref LocalizedText[] localizedTextOut,
ref StatusCode[] statusCodeOut)
{
stringOut = stringIn;
dateTimeOut = dateTimeIn;
guidOut = guidIn;
byteStringOut = byteStringIn;
xmlElementOut = xmlElementIn;
nodeIdOut = nodeIdIn;
expandedNodeIdOut = expandedNodeIdIn;
qualifiedNameOut = qualifiedNameIn;
localizedTextOut = localizedTextIn;
statusCodeOut = statusCodeIn;
return ServiceResult.Good;
}
private ServiceResult OnArrayValue3(
ISystemContext context,
MethodState method,
NodeId objectId,
Variant[] variantIn,
int[] enumerationIn,
ExtensionObject[] structureIn,
ref Variant[] variantOut,
ref int[] enumerationOut,
ref ExtensionObject[] structureOut)
{
variantOut = variantIn;
enumerationOut = enumerationIn;
structureOut = structureIn;
return ServiceResult.Good;
}
private ServiceResult OnUserScalarValue1(
ISystemContext context,
MethodState method,
NodeId objectId,
bool booleanIn,
sbyte sByteIn,
byte byteIn,
short int16In,
ushort uInt16In,
int int32In,
uint uInt32In,
long int64In,
ulong uInt64In,
float floatIn,
double doubleIn,
string stringIn,
ref bool booleanOut,
ref sbyte sByteOut,
ref byte byteOut,
ref short int16Out,
ref ushort uInt16Out,
ref int int32Out,
ref uint uInt32Out,
ref long int64Out,
ref ulong uInt64Out,
ref float floatOut,
ref double doubleOut,
ref string stringOut)
{
booleanOut = booleanIn;
sByteOut = sByteIn;
byteOut = byteIn;
int16Out = int16In;
uInt16Out = uInt16In;
int32Out = int32In;
uInt32Out = uInt32In;
int64Out = int64In;
uInt64Out = uInt64In;
floatOut = floatIn;
doubleOut = doubleIn;
stringOut = stringIn;
return ServiceResult.Good;
}
private ServiceResult OnUserScalarValue2(
ISystemContext context,
MethodState method,
NodeId objectId,
DateTime dateTimeIn,
Uuid guidIn,
byte[] byteStringIn,
XmlElement xmlElementIn,
NodeId nodeIdIn,
ExpandedNodeId expandedNodeIdIn,
QualifiedName qualifiedNameIn,
LocalizedText localizedTextIn,
StatusCode statusCodeIn,
object variantIn,
ref DateTime dateTimeOut,
ref Uuid guidOut,
ref byte[] byteStringOut,
ref XmlElement xmlElementOut,
ref NodeId nodeIdOut,
ref ExpandedNodeId expandedNodeIdOut,
ref QualifiedName qualifiedNameOut,
ref LocalizedText localizedTextOut,
ref StatusCode statusCodeOut,
ref object variantOut)
{
dateTimeOut = dateTimeIn;
guidOut = guidIn;
byteStringOut = byteStringIn;
xmlElementOut = xmlElementIn;
nodeIdOut = nodeIdIn;
expandedNodeIdOut = expandedNodeIdIn;
qualifiedNameOut = qualifiedNameIn;
localizedTextOut = localizedTextIn;
statusCodeOut = statusCodeIn;
variantOut = variantIn;
return ServiceResult.Good;
}
private ServiceResult OnUserArrayValue1(
ISystemContext context,
MethodState method,
NodeId objectId,
bool[] booleanIn,
sbyte[] sByteIn,
byte[] byteIn,
short[] int16In,
ushort[] uInt16In,
int[] int32In,
uint[] uInt32In,
long[] int64In,
ulong[] uInt64In,
float[] floatIn,
double[] doubleIn,
string[] stringIn,
ref bool[] booleanOut,
ref sbyte[] sByteOut,
ref byte[] byteOut,
ref short[] int16Out,
ref ushort[] uInt16Out,
ref int[] int32Out,
ref uint[] uInt32Out,
ref long[] int64Out,
ref ulong[] uInt64Out,
ref float[] floatOut,
ref double[] doubleOut,
ref string[] stringOut)
{
booleanOut = booleanIn;
sByteOut = sByteIn;
byteOut = byteIn;
int16Out = int16In;
uInt16Out = uInt16In;
int32Out = int32In;
uInt32Out = uInt32In;
int64Out = int64In;
uInt64Out = uInt64In;
floatOut = floatIn;
doubleOut = doubleIn;
stringOut = stringIn;
return ServiceResult.Good;
}
private ServiceResult OnUserArrayValue2(
ISystemContext context,
MethodState method,
NodeId objectId,
DateTime[] dateTimeIn,
Uuid[] guidIn,
byte[][] byteStringIn,
XmlElement[] xmlElementIn,
NodeId[] nodeIdIn,
ExpandedNodeId[] expandedNodeIdIn,
QualifiedName[] qualifiedNameIn,
LocalizedText[] localizedTextIn,
StatusCode[] statusCodeIn,
Variant[] variantIn,
ref DateTime[] dateTimeOut,
ref Uuid[] guidOut,
ref byte[][] byteStringOut,
ref XmlElement[] xmlElementOut,
ref NodeId[] nodeIdOut,
ref ExpandedNodeId[] expandedNodeIdOut,
ref QualifiedName[] qualifiedNameOut,
ref LocalizedText[] localizedTextOut,
ref StatusCode[] statusCodeOut,
ref Variant[] variantOut)
{
dateTimeOut = dateTimeIn;
guidOut = guidIn;
byteStringOut = byteStringIn;
xmlElementOut = xmlElementIn;
nodeIdOut = nodeIdIn;
expandedNodeIdOut = expandedNodeIdIn;
qualifiedNameOut = qualifiedNameIn;
localizedTextOut = localizedTextIn;
statusCodeOut = statusCodeIn;
variantOut = variantIn;
return ServiceResult.Good;
}
}
}
@@ -0,0 +1,129 @@
/* ========================================================================
* Copyright (c) 2005-2019 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 System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using Opc.Ua;
namespace TestData
{
public partial class ScalarValueObjectState
{
#region Initialization
/// <summary>
/// Initializes the object as a collection of counters which change value on read.
/// </summary>
protected override void OnAfterCreate(ISystemContext context, NodeState node)
{
base.OnAfterCreate(context, node);
InitializeVariable(context, BooleanValue, TestData.Variables.ScalarValueObjectType_BooleanValue);
InitializeVariable(context, SByteValue, TestData.Variables.ScalarValueObjectType_SByteValue);
InitializeVariable(context, ByteValue, TestData.Variables.ScalarValueObjectType_ByteValue);
InitializeVariable(context, Int16Value, TestData.Variables.ScalarValueObjectType_Int16Value);
InitializeVariable(context, UInt16Value, TestData.Variables.ScalarValueObjectType_UInt16Value);
InitializeVariable(context, Int32Value, TestData.Variables.ScalarValueObjectType_Int32Value);
InitializeVariable(context, UInt32Value, TestData.Variables.ScalarValueObjectType_UInt32Value);
InitializeVariable(context, Int64Value, TestData.Variables.ScalarValueObjectType_Int64Value);
InitializeVariable(context, UInt64Value, TestData.Variables.ScalarValueObjectType_UInt64Value);
InitializeVariable(context, FloatValue, TestData.Variables.ScalarValueObjectType_FloatValue);
InitializeVariable(context, DoubleValue, TestData.Variables.ScalarValueObjectType_DoubleValue);
InitializeVariable(context, StringValue, TestData.Variables.ScalarValueObjectType_StringValue);
InitializeVariable(context, DateTimeValue, TestData.Variables.ScalarValueObjectType_DateTimeValue);
InitializeVariable(context, GuidValue, TestData.Variables.ScalarValueObjectType_GuidValue);
InitializeVariable(context, ByteStringValue, TestData.Variables.ScalarValueObjectType_ByteStringValue);
InitializeVariable(context, XmlElementValue, TestData.Variables.ScalarValueObjectType_XmlElementValue);
InitializeVariable(context, NodeIdValue, TestData.Variables.ScalarValueObjectType_NodeIdValue);
InitializeVariable(context, ExpandedNodeIdValue, TestData.Variables.ScalarValueObjectType_ExpandedNodeIdValue);
InitializeVariable(context, QualifiedNameValue, TestData.Variables.ScalarValueObjectType_QualifiedNameValue);
InitializeVariable(context, LocalizedTextValue, TestData.Variables.ScalarValueObjectType_LocalizedTextValue);
InitializeVariable(context, StatusCodeValue, TestData.Variables.ScalarValueObjectType_StatusCodeValue);
InitializeVariable(context, VariantValue, TestData.Variables.ScalarValueObjectType_VariantValue);
InitializeVariable(context, EnumerationValue, TestData.Variables.ScalarValueObjectType_EnumerationValue);
InitializeVariable(context, StructureValue, TestData.Variables.ScalarValueObjectType_StructureValue);
InitializeVariable(context, NumberValue, TestData.Variables.ScalarValueObjectType_NumberValue);
InitializeVariable(context, IntegerValue, TestData.Variables.ScalarValueObjectType_IntegerValue);
InitializeVariable(context, UIntegerValue, TestData.Variables.ScalarValueObjectType_UIntegerValue);
}
#endregion
#region Protected Methods
/// <summary>
/// Handles the generate values method.
/// </summary>
protected override ServiceResult OnGenerateValues(
ISystemContext context,
MethodState method,
NodeId objectId,
uint count)
{
TestDataSystem system = context.SystemHandle as TestDataSystem;
if (system == null)
{
return StatusCodes.BadOutOfService;
}
GenerateValue(system, BooleanValue);
GenerateValue(system, SByteValue);
GenerateValue(system, ByteValue);
GenerateValue(system, Int16Value);
GenerateValue(system, UInt16Value);
GenerateValue(system, Int32Value);
GenerateValue(system, UInt32Value);
GenerateValue(system, UInt32Value);
GenerateValue(system, Int64Value);
GenerateValue(system, UInt64Value);
GenerateValue(system, FloatValue);
GenerateValue(system, DoubleValue);
GenerateValue(system, StringValue);
GenerateValue(system, DateTimeValue);
GenerateValue(system, GuidValue);
GenerateValue(system, ByteStringValue);
GenerateValue(system, XmlElementValue);
GenerateValue(system, NodeIdValue);
GenerateValue(system, ExpandedNodeIdValue);
GenerateValue(system, QualifiedNameValue);
GenerateValue(system, LocalizedTextValue);
GenerateValue(system, StatusCodeValue);
GenerateValue(system, VariantValue);
GenerateValue(system, EnumerationValue);
GenerateValue(system, StructureValue);
GenerateValue(system, NumberValue);
GenerateValue(system, IntegerValue);
GenerateValue(system, UIntegerValue);
return base.OnGenerateValues(context, method, objectId, count);
}
#endregion
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,63 @@
AnalogArrayValueObjectType,9763,ObjectType
AnalogScalarValueObjectType,9534,ObjectType
ArrayValueDataType,9669,DataType
ArrayValueDataType_Encoding_DefaultBinary,11438,Object
ArrayValueDataType_Encoding_DefaultJson,15048,Object
ArrayValueDataType_Encoding_DefaultXml,11419,Object
ArrayValueObjectType,9679,ObjectType
BooleanDataType,9898,DataType
ByteDataType,9900,DataType
ByteStringDataType,9912,DataType
Data,10157,Object
DateTimeDataType,9910,DataType
DoubleDataType,9908,DataType
ExpandedNodeIdDataType,9915,DataType
FloatDataType,9907,DataType
GenerateValuesEventType,9371,ObjectType
GuidDataType,9911,DataType
Int16DataType,9901,DataType
Int32DataType,9903,DataType
Int64DataType,9905,DataType
LocalizedTextDataType,9917,DataType
MethodTestType,10092,ObjectType
NodeIdDataType,9914,DataType
QualifiedNameDataType,9916,DataType
SByteDataType,9899,DataType
ScalarValueDataType,9440,DataType
ScalarValueDataType_Encoding_DefaultBinary,11437,Object
ScalarValueDataType_Encoding_DefaultJson,15047,Object
ScalarValueDataType_Encoding_DefaultXml,11418,Object
ScalarValueObjectType,9450,ObjectType
StatusCodeDataType,9918,DataType
StringDataType,9909,DataType
TestData_BinarySchema,11422,Variable
TestData_XmlSchema,11441,Variable
TestDataObjectType,9383,ObjectType
TestSystemConditionType,10123,ObjectType
UInt16DataType,9902,DataType
UInt32DataType,9904,DataType
UInt64DataType,9906,DataType
UserArrayValueDataType,10006,DataType
UserArrayValueDataType_Encoding_DefaultBinary,11440,Object
UserArrayValueDataType_Encoding_DefaultJson,15050,Object
UserArrayValueDataType_Encoding_DefaultXml,11421,Object
UserArrayValueObjectType,10007,ObjectType
UserScalarValueDataType,9920,DataType
UserScalarValueDataType_Encoding_DefaultBinary,11439,Object
UserScalarValueDataType_Encoding_DefaultJson,15049,Object
UserScalarValueDataType_Encoding_DefaultXml,11420,Object
UserScalarValueObjectType,9921,ObjectType
VariantDataType,9919,DataType
Vector,1000,DataType
Vector_Encoding_DefaultBinary,1008,Object
Vector_Encoding_DefaultJson,64,Object
Vector_Encoding_DefaultXml,36,Object
WorkOrderStatusType,1004,DataType
WorkOrderStatusType_Encoding_DefaultBinary,1011,Object
WorkOrderStatusType_Encoding_DefaultJson,67,Object
WorkOrderStatusType_Encoding_DefaultXml,39,Object
WorkOrderType,1005,DataType
WorkOrderType_Encoding_DefaultBinary,1012,Object
WorkOrderType_Encoding_DefaultJson,68,Object
WorkOrderType_Encoding_DefaultXml,40,Object
XmlElementDataType,9913,DataType
1 AnalogArrayValueObjectType 9763 ObjectType
2 AnalogScalarValueObjectType 9534 ObjectType
3 ArrayValueDataType 9669 DataType
4 ArrayValueDataType_Encoding_DefaultBinary 11438 Object
5 ArrayValueDataType_Encoding_DefaultJson 15048 Object
6 ArrayValueDataType_Encoding_DefaultXml 11419 Object
7 ArrayValueObjectType 9679 ObjectType
8 BooleanDataType 9898 DataType
9 ByteDataType 9900 DataType
10 ByteStringDataType 9912 DataType
11 Data 10157 Object
12 DateTimeDataType 9910 DataType
13 DoubleDataType 9908 DataType
14 ExpandedNodeIdDataType 9915 DataType
15 FloatDataType 9907 DataType
16 GenerateValuesEventType 9371 ObjectType
17 GuidDataType 9911 DataType
18 Int16DataType 9901 DataType
19 Int32DataType 9903 DataType
20 Int64DataType 9905 DataType
21 LocalizedTextDataType 9917 DataType
22 MethodTestType 10092 ObjectType
23 NodeIdDataType 9914 DataType
24 QualifiedNameDataType 9916 DataType
25 SByteDataType 9899 DataType
26 ScalarValueDataType 9440 DataType
27 ScalarValueDataType_Encoding_DefaultBinary 11437 Object
28 ScalarValueDataType_Encoding_DefaultJson 15047 Object
29 ScalarValueDataType_Encoding_DefaultXml 11418 Object
30 ScalarValueObjectType 9450 ObjectType
31 StatusCodeDataType 9918 DataType
32 StringDataType 9909 DataType
33 TestData_BinarySchema 11422 Variable
34 TestData_XmlSchema 11441 Variable
35 TestDataObjectType 9383 ObjectType
36 TestSystemConditionType 10123 ObjectType
37 UInt16DataType 9902 DataType
38 UInt32DataType 9904 DataType
39 UInt64DataType 9906 DataType
40 UserArrayValueDataType 10006 DataType
41 UserArrayValueDataType_Encoding_DefaultBinary 11440 Object
42 UserArrayValueDataType_Encoding_DefaultJson 15050 Object
43 UserArrayValueDataType_Encoding_DefaultXml 11421 Object
44 UserArrayValueObjectType 10007 ObjectType
45 UserScalarValueDataType 9920 DataType
46 UserScalarValueDataType_Encoding_DefaultBinary 11439 Object
47 UserScalarValueDataType_Encoding_DefaultJson 15049 Object
48 UserScalarValueDataType_Encoding_DefaultXml 11420 Object
49 UserScalarValueObjectType 9921 ObjectType
50 VariantDataType 9919 DataType
51 Vector 1000 DataType
52 Vector_Encoding_DefaultBinary 1008 Object
53 Vector_Encoding_DefaultJson 64 Object
54 Vector_Encoding_DefaultXml 36 Object
55 WorkOrderStatusType 1004 DataType
56 WorkOrderStatusType_Encoding_DefaultBinary 1011 Object
57 WorkOrderStatusType_Encoding_DefaultJson 67 Object
58 WorkOrderStatusType_Encoding_DefaultXml 39 Object
59 WorkOrderType 1005 DataType
60 WorkOrderType_Encoding_DefaultBinary 1012 Object
61 WorkOrderType_Encoding_DefaultJson 68 Object
62 WorkOrderType_Encoding_DefaultXml 40 Object
63 XmlElementDataType 9913 DataType
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More