diff --git a/SampleApplications/SDK/Opc.Ua.Client/CoreClientUtils.cs b/SampleApplications/SDK/Opc.Ua.Client/CoreClientUtils.cs
new file mode 100644
index 0000000000..c1fa3cce94
--- /dev/null
+++ b/SampleApplications/SDK/Opc.Ua.Client/CoreClientUtils.cs
@@ -0,0 +1,175 @@
+/* ========================================================================
+ * Copyright (c) 2005-2013 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;
+
+namespace Opc.Ua.Client
+{
+ ///
+ /// Defines numerous re-useable utility functions for clients.
+ ///
+ public class CoreClientUtils
+ {
+ #region Discovery
+ ///
+ /// Discovers the servers on the local machine.
+ ///
+ /// The configuration.
+ /// A list of server urls.
+ public static IList DiscoverServers(ApplicationConfiguration configuration)
+ {
+ List serverUrls = new List();
+
+ // set a short timeout because this is happening in the drop down event.
+ EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration);
+ endpointConfiguration.OperationTimeout = 5000;
+
+ // Connect to the local discovery server and find the available servers.
+ using (DiscoveryClient client = DiscoveryClient.Create(new Uri("opc.tcp://localhost:4840"), endpointConfiguration))
+ {
+ ApplicationDescriptionCollection servers = client.FindServers(null);
+
+ // populate the drop down list with the discovery URLs for the available servers.
+ for (int ii = 0; ii < servers.Count; ii++)
+ {
+ if (servers[ii].ApplicationType == ApplicationType.DiscoveryServer)
+ {
+ continue;
+ }
+
+ for (int jj = 0; jj < servers[ii].DiscoveryUrls.Count; jj++)
+ {
+ string discoveryUrl = servers[ii].DiscoveryUrls[jj];
+
+ // Many servers will use the '/discovery' suffix for the discovery endpoint.
+ // The URL without this prefix should be the base URL for the server.
+ if (discoveryUrl.EndsWith("/discovery"))
+ {
+ discoveryUrl = discoveryUrl.Substring(0, discoveryUrl.Length - "/discovery".Length);
+ }
+
+ // ensure duplicates do not get added.
+ if (!serverUrls.Contains(discoveryUrl))
+ {
+ serverUrls.Add(discoveryUrl);
+ }
+ }
+ }
+ }
+
+ return serverUrls;
+ }
+
+ ///
+ /// Finds the endpoint that best matches the current settings.
+ ///
+ /// The discovery URL.
+ /// if set to true select an endpoint that uses security.
+ /// The best available endpoint.
+ public static EndpointDescription SelectEndpoint(string discoveryUrl, bool useSecurity, int operationTimeout = -1)
+ {
+ // needs to add the '/discovery' back onto non-UA TCP URLs.
+ if (discoveryUrl.StartsWith(Utils.UriSchemeHttps))
+ {
+ if (!discoveryUrl.EndsWith("/discovery"))
+ {
+ discoveryUrl += "/discovery";
+ }
+ }
+
+ // parse the selected URL.
+ Uri uri = new Uri(discoveryUrl);
+
+ EndpointConfiguration configuration = EndpointConfiguration.Create();
+ if (operationTimeout > 0)
+ {
+ configuration.OperationTimeout = operationTimeout;
+ }
+
+ EndpointDescription selectedEndpoint = null;
+
+ // Connect to the server's discovery endpoint and find the available configuration.
+ using (DiscoveryClient client = DiscoveryClient.Create(uri, configuration))
+ {
+ EndpointDescriptionCollection endpoints = client.GetEndpoints(null);
+
+ // select the best endpoint to use based on the selected URL and the UseSecurity checkbox.
+ for (int ii = 0; ii < endpoints.Count; ii++)
+ {
+ EndpointDescription endpoint = endpoints[ii];
+
+ // check for a match on the URL scheme.
+ if (endpoint.EndpointUrl.StartsWith(uri.Scheme))
+ {
+ // check if security was requested.
+ if (useSecurity)
+ {
+ if (endpoint.SecurityMode == MessageSecurityMode.None)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ if (endpoint.SecurityMode != MessageSecurityMode.None)
+ {
+ continue;
+ }
+ }
+
+ // pick the first available endpoint by default.
+ if (selectedEndpoint == null)
+ {
+ selectedEndpoint = endpoint;
+ }
+
+ // The security level is a relative measure assigned by the server to the
+ // endpoints that it returns. Clients should always pick the highest level
+ // unless they have a reason not too.
+ if (endpoint.SecurityLevel > selectedEndpoint.SecurityLevel)
+ {
+ selectedEndpoint = endpoint;
+ }
+ }
+ }
+
+ // pick the first available endpoint by default.
+ if (selectedEndpoint == null && endpoints.Count > 0)
+ {
+ selectedEndpoint = endpoints[0];
+ }
+ }
+
+ // return the selected endpoint.
+ return selectedEndpoint;
+ }
+ #endregion
+ }
+}
diff --git a/SampleApplications/SDK/Opc.Ua.Client/Session.cs b/SampleApplications/SDK/Opc.Ua.Client/Session.cs
index c58f9bc2f3..bcfeef10be 100644
--- a/SampleApplications/SDK/Opc.Ua.Client/Session.cs
+++ b/SampleApplications/SDK/Opc.Ua.Client/Session.cs
@@ -830,26 +830,29 @@ namespace Opc.Ua.Client
if (domains != null)
{
- string hostname = endpoint.EndpointUrl.DnsSafeHost;
+ string [] hostnames = endpoint.EndpointUrl.DnsSafeHost.Split('.');
- if (hostname == "localhost" || hostname == "127.0.0.1")
+ if (String.Compare(hostnames[0], "localhost", StringComparison.CurrentCultureIgnoreCase)==0)
{
- hostname = Utils.GetHostName();
+ hostnames[0] = Utils.GetHostName();
}
for (int ii = 0; ii < domains.Count; ii++)
{
- if (String.Compare(hostname, domains[ii], StringComparison.CurrentCultureIgnoreCase) == 0)
+ foreach (string hostname in hostnames)
{
- domainFound = true;
- break;
+ if (String.Compare(hostname, domains[ii], StringComparison.CurrentCultureIgnoreCase) == 0)
+ {
+ domainFound = true;
+ break;
+ }
}
}
- }
- if (!domainFound)
- {
- throw new ServiceResultException(StatusCodes.BadCertificateHostNameInvalid);
+ if (!domainFound)
+ {
+ throw new ServiceResultException(StatusCodes.BadCertificateHostNameInvalid);
+ }
}
}
@@ -1994,7 +1997,7 @@ namespace Opc.Ua.Client
serverCertificate = Utils.ParseCertificateBlob(certificateData);
m_configuration.CertificateValidator.Validate(serverCertificate);
- if(checkDomain)
+ if (checkDomain)
{
CheckCertificateDomain(m_endpoint);
}
diff --git a/SampleApplications/SDK/Opc.Ua.Configuration/ApplicationInstance.cs b/SampleApplications/SDK/Opc.Ua.Configuration/ApplicationInstance.cs
index 3d4a94dfb7..e340fa6001 100644
--- a/SampleApplications/SDK/Opc.Ua.Configuration/ApplicationInstance.cs
+++ b/SampleApplications/SDK/Opc.Ua.Configuration/ApplicationInstance.cs
@@ -707,19 +707,6 @@ namespace Opc.Ua.Configuration
await AddToTrustedStore(configuration, certificate);
}
- // add to discovery server.
- if (configuration.ApplicationType == ApplicationType.Server || configuration.ApplicationType == ApplicationType.ClientAndServer)
- {
- try
- {
- await AddToDiscoveryServerTrustList(certificate, null, null, configuration.SecurityConfiguration.TrustedPeerCertificates);
- }
- catch (Exception e)
- {
- Utils.Trace(e, "Could not add certificate to LDS trust list.");
- }
- }
-
// update configuration file.
ConfigUtils.UpdateConfigurationLocation(InstallConfig.ExecutableFile, InstallConfig.ConfigurationFile);
@@ -1012,19 +999,6 @@ namespace Opc.Ua.Configuration
await AddToTrustedStore(configuration, certificate);
}
- // add to discovery server.
- if (configuration.ApplicationType == ApplicationType.Server || configuration.ApplicationType == ApplicationType.ClientAndServer)
- {
- try
- {
- await AddToDiscoveryServerTrustList(certificate, null, null, configuration.SecurityConfiguration.TrustedPeerCertificates);
- }
- catch (Exception e)
- {
- Utils.Trace(e, "Could not add certificate to LDS trust list.");
- }
- }
-
return true;
}
#endregion
@@ -1324,121 +1298,6 @@ namespace Opc.Ua.Configuration
}
}
- ///
- /// Adds the application certificate to the discovery server trust list.
- ///
- public static async Task AddToDiscoveryServerTrustList(
- X509Certificate2 certificate,
- string oldThumbprint,
- IList issuers,
- CertificateStoreIdentifier trustedCertificateStore)
- {
- Utils.Trace(Utils.TraceMasks.Information, "Adding certificate to discovery server trust list.");
-
- try
- {
- string configurationPath = Utils.GetAbsoluteFilePath(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "OPC Foundation" + Path.DirectorySeparatorChar + "Config" + Path.DirectorySeparatorChar + "Opc.Ua.DiscoveryServer.Config.xml", true, false, false);
- if (configurationPath == null)
- {
- Utils.Trace("Could not find the discovery server configuration file. Please confirm that it is installed.");
- }
- else
- {
- Opc.Ua.Security.SecuredApplication ldsConfiguration = new Opc.Ua.Security.SecurityConfigurationManager().ReadConfiguration(configurationPath);
- CertificateStoreIdentifier csid = Opc.Ua.Security.SecuredApplication.FromCertificateStoreIdentifier(ldsConfiguration.TrustedCertificateStore);
- await AddApplicationCertificateToStore(csid, certificate, oldThumbprint);
-
- if (issuers != null && ldsConfiguration.IssuerCertificateStore != null)
- {
- csid = Opc.Ua.Security.SecuredApplication.FromCertificateStoreIdentifier(ldsConfiguration.IssuerCertificateStore);
- AddIssuerCertificatesToStore(csid, issuers);
- }
-
- CertificateIdentifier cid = Opc.Ua.Security.SecuredApplication.FromCertificateIdentifier(ldsConfiguration.ApplicationCertificate);
- X509Certificate2 ldsCertificate = await cid.Find(false);
-
- // add LDS certificate to application trust list.
- if (ldsCertificate != null && trustedCertificateStore != null)
- {
- await AddApplicationCertificateToStore(csid, ldsCertificate, null);
- }
- }
- }
- catch (Exception e)
- {
- Utils.Trace(e, "Could not add certificate to discovery server trust list.");
- }
- }
-
- ///
- /// Adds an application certificate to a store.
- ///
- private static async Task AddApplicationCertificateToStore(
- CertificateStoreIdentifier csid,
- X509Certificate2 certificate,
- string oldThumbprint)
- {
- ICertificateStore store = csid.OpenStore();
-
- try
- {
- // delete the old certificate.
- if (oldThumbprint != null)
- {
- await store.Delete(oldThumbprint);
- }
-
- // delete certificates with the same application uri.
- if (store.FindByThumbprint(certificate.Thumbprint) == null)
- {
- string applicationUri = Utils.GetApplicationUriFromCertificate(certificate);
-
- // delete any existing certificates.
- X509Certificate2Collection collection = await store.Enumerate();
- foreach (X509Certificate2 target in collection)
- {
- if (Utils.CompareDistinguishedName(target.Subject, certificate.Subject))
- {
- if (Utils.GetApplicationUriFromCertificate(target) == applicationUri)
- {
- await store.Delete(target.Thumbprint);
- }
- }
- }
-
- // add new certificate.
- await store.Add(new X509Certificate2(certificate.RawData));
- }
- }
- finally
- {
- store.Close();
- }
- }
-
- ///
- /// Adds an application certificate to a store.
- ///
- private static void AddIssuerCertificatesToStore(CertificateStoreIdentifier csid, IList issuers)
- {
- ICertificateStore store = csid.OpenStore();
-
- try
- {
- foreach (X509Certificate2 issuer in issuers)
- {
- if (store.FindByThumbprint(issuer.Thumbprint) == null)
- {
- store.Add(issuer);
- }
- }
- }
- finally
- {
- store.Close();
- }
- }
-
///
/// Adds the certificate to the Trusted Certificate Store
///
diff --git a/SampleApplications/SDK/Opc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs b/SampleApplications/SDK/Opc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs
index e1e7ae478a..388881fb6f 100644
--- a/SampleApplications/SDK/Opc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs
+++ b/SampleApplications/SDK/Opc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs
@@ -154,6 +154,25 @@ namespace Opc.Ua.Server
getMonitoredItems.OnCallMethod = OnGetMonitoredItems;
}
+ // set ArrayDimensions for GetMonitoredItems.OutputArguments.Value.
+ PropertyState getMonitoredItemsOutputArguments = (PropertyState)FindPredefinedNode(
+ VariableIds.Server_GetMonitoredItems_OutputArguments,
+ typeof(PropertyState));
+
+ if (getMonitoredItemsOutputArguments != null)
+ {
+ Argument[] outputArgumentsValue = (Argument[])getMonitoredItemsOutputArguments.Value;
+
+ if (outputArgumentsValue != null)
+ {
+ foreach (Argument argument in outputArgumentsValue)
+ {
+ argument.ArrayDimensions = new UInt32Collection { 0 };
+ }
+
+ getMonitoredItemsOutputArguments.ClearChangeMasks(SystemContext, false);
+ }
+ }
}
}
diff --git a/SampleApplications/Samples/ClientControls.Net4/ClientUtils.cs b/SampleApplications/Samples/ClientControls.Net4/ClientUtils.cs
index af5dcf731b..7038a81a2c 100644
--- a/SampleApplications/Samples/ClientControls.Net4/ClientUtils.cs
+++ b/SampleApplications/Samples/ClientControls.Net4/ClientUtils.cs
@@ -284,154 +284,6 @@ namespace Opc.Ua.Client.Controls
}
#endregion
- #region Discovery
- ///
- /// Discovers the servers on the local machine.
- ///
- /// The configuration.
- /// A list of server urls.
- public static IList DiscoverServers(ApplicationConfiguration configuration)
- {
- List serverUrls = new List();
-
- // set a short timeout because this is happening in the drop down event.
- EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration);
- endpointConfiguration.OperationTimeout = 5000;
-
- // Connect to the local discovery server and find the available servers.
- using (DiscoveryClient client = DiscoveryClient.Create(new Uri("opc.tcp://localhost:4840"), endpointConfiguration))
- {
- ApplicationDescriptionCollection servers = client.FindServers(null);
-
- // populate the drop down list with the discovery URLs for the available servers.
- for (int ii = 0; ii < servers.Count; ii++)
- {
- if (servers[ii].ApplicationType == ApplicationType.DiscoveryServer)
- {
- continue;
- }
-
- for (int jj = 0; jj < servers[ii].DiscoveryUrls.Count; jj++)
- {
- string discoveryUrl = servers[ii].DiscoveryUrls[jj];
-
- // Many servers will use the '/discovery' suffix for the discovery endpoint.
- // The URL without this prefix should be the base URL for the server.
- if (discoveryUrl.EndsWith("/discovery"))
- {
- discoveryUrl = discoveryUrl.Substring(0, discoveryUrl.Length - "/discovery".Length);
- }
-
- // ensure duplicates do not get added.
- if (!serverUrls.Contains(discoveryUrl))
- {
- serverUrls.Add(discoveryUrl);
- }
- }
- }
- }
-
- return serverUrls;
- }
-
- ///
- /// Finds the endpoint that best matches the current settings.
- ///
- /// The discovery URL.
- /// if set to true select an endpoint that uses security.
- /// The best available endpoint.
- public static EndpointDescription SelectEndpoint(string discoveryUrl, bool useSecurity)
- {
- // needs to add the '/discovery' back onto non-UA TCP URLs.
- if (!discoveryUrl.StartsWith(Utils.UriSchemeOpcTcp))
- {
- if (!discoveryUrl.EndsWith("/discovery"))
- {
- discoveryUrl += "/discovery";
- }
- }
-
- // parse the selected URL.
- Uri uri = new Uri(discoveryUrl);
-
- // set a short timeout because this is happening in the drop down event.
- EndpointConfiguration configuration = EndpointConfiguration.Create();
- configuration.OperationTimeout = 5000;
-
- EndpointDescription selectedEndpoint = null;
-
- // Connect to the server's discovery endpoint and find the available configuration.
- using (DiscoveryClient client = DiscoveryClient.Create(uri, configuration))
- {
- EndpointDescriptionCollection endpoints = client.GetEndpoints(null);
-
- // select the best endpoint to use based on the selected URL and the UseSecurity checkbox.
- for (int ii = 0; ii < endpoints.Count; ii++)
- {
- EndpointDescription endpoint = endpoints[ii];
-
- // check for a match on the URL scheme.
- if (endpoint.EndpointUrl.StartsWith(uri.Scheme))
- {
- // check if security was requested.
- if (useSecurity)
- {
- if (endpoint.SecurityMode == MessageSecurityMode.None)
- {
- continue;
- }
- }
- else
- {
- if (endpoint.SecurityMode != MessageSecurityMode.None)
- {
- continue;
- }
- }
-
- // pick the first available endpoint by default.
- if (selectedEndpoint == null)
- {
- selectedEndpoint = endpoint;
- }
-
- // The security level is a relative measure assigned by the server to the
- // endpoints that it returns. Clients should always pick the highest level
- // unless they have a reason not too.
- if (endpoint.SecurityLevel > selectedEndpoint.SecurityLevel)
- {
- selectedEndpoint = endpoint;
- }
- }
- }
-
- // pick the first available endpoint by default.
- if (selectedEndpoint == null && endpoints.Count > 0)
- {
- selectedEndpoint = endpoints[0];
- }
- }
-
- // if a server is behind a firewall it may return URLs that are not accessible to the client.
- // This problem can be avoided by assuming that the domain in the URL used to call
- // GetEndpoints can be used to access any of the endpoints. This code makes that conversion.
- // Note that the conversion only makes sense if discovery uses the same protocol as the endpoint.
-
- Uri endpointUrl = Utils.ParseUri(selectedEndpoint.EndpointUrl);
-
- if (endpointUrl != null && endpointUrl.Scheme == uri.Scheme)
- {
- UriBuilder builder = new UriBuilder(endpointUrl);
- builder.Host = uri.DnsSafeHost;
- builder.Port = uri.Port;
- selectedEndpoint.EndpointUrl = builder.ToString();
- }
-
- // return the selected endpoint.
- return selectedEndpoint;
- }
- #endregion
-
#region Browse
///
/// Browses the address space and returns the references found.
@@ -938,7 +790,6 @@ namespace Opc.Ua.Client.Controls
}
#endregion
-
#region Type Model Browsing
///
/// Collects the instance declarations for a type.
diff --git a/SampleApplications/Samples/ClientControls.Net4/Common/Client/ConnectServerCtrl.cs b/SampleApplications/Samples/ClientControls.Net4/Common/Client/ConnectServerCtrl.cs
index f60a73c4d3..9d6818c98d 100644
--- a/SampleApplications/Samples/ClientControls.Net4/Common/Client/ConnectServerCtrl.cs
+++ b/SampleApplications/Samples/ClientControls.Net4/Common/Client/ConnectServerCtrl.cs
@@ -56,6 +56,7 @@ namespace Opc.Ua.Client.Controls
private ApplicationConfiguration m_configuration;
private Session m_session;
private int m_reconnectPeriod = 10;
+ private int m_discoverTimeout = 5000;
private SessionReconnectHandler m_reconnectHandler;
private CertificateValidationEventHandler m_CertificateValidation;
private EventHandler m_ReconnectComplete;
@@ -285,7 +286,7 @@ namespace Opc.Ua.Client.Controls
}
// select the best endpoint.
- EndpointDescription endpointDescription = ClientUtils.SelectEndpoint(serverUrl, UseSecurityCK.Checked);
+ EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(serverUrl, UseSecurityCK.Checked, m_discoverTimeout);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
@@ -398,7 +399,7 @@ namespace Opc.Ua.Client.Controls
}
// return the selected endpoint.
- return ClientUtils.SelectEndpoint(discoveryUrl, UseSecurityCK.Checked);
+ return CoreClientUtils.SelectEndpoint(discoveryUrl, UseSecurityCK.Checked, m_discoverTimeout);
}
finally
{
diff --git a/SampleApplications/Samples/NetCoreConsoleClient/Program.cs b/SampleApplications/Samples/NetCoreConsoleClient/Program.cs
index 4c569be180..33d3ebf7d7 100644
--- a/SampleApplications/Samples/NetCoreConsoleClient/Program.cs
+++ b/SampleApplications/Samples/NetCoreConsoleClient/Program.cs
@@ -48,6 +48,7 @@ namespace NetCoreConsoleClient
public static async Task ConsoleSampleClient(string endpointURL)
{
Console.WriteLine("1 - Create an Application Configuration.");
+ Utils.SetTraceOutput(Utils.TraceOutput.DebugAndFile);
var config = new ApplicationConfiguration()
{
ApplicationName = "UA Core Sample Client",
@@ -130,9 +131,7 @@ namespace NetCoreConsoleClient
}
Console.WriteLine("2 - Discover endpoints of {0}.", endpointURL);
- Uri endpointURI = new Uri(endpointURL);
- var endpointCollection = DiscoverEndpoints(config, endpointURI, 10);
- var selectedEndpoint = SelectUaTcpEndpoint(endpointCollection, haveAppCertificate);
+ var selectedEndpoint = CoreClientUtils.SelectEndpoint(endpointURL, haveAppCertificate);
Console.WriteLine(" Selected endpoint uses: {0}",
selectedEndpoint.SecurityPolicyUri.Substring(selectedEndpoint.SecurityPolicyUri.LastIndexOf('#') + 1));
@@ -219,61 +218,5 @@ namespace NetCoreConsoleClient
e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted);
}
- private static EndpointDescriptionCollection DiscoverEndpoints(ApplicationConfiguration config, Uri discoveryUrl, int timeout)
- {
- // use a short timeout.
- EndpointConfiguration configuration = EndpointConfiguration.Create(config);
- configuration.OperationTimeout = timeout;
-
- using (DiscoveryClient client = DiscoveryClient.Create(
- discoveryUrl,
- EndpointConfiguration.Create(config)))
- {
- try
- {
- EndpointDescriptionCollection endpoints = client.GetEndpoints(null);
- ReplaceLocalHostWithRemoteHost(endpoints, discoveryUrl);
- return endpoints;
- }
- catch (Exception e)
- {
- Console.WriteLine("Could not fetch endpoints from url: {0}", discoveryUrl);
- Console.WriteLine("Reason = {0}", e.Message);
- throw e;
- }
- }
- }
-
- private static EndpointDescription SelectUaTcpEndpoint(EndpointDescriptionCollection endpointCollection, bool haveCert)
- {
- EndpointDescription bestEndpoint = null;
- foreach (EndpointDescription endpoint in endpointCollection)
- {
- if (endpoint.TransportProfileUri == Profiles.UaTcpTransport)
- {
- if (bestEndpoint == null ||
- haveCert && (endpoint.SecurityLevel > bestEndpoint.SecurityLevel) ||
- !haveCert && (endpoint.SecurityLevel < bestEndpoint.SecurityLevel))
- {
- bestEndpoint = endpoint;
- }
- }
- }
- return bestEndpoint;
- }
-
- private static void ReplaceLocalHostWithRemoteHost(EndpointDescriptionCollection endpoints, Uri discoveryUrl)
- {
- foreach (EndpointDescription endpoint in endpoints)
- {
- endpoint.EndpointUrl = Utils.ReplaceLocalhost(endpoint.EndpointUrl, discoveryUrl.DnsSafeHost);
- StringCollection updatedDiscoveryUrls = new StringCollection();
- foreach (string url in endpoint.Server.DiscoveryUrls)
- {
- updatedDiscoveryUrls.Add(Utils.ReplaceLocalhost(url, discoveryUrl.DnsSafeHost));
- }
- endpoint.Server.DiscoveryUrls = updatedDiscoveryUrls;
- }
- }
}
}
diff --git a/SampleApplications/Workshop/Reference/ConsoleReferenceServer/ConsoleReferenceServer.csproj b/SampleApplications/Workshop/Reference/ConsoleReferenceServer/ConsoleReferenceServer.csproj
index dea189443f..b1b1352f1b 100644
--- a/SampleApplications/Workshop/Reference/ConsoleReferenceServer/ConsoleReferenceServer.csproj
+++ b/SampleApplications/Workshop/Reference/ConsoleReferenceServer/ConsoleReferenceServer.csproj
@@ -5,7 +5,6 @@
ConsoleReferenceServer
Exe
ConsoleReferenceServer
- 1.0.4
false
false
false
diff --git a/SampleApplications/Workshop/Reference/ConsoleReferenceServer/Program.cs b/SampleApplications/Workshop/Reference/ConsoleReferenceServer/Program.cs
index e538d2ac51..e7766918df 100644
--- a/SampleApplications/Workshop/Reference/ConsoleReferenceServer/Program.cs
+++ b/SampleApplications/Workshop/Reference/ConsoleReferenceServer/Program.cs
@@ -103,6 +103,7 @@ namespace Quickstarts.ReferenceServer
{
Console.WriteLine("Server stopped. Waiting for exit...");
+ server.Stop();
server.Dispose();
server = null;
diff --git a/SampleApplications/Workshop/Reference/ConsoleReferenceServer/Quickstarts.ReferenceServer.Config.xml b/SampleApplications/Workshop/Reference/ConsoleReferenceServer/Quickstarts.ReferenceServer.Config.xml
index 5ca4fac7fb..f088750646 100644
--- a/SampleApplications/Workshop/Reference/ConsoleReferenceServer/Quickstarts.ReferenceServer.Config.xml
+++ b/SampleApplications/Workshop/Reference/ConsoleReferenceServer/Quickstarts.ReferenceServer.Config.xml
@@ -192,7 +192,8 @@
-
+
+ 5
diff --git a/SampleApplications/Workshop/Reference/Server/Quickstarts.ReferenceServer.Config.xml b/SampleApplications/Workshop/Reference/Server/Quickstarts.ReferenceServer.Config.xml
index 2d9e01be59..dcb4b2b554 100644
--- a/SampleApplications/Workshop/Reference/Server/Quickstarts.ReferenceServer.Config.xml
+++ b/SampleApplications/Workshop/Reference/Server/Quickstarts.ReferenceServer.Config.xml
@@ -192,7 +192,8 @@
-
+
+ 5
diff --git a/SampleApplications/Workshop/Reference/Server/ReferenceNodeManager.cs b/SampleApplications/Workshop/Reference/Server/ReferenceNodeManager.cs
index e861d6ccbb..0c4e712a83 100644
--- a/SampleApplications/Workshop/Reference/Server/ReferenceNodeManager.cs
+++ b/SampleApplications/Workshop/Reference/Server/ReferenceNodeManager.cs
@@ -233,11 +233,8 @@ namespace Quickstarts.ReferenceServer
#region Scalar_Static_Arrays
FolderState arraysFolder = CreateFolder(staticFolder, "Scalar_Static_Arrays", "Arrays");
const string staticArrays = "Scalar_Static_Arrays_";
- // set ArrayDimensions for Boolean variable.
- BaseDataVariableState booleanArrayVar = CreateVariable(arraysFolder, staticArrays + "Boolean", "Boolean", DataTypeIds.Boolean, ValueRanks.OneDimension);
- booleanArrayVar.ArrayDimensions = new ReadOnlyList(new List { 0 });
- variables.Add(booleanArrayVar);
+ variables.Add(CreateVariable(arraysFolder, staticArrays + "Boolean", "Boolean", DataTypeIds.Boolean, ValueRanks.OneDimension));
variables.Add(CreateVariable(arraysFolder, staticArrays + "Byte", "Byte", DataTypeIds.Byte, ValueRanks.OneDimension));
variables.Add(CreateVariable(arraysFolder, staticArrays + "ByteString", "ByteString", DataTypeIds.ByteString, ValueRanks.OneDimension));
variables.Add(CreateVariable(arraysFolder, staticArrays + "DateTime", "DateTime", DataTypeIds.DateTime, ValueRanks.OneDimension));
@@ -2089,6 +2086,15 @@ namespace Quickstarts.ReferenceServer
variable.StatusCode = StatusCodes.Good;
variable.Timestamp = DateTime.UtcNow;
+ if (valueRank == ValueRanks.OneDimension)
+ {
+ variable.ArrayDimensions = new ReadOnlyList(new List { 0 });
+ }
+ else if (valueRank == ValueRanks.TwoDimensions)
+ {
+ variable.ArrayDimensions = new ReadOnlyList(new List { 0, 0 });
+ }
+
if (parent != null)
{
parent.AddChild(variable);
diff --git a/SampleApplications/Workshop/Reference/Server/ReferenceServer.cs b/SampleApplications/Workshop/Reference/Server/ReferenceServer.cs
index 79353e7c2b..6914b2abca 100644
--- a/SampleApplications/Workshop/Reference/Server/ReferenceServer.cs
+++ b/SampleApplications/Workshop/Reference/Server/ReferenceServer.cs
@@ -28,6 +28,7 @@
* ======================================================================*/
using System;
+using System.Threading;
using System.Collections.Generic;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
@@ -67,6 +68,19 @@ namespace Quickstarts.ReferenceServer
// create the custom node managers.
nodeManagers.Add(new EmptyNodeManager(server, configuration));
+ // get the ShutdownDelay configuration parameter.
+ ReferenceServerConfiguration referenceServerConfiguration = configuration.ParseExtension();
+
+ if (referenceServerConfiguration != null)
+ {
+ m_shutdownDelay = referenceServerConfiguration.ShutdownDelay;
+ }
+ else
+ {
+ // default value of 5 seconds.
+ m_shutdownDelay = 5;
+ }
+
// create master node manager.
return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray());
}
@@ -125,6 +139,46 @@ namespace Quickstarts.ReferenceServer
// request notifications when the user identity is changed. all valid users are accepted by default.
server.SessionManager.ImpersonateUser += new ImpersonateEventHandler(SessionManager_ImpersonateUser);
}
+
+ ///
+ /// Cleans up before the server shuts down.
+ ///
+ ///
+ /// This method is called before any shutdown processing occurs.
+ ///
+ protected override void OnServerStopping()
+ {
+ try
+ {
+ // check for connected clients
+ IList currentessions = this.ServerInternal.SessionManager.GetSessions();
+
+ if (currentessions.Count > 0)
+ {
+ // provide some time for the connected clients to detect the shutdown state.
+ ServerInternal.Status.Value.ShutdownReason = new LocalizedText("en-US", "Application closed.");
+ ServerInternal.Status.Variable.ShutdownReason.Value = new LocalizedText("en-US", "Application closed.");
+ ServerInternal.Status.Value.State = ServerState.Shutdown;
+ ServerInternal.Status.Variable.State.Value = ServerState.Shutdown;
+ ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, true);
+
+ for (uint timeTillShutdown = m_shutdownDelay; timeTillShutdown > 0; timeTillShutdown--)
+ {
+ ServerInternal.Status.Value.SecondsTillShutdown = timeTillShutdown;
+ ServerInternal.Status.Variable.SecondsTillShutdown.Value = timeTillShutdown;
+ ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, true);
+
+ Thread.Sleep(1000);
+ }
+ }
+ }
+ catch
+ {
+ // ignore error during shutdown procedure.
+ }
+
+ base.OnServerStopping();
+ }
#endregion
#region User Validation Functions
///
@@ -243,6 +297,10 @@ namespace Quickstarts.ReferenceServer
new LocalizedText(info)));
}
}
+
+ #region Private Fields
+ private uint m_shutdownDelay = 0;
+ #endregion
#endregion
}
}
diff --git a/SampleApplications/Workshop/Reference/Server/ReferenceServerConfiguration.cs b/SampleApplications/Workshop/Reference/Server/ReferenceServerConfiguration.cs
index 2e64fb32e8..5b39138324 100644
--- a/SampleApplications/Workshop/Reference/Server/ReferenceServerConfiguration.cs
+++ b/SampleApplications/Workshop/Reference/Server/ReferenceServerConfiguration.cs
@@ -68,9 +68,19 @@ namespace Quickstarts.ReferenceServer
#endregion
#region Public Properties
+ ///
+ /// The delay in seconds to allow a graceful shutdown.
+ ///
+ [DataMember(Order = 1)]
+ public uint ShutdownDelay
+ {
+ get { return m_shutdownDelay; }
+ set { m_shutdownDelay = value; }
+ }
#endregion
#region Private Members
+ private uint m_shutdownDelay;
#endregion
}
}
diff --git a/Stack/Opc.Ua.Core/Stack/Client/DiscoveryClient.cs b/Stack/Opc.Ua.Core/Stack/Client/DiscoveryClient.cs
index a02ddde00f..093af42df6 100644
--- a/Stack/Opc.Ua.Core/Stack/Client/DiscoveryClient.cs
+++ b/Stack/Opc.Ua.Core/Stack/Client/DiscoveryClient.cs
@@ -72,6 +72,34 @@ namespace Opc.Ua
profileUris,
out endpoints);
+ // if a server is behind a firewall, can only be accessed with a FQDN or IP address
+ // it may return URLs that are not accessible to the client. This problem can be avoided
+ // by assuming that the domain in the URL used to call GetEndpoints can be used to
+ // access any of the endpoints. This code patches the returned endpoints accordingly.
+ Uri endpointUrl = Utils.ParseUri(this.Endpoint.EndpointUrl);
+ if (endpointUrl != null)
+ {
+ // patch discovery Url to endpoint Url used for service call
+ foreach (EndpointDescription discoveryEndPoint in endpoints)
+ {
+ Uri discoveryEndPointUri = Utils.ParseUri(discoveryEndPoint.EndpointUrl);
+ if (endpointUrl.Scheme == discoveryEndPointUri.Scheme)
+ {
+ UriBuilder builder = new UriBuilder(discoveryEndPointUri);
+ builder.Host = endpointUrl.DnsSafeHost;
+ builder.Port = endpointUrl.Port;
+ discoveryEndPoint.EndpointUrl = builder.ToString();
+ }
+
+ if (discoveryEndPoint.Server != null &&
+ discoveryEndPoint.Server.DiscoveryUrls != null)
+ {
+ discoveryEndPoint.Server.DiscoveryUrls.Clear();
+ discoveryEndPoint.Server.DiscoveryUrls.Add(this.Endpoint.EndpointUrl.ToString());
+ }
+ }
+ }
+
return endpoints;
}
diff --git a/Stack/Opc.Ua.Core/Stack/Configuration/ConfiguredEndpoints.cs b/Stack/Opc.Ua.Core/Stack/Configuration/ConfiguredEndpoints.cs
index c8aedda962..c6ef48297e 100644
--- a/Stack/Opc.Ua.Core/Stack/Configuration/ConfiguredEndpoints.cs
+++ b/Stack/Opc.Ua.Core/Stack/Configuration/ConfiguredEndpoints.cs
@@ -1122,7 +1122,7 @@ namespace Opc.Ua
// third pass - match based on security level.
foreach (EndpointDescription description in matches)
{
- if (description.SecurityMode > match.SecurityMode)
+ if (description.SecurityLevel > match.SecurityLevel)
{
match = description;
}
diff --git a/Stack/Opc.Ua.Core/Types/Utils/DataGenerator.cs b/Stack/Opc.Ua.Core/Types/Utils/DataGenerator.cs
index 153388ff72..1a3fcfc90a 100644
--- a/Stack/Opc.Ua.Core/Types/Utils/DataGenerator.cs
+++ b/Stack/Opc.Ua.Core/Types/Utils/DataGenerator.cs
@@ -350,7 +350,7 @@ namespace Opc.Ua.Test
actualDimensions[ii] = (int)arrayDimensions[ii];
}
- if (actualDimensions[ii] == 0)
+ while (actualDimensions[ii] == 0)
{
actualDimensions[ii] = m_random.NextInt32(m_maxArrayLength);
}
diff --git a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs
index 27b59cf577..ea8ed0f7ac 100644
--- a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs
+++ b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs
@@ -271,7 +271,7 @@ namespace Opc.Ua
truncated = true;
}
- using (StreamWriter writer = new StreamWriter(File.Open(file.FullName, FileMode.Append)))
+ using (StreamWriter writer = new StreamWriter(File.Open(file.FullName, FileMode.Append, FileAccess.Write, FileShare.Read)))
{
if (truncated)
{
@@ -1389,18 +1389,46 @@ namespace Opc.Ua
return value;
}
- // copy arrays.
+ // copy arrays, any dimension.
Array array = value as Array;
if (array != null)
{
- Array clone = Array.CreateInstance(type.GetElementType(), array.Length);
-
- for (int ii = 0; ii < array.Length; ii++)
+ if (array.Rank == 1)
{
- clone.SetValue(Utils.Clone(array.GetValue(ii)), ii);
+ Array clone = Array.CreateInstance(type.GetElementType(), array.Length);
+ for (int ii = 0; ii < array.Length; ii++)
+ {
+ clone.SetValue(Utils.Clone(array.GetValue(ii)), ii);
+ }
+ return clone;
}
+ else
+ {
+ int[] arrayRanks = new int[array.Rank];
+ int[] arrayIndex = new int[array.Rank];
+ for (int ii=0; ii