Update da GitHub del progetto UA-NetStd
This commit is contained in:
@@ -327,3 +327,6 @@ _UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
|
||||
|
||||
.fake
|
||||
.ionide
|
||||
@@ -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
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-51
@@ -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;
|
||||
@@ -96,53 +93,13 @@ namespace Opc.Ua.Client.Controls
|
||||
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>
|
||||
+3
-3
@@ -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);
|
||||
var config = await application.LoadApplicationConfiguration(silent: false);
|
||||
|
||||
// setup the logging
|
||||
ConsoleUtils.ConfigureLogging(config, applicationName, logConsole, LogLevel.Information);
|
||||
|
||||
// delete old certificate
|
||||
if (renewCertificate)
|
||||
{
|
||||
await application.DeleteApplicationInstanceCertificate().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// check the application certificate.
|
||||
await application.CheckApplicationInstanceCertificate(silent: false, minimumKeySize: 0);
|
||||
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)
|
||||
{
|
||||
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, console, ClientBase.ValidateResponse);
|
||||
UAClient uaClient = new UAClient(application.ApplicationConfiguration, output, ClientBase.ValidateResponse) {
|
||||
AutoAccept = autoAccept
|
||||
};
|
||||
|
||||
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();
|
||||
bool connected = await uaClient.ConnectAsync(serverUrl.ToString());
|
||||
if (connected)
|
||||
{
|
||||
// Run tests for available methods.
|
||||
uaClient.ReadNodes();
|
||||
uaClient.WriteNodes();
|
||||
uaClient.Browse();
|
||||
uaClient.CallMethod();
|
||||
|
||||
console.WriteLine("Please enter NodeId Value (enter for next option) (ex: ns=4,i=5001)");
|
||||
string fullVal = Console.ReadLine();
|
||||
if (!string.IsNullOrEmpty(fullVal))
|
||||
{
|
||||
selectedItemList = uaClient.Browse(fullVal);
|
||||
}
|
||||
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);
|
||||
}
|
||||
console.WriteLine("Press ENTER to continue...");
|
||||
Console.ReadLine();
|
||||
|
||||
//uaClient.CallMethod();
|
||||
List<Opc.Ua.Client.MonitoredItem> subscribedItems = uaClient.SubscribeToDataChanges(selectedItemList);
|
||||
uaClient.SubscribeToDataChanges();
|
||||
|
||||
// Wait for some DataChange notifications from MonitoredItems
|
||||
await Task.Delay(20_000 * 3 * 15);
|
||||
quit = quitEvent.WaitOne(Math.Min(30_000, waitTime));
|
||||
|
||||
uaClient.Disconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
console.WriteLine("Could not connect to server!");
|
||||
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("\nProgram ended.");
|
||||
console.WriteLine("Press any key to finish...");
|
||||
Console.ReadKey();
|
||||
} while (!quit);
|
||||
|
||||
output.WriteLine("\nClient stopped.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
console.WriteLine(ex.Message);
|
||||
output.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -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>
|
||||
/// Evento per incapsulare dati x refresh pagina
|
||||
/// Call UA method
|
||||
/// </summary>
|
||||
public class opcUaMonitItemChange : EventArgs
|
||||
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
|
||||
|
||||
#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) =>
|
||||
{
|
||||
|
||||
Console.CancelKeyPress += (sender, eArgs) => {
|
||||
quitEvent.Set();
|
||||
eArgs.Cancel = true;
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||

|
||||
|
||||
## 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="Mono.Options" Version="6.6.0.161" />
|
||||
<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.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;
|
||||
}
|
||||
}
|
||||
catch (OptionException e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
showHelp = true;
|
||||
output = new LogWriter();
|
||||
}
|
||||
|
||||
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() {
|
||||
// create the UA server
|
||||
var server = new UAServer<ReferenceServer>(output) {
|
||||
AutoAccept = autoAccept,
|
||||
LogConsole = console,
|
||||
Password = password
|
||||
};
|
||||
await server.Run().ConfigureAwait(false);
|
||||
|
||||
return (int)server.ExitCode;
|
||||
// load the server configuration, validate certificates
|
||||
output.WriteLine("Loading configuration from {0}.", configSectionName);
|
||||
await server.LoadAsync(applicationName, configSectionName).ConfigureAwait(false);
|
||||
|
||||
// use the shadow config to map the config to an externally accessible location
|
||||
if (shadowConfig)
|
||||
{
|
||||
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))
|
||||
{
|
||||
output.WriteLine("Create a copy of the config in the shadow location.");
|
||||
File.Copy(server.Configuration.SourceFilePath, shadowFilePath, true);
|
||||
}
|
||||
output.WriteLine("Reloading configuration from {0}.", shadowFilePath);
|
||||
await server.LoadAsync(applicationName, Path.Combine(shadowPath, configSectionName)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
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; }
|
||||
// setup the logging
|
||||
ConsoleUtils.ConfigureLogging(server.Configuration, applicationName, logConsole, LogLevel.Information);
|
||||
|
||||
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;
|
||||
}
|
||||
// check or renew the certificate
|
||||
output.WriteLine("Check the certificate.");
|
||||
await server.CheckCertificateAsync(renewCertificate).ConfigureAwait(false);
|
||||
|
||||
var quitEvent = new ManualResetEvent(false);
|
||||
try
|
||||
{
|
||||
Console.CancelKeyPress += (sender, eArgs) => {
|
||||
quitEvent.Set();
|
||||
eArgs.Cancel = true;
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
// 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
|
||||
quitEvent.WaitOne();
|
||||
var quitEvent = ConsoleUtils.CtrlCHandler();
|
||||
bool ctrlc = quitEvent.WaitOne(timeout);
|
||||
|
||||
if (m_server != null)
|
||||
{
|
||||
Console.WriteLine("Server stopped. Waiting for exit...");
|
||||
// stop server. May have to wait for clients to disconnect.
|
||||
output.WriteLine("Server stopped. Waiting for exit...");
|
||||
await server.StopAsync().ConfigureAwait(false);
|
||||
|
||||
using (ReferenceServer server = m_server)
|
||||
{
|
||||
// Stop status thread
|
||||
m_server = null;
|
||||
m_status.Wait();
|
||||
// Stop server and dispose
|
||||
server.Stop();
|
||||
return (int)ExitCode.Ok;
|
||||
}
|
||||
}
|
||||
|
||||
ExitCode = ExitCode.Ok;
|
||||
}
|
||||
|
||||
private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
|
||||
catch (ErrorExitException eee)
|
||||
{
|
||||
if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
|
||||
{
|
||||
if (AutoAccept)
|
||||
{
|
||||
if (!LogConsole)
|
||||
{
|
||||
Console.WriteLine("Accepted Certificate: {0}", e.Certificate.Subject);
|
||||
}
|
||||
Utils.Trace(Utils.TraceMasks.Security, "Accepted Certificate: {0}", e.Certificate.Subject);
|
||||
e.Accept = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!LogConsole)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+51
-10
@@ -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,13 +787,21 @@ 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";
|
||||
@@ -184,7 +811,7 @@ namespace Quickstarts.ConsoleReferenceSubscriber
|
||||
{
|
||||
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
|
||||
},
|
||||
|
||||
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 = 1,
|
||||
MajorVersion = 1
|
||||
MinorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration),
|
||||
MajorVersion = ConfigurationVersionUtils.CalculateVersionTime(kTimeOfConfiguration)
|
||||
};
|
||||
#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))
|
||||
});
|
||||
return allTypesMetaData;
|
||||
}
|
||||
|
||||
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)
|
||||
/// <summary>
|
||||
/// Initialize logging
|
||||
/// </summary>
|
||||
private static void InitializeLog()
|
||||
{
|
||||
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))
|
||||
});
|
||||
// Initialize logger
|
||||
Utils.SetTraceLog("%CommonApplicationData%\\OPC Foundation\\Logs\\Quickstarts.ConsoleReferenceSubscriber.log.txt", true);
|
||||
Utils.SetTraceMask(Utils.TraceMasks.Error);
|
||||
Utils.SetTraceOutput(Utils.TraceOutput.DebugAndFile);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+7
-11
@@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
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
|
||||
|
@@ -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
|
||||
}
|
||||
+7
-11
@@ -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
|
||||
|
@@ -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>
|
||||
BIN
Binary file not shown.
+125
@@ -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
|
||||
}
|
||||
}
|
||||
+171
@@ -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
|
||||
|
@@ -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>
|
||||
+121
@@ -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>
|
||||
+3
-7
@@ -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";
|
||||
}
|
||||
}
|
||||
+109
-59
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
-14
@@ -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);
|
||||
|
||||
@@ -141,14 +148,46 @@ namespace Quickstarts.ReferenceServer
|
||||
server.SessionManager.ImpersonateUser += new ImpersonateEventHandler(SessionManager_ImpersonateUser);
|
||||
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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>
|
||||
+2
-6
@@ -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
|
||||
+27
-18
@@ -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)
|
||||
{
|
||||
+4
-5
@@ -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;
|
||||
}
|
||||
|
||||
+173
-66
@@ -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,8 +87,6 @@ namespace Opc.Ua.Server
|
||||
Utils.SilentDispose(m_samplingTimer);
|
||||
m_samplingTimer = null;
|
||||
|
||||
if (m_predefinedNodes != null)
|
||||
{
|
||||
foreach (NodeState node in m_predefinedNodes.Values)
|
||||
{
|
||||
Utils.SilentDispose(node);
|
||||
@@ -97,7 +94,6 @@ namespace Opc.Ua.Server
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INodeIdFactory Members
|
||||
@@ -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)
|
||||
{
|
||||
@@ -838,7 +846,7 @@ namespace Opc.Ua.Server
|
||||
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)
|
||||
@@ -1152,7 +1160,7 @@ namespace Opc.Ua.Server
|
||||
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);
|
||||
@@ -1250,7 +1259,7 @@ namespace Opc.Ua.Server
|
||||
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)
|
||||
@@ -1375,7 +1384,7 @@ namespace Opc.Ua.Server
|
||||
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>();
|
||||
|
||||
@@ -1593,7 +1602,7 @@ namespace Opc.Ua.Server
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1695,7 +1704,7 @@ namespace Opc.Ua.Server
|
||||
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)
|
||||
@@ -1773,7 +1782,7 @@ namespace Opc.Ua.Server
|
||||
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)
|
||||
{
|
||||
// check for loose coupling.
|
||||
if (source.ReferenceExists(ReferenceTypeIds.HasComponent, false, methodToCall.MethodId))
|
||||
{
|
||||
method = (MethodState)FindPredefinedNode(methodToCall.MethodId, typeof(MethodState));
|
||||
}
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
errors[ii] = StatusCodes.BadMethodInvalid;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if method belongs to the object.
|
||||
if (!Object.ReferenceEquals(method.Parent, source))
|
||||
{
|
||||
errors[ii] = StatusCodes.BadMethodInvalid;
|
||||
continue;
|
||||
}
|
||||
|
||||
CallMethodResult result = results[ii] = new CallMethodResult();
|
||||
@@ -1977,7 +1988,6 @@ namespace Opc.Ua.Server
|
||||
bool unsubscribe)
|
||||
{
|
||||
ServerSystemContext systemContext = m_systemContext.Copy(context);
|
||||
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
|
||||
|
||||
lock (Lock)
|
||||
{
|
||||
@@ -2042,7 +2052,6 @@ namespace Opc.Ua.Server
|
||||
bool unsubscribe)
|
||||
{
|
||||
ServerSystemContext systemContext = m_systemContext.Copy(context);
|
||||
IDictionary<NodeId,NodeState> operationCache = new NodeIdDictionary<NodeState>();
|
||||
|
||||
lock (Lock)
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
@@ -2202,7 +2211,7 @@ namespace Opc.Ua.Server
|
||||
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,
|
||||
@@ -2442,10 +2483,18 @@ namespace Opc.Ua.Server
|
||||
initialValue);
|
||||
|
||||
if (ServiceResult.IsBad(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.
|
||||
MonitoringParameters parameters = itemToCreate.RequestedParameters;
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,6 +2927,78 @@ 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>
|
||||
@@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
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
Reference in New Issue
Block a user