Merge pull request #222 from OPCFoundation/master

Sync prototyping with master
This commit is contained in:
Martin Regen
2017-08-09 12:30:29 +02:00
committed by GitHub
18 changed files with 369 additions and 378 deletions
@@ -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
}
}
+14 -11
View File
@@ -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;
}
}
}
}
@@ -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;
@@ -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);
}
+43 -7
View File
@@ -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));