mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 07:07:24 +08:00
Merge pull request #222 from OPCFoundation/master
Sync prototyping with master
This commit is contained in:
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines numerous re-useable utility functions for clients.
|
||||
/// </summary>
|
||||
public class CoreClientUtils
|
||||
{
|
||||
#region Discovery
|
||||
/// <summary>
|
||||
/// Discovers the servers on the local machine.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
/// <returns>A list of server urls.</returns>
|
||||
public static IList<string> DiscoverServers(ApplicationConfiguration configuration)
|
||||
{
|
||||
List<string> serverUrls = new List<string>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the endpoint that best matches the current settings.
|
||||
/// </summary>
|
||||
/// <param name="discoveryUrl">The discovery URL.</param>
|
||||
/// <param name="useSecurity">if set to <c>true</c> select an endpoint that uses security.</param>
|
||||
/// <returns>The best available endpoint.</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the application certificate to the discovery server trust list.
|
||||
/// </summary>
|
||||
public static async Task AddToDiscoveryServerTrustList(
|
||||
X509Certificate2 certificate,
|
||||
string oldThumbprint,
|
||||
IList<X509Certificate2> 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.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an application certificate to a store.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an application certificate to a store.
|
||||
/// </summary>
|
||||
private static void AddIssuerCertificatesToStore(CertificateStoreIdentifier csid, IList<X509Certificate2> issuers)
|
||||
{
|
||||
ICertificateStore store = csid.OpenStore();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (X509Certificate2 issuer in issuers)
|
||||
{
|
||||
if (store.FindByThumbprint(issuer.Thumbprint) == null)
|
||||
{
|
||||
store.Add(issuer);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the certificate to the Trusted Certificate Store
|
||||
/// </summary>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -284,154 +284,6 @@ namespace Opc.Ua.Client.Controls
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Discovery
|
||||
/// <summary>
|
||||
/// Discovers the servers on the local machine.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
/// <returns>A list of server urls.</returns>
|
||||
public static IList<string> DiscoverServers(ApplicationConfiguration configuration)
|
||||
{
|
||||
List<string> serverUrls = new List<string>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the endpoint that best matches the current settings.
|
||||
/// </summary>
|
||||
/// <param name="discoveryUrl">The discovery URL.</param>
|
||||
/// <param name="useSecurity">if set to <c>true</c> select an endpoint that uses security.</param>
|
||||
/// <returns>The best available endpoint.</returns>
|
||||
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
|
||||
/// <summary>
|
||||
/// Browses the address space and returns the references found.
|
||||
@@ -938,7 +790,6 @@ namespace Opc.Ua.Client.Controls
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Type Model Browsing
|
||||
/// <summary>
|
||||
/// Collects the instance declarations for a type.
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -5,7 +5,6 @@
|
||||
<AssemblyName>ConsoleReferenceServer</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>ConsoleReferenceServer</PackageId>
|
||||
<RuntimeFrameworkVersion>1.0.4</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace Quickstarts.ReferenceServer
|
||||
{
|
||||
Console.WriteLine("Server stopped. Waiting for exit...");
|
||||
|
||||
server.Stop();
|
||||
server.Dispose();
|
||||
server = null;
|
||||
|
||||
|
||||
+2
-1
@@ -192,7 +192,8 @@
|
||||
|
||||
<Extensions>
|
||||
<ua:XmlElement>
|
||||
<ReferenceServerConfiguration xmlns="http://opcfoundation.org/Quickstarts/Empty">
|
||||
<ReferenceServerConfiguration xmlns="http://opcfoundation.org/Quickstarts/ReferenceApplications">
|
||||
<ShutdownDelay>5</ShutdownDelay>
|
||||
</ReferenceServerConfiguration>
|
||||
</ua:XmlElement>
|
||||
</Extensions>
|
||||
|
||||
@@ -192,7 +192,8 @@
|
||||
|
||||
<Extensions>
|
||||
<ua:XmlElement>
|
||||
<ReferenceServerConfiguration xmlns="http://opcfoundation.org/Quickstarts/Empty">
|
||||
<ReferenceServerConfiguration xmlns="http://opcfoundation.org/Quickstarts/ReferenceApplications">
|
||||
<ShutdownDelay>5</ShutdownDelay>
|
||||
</ReferenceServerConfiguration>
|
||||
</ua:XmlElement>
|
||||
</Extensions>
|
||||
|
||||
@@ -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<uint>(new List<uint> { 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<uint>(new List<uint> { 0 });
|
||||
}
|
||||
else if (valueRank == ValueRanks.TwoDimensions)
|
||||
{
|
||||
variable.ArrayDimensions = new ReadOnlyList<uint>(new List<uint> { 0, 0 });
|
||||
}
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parent.AddChild(variable);
|
||||
|
||||
@@ -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<ReferenceServerConfiguration>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up before the server shuts down.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is called before any shutdown processing occurs.
|
||||
/// </remarks>
|
||||
protected override void OnServerStopping()
|
||||
{
|
||||
try
|
||||
{
|
||||
// check for connected clients
|
||||
IList<Session> 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
|
||||
/// <summary>
|
||||
@@ -243,6 +297,10 @@ namespace Quickstarts.ReferenceServer
|
||||
new LocalizedText(info)));
|
||||
}
|
||||
}
|
||||
|
||||
#region Private Fields
|
||||
private uint m_shutdownDelay = 0;
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,19 @@ namespace Quickstarts.ReferenceServer
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
/// <summary>
|
||||
/// The delay in seconds to allow a graceful shutdown.
|
||||
/// </summary>
|
||||
[DataMember(Order = 1)]
|
||||
public uint ShutdownDelay
|
||||
{
|
||||
get { return m_shutdownDelay; }
|
||||
set { m_shutdownDelay = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Members
|
||||
private uint m_shutdownDelay;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<array.Rank; ii++)
|
||||
{
|
||||
arrayRanks[ii] = array.GetLength(ii);
|
||||
arrayIndex[ii] = 0;
|
||||
}
|
||||
Array clone = Array.CreateInstance(type.GetElementType(), arrayRanks);
|
||||
for (int ii = 0; ii < array.Length; ii++)
|
||||
{
|
||||
clone.SetValue(Utils.Clone(array.GetValue(arrayIndex)), arrayIndex);
|
||||
|
||||
return clone;
|
||||
// iterate the index array
|
||||
for (int ix = 0; ix < array.Rank; ix++)
|
||||
{
|
||||
arrayIndex[ix]++;
|
||||
if (arrayIndex[ix] < arrayRanks[ix])
|
||||
{
|
||||
break;
|
||||
}
|
||||
arrayIndex[ix] = 0;
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
// copy XmlNode.
|
||||
@@ -1696,6 +1724,14 @@ namespace Opc.Ua
|
||||
return castedObject.MemberwiseClone();
|
||||
}
|
||||
}
|
||||
// copy Opc.Ua.ExpandedNodeId
|
||||
{
|
||||
ExpandedNodeId castedObject = value as ExpandedNodeId;
|
||||
if (castedObject != null)
|
||||
{
|
||||
return castedObject.MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
||||
// don't know how to clone object.
|
||||
throw new NotSupportedException(Utils.Format("Don't know how to clone objects of type '{0}'", type.FullName));
|
||||
|
||||
Reference in New Issue
Block a user