mirror of
https://gitee.com/iioter/iotgateway.git
synced 2024-11-29 18:28:09 +08:00
基金会UAServer
This commit is contained in:
parent
6b9a0aa57c
commit
2e8bee9451
Binary file not shown.
300
IoTGateway/Quickstarts.ReferenceServer.Config.xml
Normal file
300
IoTGateway/Quickstarts.ReferenceServer.Config.xml
Normal file
@ -0,0 +1,300 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ApplicationConfiguration
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
|
||||
xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"
|
||||
>
|
||||
<ApplicationName>Quickstart Reference Server</ApplicationName>
|
||||
<ApplicationUri>urn:localhost:UA:Quickstarts:ReferenceServer</ApplicationUri>
|
||||
<ProductUri>uri:opcfoundation.org:Quickstarts:ReferenceServer</ProductUri>
|
||||
<ApplicationType>Server_0</ApplicationType>
|
||||
|
||||
<SecurityConfiguration>
|
||||
|
||||
<!-- Where the application instance certificate is stored (MachineDefault) -->
|
||||
<ApplicationCertificate>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>%LocalApplicationData%/OPC Foundation/pki/own</StorePath>
|
||||
<SubjectName>CN=Quickstart Reference Server, C=US, S=Arizona, O=OPC Foundation, DC=localhost</SubjectName>
|
||||
</ApplicationCertificate>
|
||||
|
||||
<!-- Where the issuer certificate are stored (certificate authorities) -->
|
||||
<TrustedIssuerCertificates>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>%LocalApplicationData%/OPC Foundation/pki/issuer</StorePath>
|
||||
</TrustedIssuerCertificates>
|
||||
|
||||
<!-- Where the trust list is stored -->
|
||||
<TrustedPeerCertificates>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>%LocalApplicationData%/OPC Foundation/pki/trusted</StorePath>
|
||||
</TrustedPeerCertificates>
|
||||
|
||||
<!-- The directory used to store invalid certficates for later review by the administrator. -->
|
||||
<RejectedCertificateStore>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>%LocalApplicationData%/OPC Foundation/pki/rejected</StorePath>
|
||||
</RejectedCertificateStore>
|
||||
|
||||
<!-- WARNING: The following setting (to automatically accept untrusted certificates) should be used
|
||||
for easy debugging purposes ONLY and turned off for production deployments! -->
|
||||
<AutoAcceptUntrustedCertificates>false</AutoAcceptUntrustedCertificates>
|
||||
|
||||
<!-- WARNING: SHA1 signed certficates are by default rejected and should be phased out.
|
||||
The setting below to allow them is only required for UACTT (1.02.336.244) which uses SHA-1 signed certs. -->
|
||||
<RejectSHA1SignedCertificates>false</RejectSHA1SignedCertificates>
|
||||
<RejectUnknownRevocationStatus>true</RejectUnknownRevocationStatus>
|
||||
<MinimumCertificateKeySize>2048</MinimumCertificateKeySize>
|
||||
<AddAppCertToTrustedStore>false</AddAppCertToTrustedStore>
|
||||
<SendCertificateChain>true</SendCertificateChain>
|
||||
|
||||
<!-- Where the User issuer certificates are stored -->
|
||||
<UserIssuerCertificates>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>%LocalApplicationData%/OPC Foundation/pki/issuerUser</StorePath>
|
||||
</UserIssuerCertificates>
|
||||
|
||||
<!-- Where the User trust list is stored-->
|
||||
<TrustedUserCertificates>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>%LocalApplicationData%/OPC Foundation/pki/trustedUser</StorePath>
|
||||
</TrustedUserCertificates>
|
||||
</SecurityConfiguration>
|
||||
|
||||
<TransportConfigurations></TransportConfigurations>
|
||||
<TransportQuotas>
|
||||
<OperationTimeout>600000</OperationTimeout>
|
||||
<MaxStringLength>1048576</MaxStringLength>
|
||||
<MaxByteStringLength>1048576</MaxByteStringLength>
|
||||
<MaxArrayLength>65535</MaxArrayLength>
|
||||
<MaxMessageSize>4194304</MaxMessageSize>
|
||||
<MaxBufferSize>65535</MaxBufferSize>
|
||||
<ChannelLifetime>300000</ChannelLifetime>
|
||||
<SecurityTokenLifetime>3600000</SecurityTokenLifetime>
|
||||
</TransportQuotas>
|
||||
<ServerConfiguration>
|
||||
<BaseAddresses>
|
||||
<ua:String>https://localhost:62540/Quickstarts/ReferenceServer</ua:String>
|
||||
<ua:String>opc.tcp://localhost:62541/Quickstarts/ReferenceServer</ua:String>
|
||||
</BaseAddresses>
|
||||
<!--
|
||||
These list the alternate addresses (via firewalls, multiple NICs etc.) that can be
|
||||
used to communicate with the server. The URL used by the client when calling
|
||||
FindServers/GetEndpoints or CreateSession will be used to filter the list of
|
||||
endpoints returned by checking for alternate base addresses that have a domain
|
||||
that matches the domain in the url provided by the client.
|
||||
|
||||
Note that any additional domains should be listed in the server's certificate. If they
|
||||
are left out the client make refuse to connect because it has no way to know if the
|
||||
alternate domain was authorized by the server administrator.
|
||||
-->
|
||||
<!--
|
||||
<AlternateBaseAddresses>
|
||||
<ua:String>http://AlternateHostName/Quickstarts/ReferenceServer</ua:String>
|
||||
<ua:String>http://10.10.103.150/Quickstarts/ReferenceServer</ua:String>
|
||||
<ua:String>http://[2a01::626d]/Quickstarts/ReferenceServer</ua:String>
|
||||
</AlternateBaseAddresses>
|
||||
-->
|
||||
<SecurityPolicies>
|
||||
<ServerSecurityPolicy>
|
||||
<SecurityMode>SignAndEncrypt_3</SecurityMode>
|
||||
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</SecurityPolicyUri>
|
||||
</ServerSecurityPolicy>
|
||||
<ServerSecurityPolicy>
|
||||
<SecurityMode>None_1</SecurityMode>
|
||||
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#None</SecurityPolicyUri>
|
||||
</ServerSecurityPolicy>
|
||||
<ServerSecurityPolicy>
|
||||
<SecurityMode>Sign_2</SecurityMode>
|
||||
<SecurityPolicyUri></SecurityPolicyUri>
|
||||
</ServerSecurityPolicy>
|
||||
<ServerSecurityPolicy>
|
||||
<SecurityMode>SignAndEncrypt_3</SecurityMode>
|
||||
<SecurityPolicyUri></SecurityPolicyUri>
|
||||
</ServerSecurityPolicy>
|
||||
<!-- deprecated security policies for reference only
|
||||
<ServerSecurityPolicy>
|
||||
<SecurityMode>Sign_2</SecurityMode>
|
||||
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256</SecurityPolicyUri>
|
||||
</ServerSecurityPolicy>
|
||||
<ServerSecurityPolicy>
|
||||
<SecurityMode>SignAndEncrypt_3</SecurityMode>
|
||||
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256</SecurityPolicyUri>
|
||||
</ServerSecurityPolicy>
|
||||
<ServerSecurityPolicy>
|
||||
<SecurityMode>Sign_2</SecurityMode>
|
||||
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15</SecurityPolicyUri>
|
||||
</ServerSecurityPolicy>
|
||||
<ServerSecurityPolicy>
|
||||
<SecurityMode>SignAndEncrypt_3</SecurityMode>
|
||||
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15</SecurityPolicyUri>
|
||||
</ServerSecurityPolicy>
|
||||
-->
|
||||
</SecurityPolicies>
|
||||
|
||||
<MinRequestThreadCount>5</MinRequestThreadCount>
|
||||
<MaxRequestThreadCount>100</MaxRequestThreadCount>
|
||||
<MaxQueuedRequestCount>2000</MaxQueuedRequestCount>
|
||||
|
||||
<!-- The SDK expects the server to support the same set of user tokens for every endpoint. -->
|
||||
<UserTokenPolicies>
|
||||
<!-- Allows anonymous users -->
|
||||
<ua:UserTokenPolicy>
|
||||
<ua:TokenType>Anonymous_0</ua:TokenType>
|
||||
<ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#None</ua:SecurityPolicyUri>
|
||||
</ua:UserTokenPolicy>
|
||||
|
||||
<!-- Allows username/password -->
|
||||
<ua:UserTokenPolicy>
|
||||
<ua:TokenType>UserName_1</ua:TokenType>
|
||||
<!-- passwords must be encrypted - this specifies what algorithm to use -->
|
||||
<ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri>
|
||||
</ua:UserTokenPolicy>
|
||||
|
||||
<!-- Allows user certificates -->
|
||||
<ua:UserTokenPolicy>
|
||||
<ua:TokenType>Certificate_2</ua:TokenType>
|
||||
<!-- certificate possession must be proven with a digital signature - this specifies what algorithm to use -->
|
||||
<ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri>
|
||||
</ua:UserTokenPolicy>
|
||||
</UserTokenPolicies>
|
||||
<DiagnosticsEnabled>true</DiagnosticsEnabled>
|
||||
<MaxSessionCount>100</MaxSessionCount>
|
||||
<MinSessionTimeout>10000</MinSessionTimeout>
|
||||
<MaxSessionTimeout>3600000</MaxSessionTimeout>
|
||||
<MaxBrowseContinuationPoints>10</MaxBrowseContinuationPoints>
|
||||
<MaxQueryContinuationPoints>10</MaxQueryContinuationPoints>
|
||||
<MaxHistoryContinuationPoints>100</MaxHistoryContinuationPoints>
|
||||
<MaxRequestAge>600000</MaxRequestAge>
|
||||
<MinPublishingInterval>100</MinPublishingInterval>
|
||||
<MaxPublishingInterval>3600000</MaxPublishingInterval>
|
||||
<PublishingResolution>50</PublishingResolution>
|
||||
<MaxSubscriptionLifetime>3600000</MaxSubscriptionLifetime>
|
||||
<MaxMessageQueueSize>100</MaxMessageQueueSize>
|
||||
<MaxNotificationQueueSize>100</MaxNotificationQueueSize>
|
||||
<MaxNotificationsPerPublish>1000</MaxNotificationsPerPublish>
|
||||
<MinMetadataSamplingInterval>1000</MinMetadataSamplingInterval>
|
||||
<AvailableSamplingRates>
|
||||
<SamplingRateGroup>
|
||||
<Start>5</Start>
|
||||
<Increment>5</Increment>
|
||||
<Count>20</Count>
|
||||
</SamplingRateGroup>
|
||||
<SamplingRateGroup>
|
||||
<Start>100</Start>
|
||||
<Increment>100</Increment>
|
||||
<Count>4</Count>
|
||||
</SamplingRateGroup>
|
||||
<SamplingRateGroup>
|
||||
<Start>500</Start>
|
||||
<Increment>250</Increment>
|
||||
<Count>2</Count>
|
||||
</SamplingRateGroup>
|
||||
<SamplingRateGroup>
|
||||
<Start>1000</Start>
|
||||
<Increment>500</Increment>
|
||||
<Count>20</Count>
|
||||
</SamplingRateGroup>
|
||||
</AvailableSamplingRates>
|
||||
|
||||
<RegistrationEndpoint>
|
||||
<ua:EndpointUrl>opc.tcp://localhost:4840</ua:EndpointUrl>
|
||||
<ua:Server>
|
||||
<ua:ApplicationUri>opc.tcp://localhost:4840</ua:ApplicationUri>
|
||||
<ua:ApplicationType>DiscoveryServer_3</ua:ApplicationType>
|
||||
<ua:DiscoveryUrls>
|
||||
<ua:String>opc.tcp://localhost:4840</ua:String>
|
||||
</ua:DiscoveryUrls>
|
||||
</ua:Server>
|
||||
<ua:SecurityMode>SignAndEncrypt_3</ua:SecurityMode>
|
||||
<ua:SecurityPolicyUri />
|
||||
<ua:UserIdentityTokens />
|
||||
</RegistrationEndpoint>
|
||||
|
||||
<MaxRegistrationInterval>0</MaxRegistrationInterval>
|
||||
<NodeManagerSaveFile>Quickstarts.ReferenceServer.nodes.xml</NodeManagerSaveFile>
|
||||
<MinSubscriptionLifetime>10000</MinSubscriptionLifetime>
|
||||
<MaxPublishRequestCount>20</MaxPublishRequestCount>
|
||||
<MaxSubscriptionCount>100</MaxSubscriptionCount>
|
||||
<MaxEventQueueSize>10000</MaxEventQueueSize>
|
||||
|
||||
<!-- see https://opcfoundation-onlineapplications.org/profilereporting/ for list of available profiles -->
|
||||
<ServerProfileArray>
|
||||
<ua:String>http://opcfoundation.org/UA-Profile/Server/StandardUA2017</ua:String>
|
||||
<ua:String>http://opcfoundation.org/UA-Profile/Server/DataAccess</ua:String>
|
||||
<ua:String>http://opcfoundation.org/UA-Profile/Server/Methods</ua:String>
|
||||
<ua:String>http://opcfoundation.org/UA-Profile/Server/ReverseConnect</ua:String>
|
||||
</ServerProfileArray>
|
||||
<ShutdownDelay>5</ShutdownDelay>
|
||||
<ServerCapabilities>
|
||||
<ua:String>DA</ua:String>
|
||||
</ServerCapabilities>
|
||||
<SupportedPrivateKeyFormats>
|
||||
<ua:String>PFX</ua:String>
|
||||
<ua:String>PEM</ua:String>
|
||||
</SupportedPrivateKeyFormats>
|
||||
<MaxTrustListSize>0</MaxTrustListSize>
|
||||
<MultiCastDnsEnabled>false</MultiCastDnsEnabled>
|
||||
<!-- Reverse connection parameters for aggregation server sample -->
|
||||
<!--
|
||||
<ReverseConnect>
|
||||
<Clients>
|
||||
<ReverseConnectClient>
|
||||
<EndpointUrl>opc.tcp://localhost:65300</EndpointUrl>
|
||||
<MaxSessionCount>0</MaxSessionCount>
|
||||
<Enabled>true</Enabled>
|
||||
</ReverseConnectClient>
|
||||
</Clients>
|
||||
<ConnectInterval>15000</ConnectInterval>
|
||||
<ConnectTimeout>30000</ConnectTimeout>
|
||||
<RejectTimeout>60000</RejectTimeout>
|
||||
</ReverseConnect>
|
||||
-->
|
||||
<OperationLimits>
|
||||
<MaxNodesPerRead>1000</MaxNodesPerRead>
|
||||
<MaxNodesPerWrite>1000</MaxNodesPerWrite>
|
||||
<MaxNodesPerMethodCall>250</MaxNodesPerMethodCall>
|
||||
<MaxNodesPerBrowse>2500</MaxNodesPerBrowse>
|
||||
<MaxNodesPerTranslateBrowsePathsToNodeIds>1000</MaxNodesPerTranslateBrowsePathsToNodeIds>
|
||||
<MaxMonitoredItemsPerCall>1000</MaxMonitoredItemsPerCall>
|
||||
</OperationLimits>
|
||||
|
||||
</ServerConfiguration>
|
||||
|
||||
<Extensions>
|
||||
<ua:XmlElement>
|
||||
<MemoryBufferConfiguration xmlns="http://samples.org/UA/MemoryBuffer">
|
||||
<Buffers>
|
||||
<MemoryBufferInstance>
|
||||
<Name>UInt32</Name>
|
||||
<TagCount>100</TagCount>
|
||||
<DataType>UInt32</DataType>
|
||||
</MemoryBufferInstance>
|
||||
<MemoryBufferInstance>
|
||||
<Name>Double</Name>
|
||||
<TagCount>100</TagCount>
|
||||
<DataType>Double</DataType>
|
||||
</MemoryBufferInstance>
|
||||
</Buffers>
|
||||
</MemoryBufferConfiguration>
|
||||
</ua:XmlElement>
|
||||
</Extensions>
|
||||
|
||||
<TraceConfiguration>
|
||||
<OutputFilePath>%LocalApplicationData%/OPC Foundation/Logs/Quickstarts.ReferenceServer.log.txt</OutputFilePath>
|
||||
<DeleteOnLoad>true</DeleteOnLoad>
|
||||
<!-- Show Only Errors -->
|
||||
<!-- <TraceMasks>1</TraceMasks> -->
|
||||
<!-- Show Only Security and Errors -->
|
||||
<!-- <TraceMasks>513</TraceMasks> -->
|
||||
<!-- Show Only Security, Errors and Trace -->
|
||||
<!-- <TraceMasks>515</TraceMasks> -->
|
||||
<!-- Show Only Security, COM Calls, Errors and Trace -->
|
||||
<!-- <TraceMasks>771</TraceMasks> -->
|
||||
<!-- Show Only Security, Service Calls, Errors and Trace -->
|
||||
<!-- <TraceMasks>523</TraceMasks> -->
|
||||
<!-- Show Only Security, ServiceResultExceptions, Errors and Trace -->
|
||||
<!-- <TraceMasks>519</TraceMasks> -->
|
||||
</TraceConfiguration>
|
||||
|
||||
</ApplicationConfiguration>
|
@ -73,6 +73,7 @@ namespace IoTGateway
|
||||
|
||||
|
||||
services.AddHostedService<IoTBackgroundService>();
|
||||
services.AddHostedService<UAService>();
|
||||
services.AddSingleton<DeviceService>();
|
||||
services.AddSingleton<DrvierService>();
|
||||
services.AddSingleton<MyMqttClient>();
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DynamicExpresso.Core" Version="2.9.7" />
|
||||
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
||||
<PackageReference Include="MQTTnet" Version="3.1.1" />
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.4.367.75" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
275
Plugins/Plugin/UA.Server/ConsoleUtils.cs
Normal file
275
Plugins/Plugin/UA.Server/ConsoleUtils.cs
Normal file
@ -0,0 +1,275 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Mono.Options;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Configuration;
|
||||
using static Opc.Ua.Utils;
|
||||
|
||||
namespace Quickstarts
|
||||
{
|
||||
/// <summary>
|
||||
/// The log output implementation of a TextWriter.
|
||||
/// </summary>
|
||||
public class LogWriter : TextWriter
|
||||
{
|
||||
private StringBuilder m_builder = new StringBuilder();
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
m_builder.Append(value);
|
||||
}
|
||||
|
||||
public override void WriteLine(char value)
|
||||
{
|
||||
m_builder.Append(value);
|
||||
//LogInfo("{0}", m_builder.ToString());
|
||||
m_builder.Clear();
|
||||
}
|
||||
|
||||
public override void WriteLine()
|
||||
{
|
||||
//LogInfo("{0}", m_builder.ToString());
|
||||
m_builder.Clear();
|
||||
}
|
||||
|
||||
public override void WriteLine(string format, object arg0)
|
||||
{
|
||||
m_builder.Append(format);
|
||||
//LogInfo(m_builder.ToString(), arg0);
|
||||
m_builder.Clear();
|
||||
}
|
||||
|
||||
public override void WriteLine(string format, object arg0, object arg1)
|
||||
{
|
||||
m_builder.Append(format);
|
||||
//LogInfo(m_builder.ToString(), arg0, arg1);
|
||||
m_builder.Clear();
|
||||
}
|
||||
|
||||
public override void WriteLine(string format, params object[] arg)
|
||||
{
|
||||
m_builder.Append(format);
|
||||
//LogInfo(m_builder.ToString(), arg);
|
||||
m_builder.Clear();
|
||||
}
|
||||
|
||||
public override void Write(string value)
|
||||
{
|
||||
m_builder.Append(value);
|
||||
}
|
||||
|
||||
public override void WriteLine(string value)
|
||||
{
|
||||
m_builder.Append(value);
|
||||
//LogInfo("{0}", m_builder.ToString());
|
||||
m_builder.Clear();
|
||||
}
|
||||
|
||||
public override Encoding Encoding
|
||||
{
|
||||
get { return Encoding.Default; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The error code why the application exit.
|
||||
/// </summary>
|
||||
public enum ExitCode : int
|
||||
{
|
||||
Ok = 0,
|
||||
ErrorNotStarted = 0x80,
|
||||
ErrorRunning = 0x81,
|
||||
ErrorException = 0x82,
|
||||
ErrorStopping = 0x83,
|
||||
ErrorCertificate = 0x84,
|
||||
ErrorInvalidCommandLine = 0x100
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// An exception that occured and caused an exit of the application.
|
||||
/// </summary>
|
||||
public class ErrorExitException : Exception
|
||||
{
|
||||
public ExitCode ExitCode { get; }
|
||||
|
||||
public ErrorExitException(ExitCode exitCode)
|
||||
{
|
||||
ExitCode = exitCode;
|
||||
}
|
||||
|
||||
public ErrorExitException()
|
||||
{
|
||||
ExitCode = ExitCode.Ok;
|
||||
}
|
||||
|
||||
public ErrorExitException(string message) : base(message)
|
||||
{
|
||||
ExitCode = ExitCode.Ok;
|
||||
}
|
||||
|
||||
public ErrorExitException(string message, ExitCode exitCode) : base(message)
|
||||
{
|
||||
ExitCode = exitCode;
|
||||
}
|
||||
|
||||
public ErrorExitException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
ExitCode = ExitCode.Ok;
|
||||
}
|
||||
|
||||
public ErrorExitException(string message, Exception innerException, ExitCode exitCode) : base(message, innerException)
|
||||
{
|
||||
ExitCode = exitCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A dialog which asks for user input.
|
||||
/// </summary>
|
||||
public class ApplicationMessageDlg : IApplicationMessageDlg
|
||||
{
|
||||
private TextWriter m_output;
|
||||
private string m_message = string.Empty;
|
||||
private bool m_ask;
|
||||
|
||||
public ApplicationMessageDlg(TextWriter output)
|
||||
{
|
||||
m_output = output;
|
||||
}
|
||||
|
||||
public override void Message(string text, bool ask)
|
||||
{
|
||||
m_message = text;
|
||||
m_ask = ask;
|
||||
}
|
||||
|
||||
public override async Task<bool> ShowAsync()
|
||||
{
|
||||
if (m_ask)
|
||||
{
|
||||
var message = new StringBuilder(m_message);
|
||||
message.Append(" (y/n, default y): ");
|
||||
//m_output.Write(message.ToString());
|
||||
|
||||
try
|
||||
{
|
||||
ConsoleKeyInfo result = Console.ReadKey();
|
||||
//m_output.WriteLine();
|
||||
return await Task.FromResult((result.KeyChar == 'y') ||
|
||||
(result.KeyChar == 'Y') || (result.KeyChar == '\r')).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// intentionally fall through
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//m_output.WriteLine(m_message);
|
||||
}
|
||||
|
||||
return await Task.FromResult(true).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper functions shared in various console applications.
|
||||
/// </summary>
|
||||
public static class ConsoleUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Process a command line of the console sample application.
|
||||
/// </summary>
|
||||
public static string ProcessCommandLine(
|
||||
TextWriter output,
|
||||
string[] args,
|
||||
Mono.Options.OptionSet options,
|
||||
ref bool showHelp,
|
||||
bool noExtraArgs = true)
|
||||
{
|
||||
IList<string> extraArgs = null;
|
||||
try
|
||||
{
|
||||
extraArgs = options.Parse(args);
|
||||
if (noExtraArgs)
|
||||
{
|
||||
foreach (string extraArg in extraArgs)
|
||||
{
|
||||
output.WriteLine("Error: Unknown option: {0}", extraArg);
|
||||
showHelp = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OptionException e)
|
||||
{
|
||||
output.WriteLine(e.Message);
|
||||
showHelp = true;
|
||||
}
|
||||
|
||||
if (showHelp)
|
||||
{
|
||||
options.WriteOptionDescriptions(output);
|
||||
throw new ErrorExitException("Invalid Commandline or help requested.", ExitCode.ErrorInvalidCommandLine);
|
||||
}
|
||||
|
||||
return extraArgs.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an event which is set if a user
|
||||
/// enters the Ctrl-C key combination.
|
||||
/// </summary>
|
||||
public static ManualResetEvent CtrlCHandler()
|
||||
{
|
||||
var quitEvent = new ManualResetEvent(false);
|
||||
try
|
||||
{
|
||||
Console.CancelKeyPress += (_, eArgs) => {
|
||||
quitEvent.Set();
|
||||
eArgs.Cancel = true;
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
// intentionally left blank
|
||||
}
|
||||
return quitEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
42
Plugins/Plugin/UA.Server/Namespaces.cs
Normal file
42
Plugins/Plugin/UA.Server/Namespaces.cs
Normal file
@ -0,0 +1,42 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
namespace Quickstarts.ReferenceServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines constants for namespaces used by the servers.
|
||||
/// </summary>
|
||||
public static partial class Namespaces
|
||||
{
|
||||
/// <summary>
|
||||
/// The namespace for the nodes provided by the reference server.
|
||||
/// </summary>
|
||||
public const string ReferenceServer = "http://opcfoundation.org/Quickstarts/ReferenceServer";
|
||||
}
|
||||
}
|
2730
Plugins/Plugin/UA.Server/ReferenceNodeManager.cs
Normal file
2730
Plugins/Plugin/UA.Server/ReferenceNodeManager.cs
Normal file
File diff suppressed because it is too large
Load Diff
379
Plugins/Plugin/UA.Server/ReferenceServer.cs
Normal file
379
Plugins/Plugin/UA.Server/ReferenceServer.cs
Normal file
@ -0,0 +1,379 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Server;
|
||||
|
||||
namespace Quickstarts.ReferenceServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the Quickstart Reference Server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each server instance must have one instance of a StandardServer object which is
|
||||
/// responsible for reading the configuration file, creating the endpoints and dispatching
|
||||
/// incoming requests to the appropriate handler.
|
||||
///
|
||||
/// This sub-class specifies non-configurable metadata such as Product Name and initializes
|
||||
/// the EmptyNodeManager which provides access to the data exposed by the Server.
|
||||
/// </remarks>
|
||||
public partial class ReferenceServer : ReverseConnectServer
|
||||
{
|
||||
#region Overridden Methods
|
||||
/// <summary>
|
||||
/// Creates the node managers for the server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method allows the sub-class create any additional node managers which it uses. The SDK
|
||||
/// always creates a CoreNodeManager which handles the built-in nodes defined by the specification.
|
||||
/// Any additional NodeManagers are expected to handle application specific nodes.
|
||||
/// </remarks>
|
||||
protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
|
||||
{
|
||||
|
||||
List<INodeManager> nodeManagers = new List<INodeManager>();
|
||||
|
||||
// create the custom node managers.
|
||||
nodeManagers.Add(new ReferenceNodeManager(server, configuration));
|
||||
|
||||
if (m_nodeManagerFactory == null || m_nodeManagerFactory.Count == 0)
|
||||
{
|
||||
AddDefaultFactories();
|
||||
}
|
||||
|
||||
foreach (var nodeManagerFactory in m_nodeManagerFactory)
|
||||
{
|
||||
nodeManagers.Add(nodeManagerFactory.Create(server, configuration));
|
||||
}
|
||||
|
||||
// create master node manager.
|
||||
return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the non-configurable properties for the application.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These properties are exposed by the server but cannot be changed by administrators.
|
||||
/// </remarks>
|
||||
protected override ServerProperties LoadServerProperties()
|
||||
{
|
||||
ServerProperties properties = new ServerProperties();
|
||||
|
||||
properties.ManufacturerName = "OPC Foundation";
|
||||
properties.ProductName = "Quickstart Reference Server";
|
||||
properties.ProductUri = "http://opcfoundation.org/Quickstart/ReferenceServer/v1.04";
|
||||
properties.SoftwareVersion = Utils.GetAssemblySoftwareVersion();
|
||||
properties.BuildNumber = Utils.GetAssemblyBuildNumber();
|
||||
properties.BuildDate = Utils.GetAssemblyTimestamp();
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the resource manager for the server.
|
||||
/// </summary>
|
||||
protected override ResourceManager CreateResourceManager(IServerInternal server, ApplicationConfiguration configuration)
|
||||
{
|
||||
ResourceManager resourceManager = new ResourceManager(server, configuration);
|
||||
|
||||
System.Reflection.FieldInfo[] fields = typeof(StatusCodes).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||
|
||||
foreach (System.Reflection.FieldInfo field in fields)
|
||||
{
|
||||
uint? id = field.GetValue(typeof(StatusCodes)) as uint?;
|
||||
|
||||
if (id != null)
|
||||
{
|
||||
resourceManager.Add(id.Value, "en-US", field.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return resourceManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the server before it starts up.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is called before any startup processing occurs. The sub-class may update the
|
||||
/// configuration object or do any other application specific startup tasks.
|
||||
/// </remarks>
|
||||
protected override void OnServerStarting(ApplicationConfiguration configuration)
|
||||
{
|
||||
|
||||
base.OnServerStarting(configuration);
|
||||
|
||||
// it is up to the application to decide how to validate user identity tokens.
|
||||
// this function creates validator for X509 identity tokens.
|
||||
CreateUserIdentityValidators(configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after the server has been started.
|
||||
/// </summary>
|
||||
protected override void OnServerStarted(IServerInternal server)
|
||||
{
|
||||
base.OnServerStarted(server);
|
||||
|
||||
// request notifications when the user identity is changed. all valid users are accepted by default.
|
||||
server.SessionManager.ImpersonateUser += new ImpersonateEventHandler(SessionManager_ImpersonateUser);
|
||||
|
||||
try
|
||||
{
|
||||
lock (ServerInternal.Status.Lock)
|
||||
{
|
||||
// allow a faster sampling interval for CurrentTime node.
|
||||
ServerInternal.Status.Variable.CurrentTime.MinimumSamplingInterval = 250;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region User Validation Functions
|
||||
/// <summary>
|
||||
/// Creates the objects used to validate the user identity tokens supported by the server.
|
||||
/// </summary>
|
||||
private void CreateUserIdentityValidators(ApplicationConfiguration configuration)
|
||||
{
|
||||
for (int ii = 0; ii < configuration.ServerConfiguration.UserTokenPolicies.Count; ii++)
|
||||
{
|
||||
UserTokenPolicy policy = configuration.ServerConfiguration.UserTokenPolicies[ii];
|
||||
|
||||
// create a validator for a certificate token policy.
|
||||
if (policy.TokenType == UserTokenType.Certificate)
|
||||
{
|
||||
// check if user certificate trust lists are specified in configuration.
|
||||
if (configuration.SecurityConfiguration.TrustedUserCertificates != null &&
|
||||
configuration.SecurityConfiguration.UserIssuerCertificates != null)
|
||||
{
|
||||
CertificateValidator certificateValidator = new CertificateValidator();
|
||||
certificateValidator.Update(configuration.SecurityConfiguration).Wait();
|
||||
certificateValidator.Update(configuration.SecurityConfiguration.UserIssuerCertificates,
|
||||
configuration.SecurityConfiguration.TrustedUserCertificates,
|
||||
configuration.SecurityConfiguration.RejectedCertificateStore);
|
||||
|
||||
// set custom validator for user certificates.
|
||||
m_userCertificateValidator = certificateValidator.GetChannelValidator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a client tries to change its user identity.
|
||||
/// </summary>
|
||||
private void SessionManager_ImpersonateUser(Session session, ImpersonateEventArgs args)
|
||||
{
|
||||
// check for a user name token.
|
||||
UserNameIdentityToken userNameToken = args.NewIdentity as UserNameIdentityToken;
|
||||
|
||||
if (userNameToken != null)
|
||||
{
|
||||
args.Identity = VerifyPassword(userNameToken);
|
||||
|
||||
// set AuthenticatedUser role for accepted user/password authentication
|
||||
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_AuthenticatedUser);
|
||||
|
||||
if (args.Identity is SystemConfigurationIdentity)
|
||||
{
|
||||
// set ConfigureAdmin role for user with permission to configure server
|
||||
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_ConfigureAdmin);
|
||||
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_SecurityAdmin);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// check for x509 user token.
|
||||
X509IdentityToken x509Token = args.NewIdentity as X509IdentityToken;
|
||||
|
||||
if (x509Token != null)
|
||||
{
|
||||
VerifyUserTokenCertificate(x509Token.Certificate);
|
||||
args.Identity = new UserIdentity(x509Token);
|
||||
|
||||
// set AuthenticatedUser role for accepted certificate authentication
|
||||
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_AuthenticatedUser);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// check for anonymous token.
|
||||
if (args.NewIdentity is AnonymousIdentityToken || args.NewIdentity == null)
|
||||
{
|
||||
// allow anonymous authentication and set Anonymous role for this authentication
|
||||
args.Identity = new UserIdentity();
|
||||
args.Identity.GrantedRoleIds.Add(ObjectIds.WellKnownRole_Anonymous);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// unsuported identity token type.
|
||||
throw ServiceResultException.Create(StatusCodes.BadIdentityTokenInvalid,
|
||||
"Not supported user token type: {0}.", args.NewIdentity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the password for a username token.
|
||||
/// </summary>
|
||||
private IUserIdentity VerifyPassword(UserNameIdentityToken userNameToken)
|
||||
{
|
||||
var userName = userNameToken.UserName;
|
||||
var password = userNameToken.DecryptedPassword;
|
||||
if (String.IsNullOrEmpty(userName))
|
||||
{
|
||||
// an empty username is not accepted.
|
||||
throw ServiceResultException.Create(StatusCodes.BadIdentityTokenInvalid,
|
||||
"Security token is not a valid username token. An empty username is not accepted.");
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(password))
|
||||
{
|
||||
// an empty password is not accepted.
|
||||
throw ServiceResultException.Create(StatusCodes.BadIdentityTokenRejected,
|
||||
"Security token is not a valid username token. An empty password is not accepted.");
|
||||
}
|
||||
|
||||
// User with permission to configure server
|
||||
if (userName == "sysadmin" && password == "demo")
|
||||
{
|
||||
return new SystemConfigurationIdentity(new UserIdentity(userNameToken));
|
||||
}
|
||||
|
||||
// standard users for CTT verification
|
||||
if (!((userName == "user1" && password == "password") ||
|
||||
(userName == "user2" && password == "password1")))
|
||||
{
|
||||
// construct translation object with default text.
|
||||
TranslationInfo info = new TranslationInfo(
|
||||
"InvalidPassword",
|
||||
"en-US",
|
||||
"Invalid username or password.",
|
||||
userName);
|
||||
|
||||
// create an exception with a vendor defined sub-code.
|
||||
throw new ServiceResultException(new ServiceResult(
|
||||
StatusCodes.BadUserAccessDenied,
|
||||
"InvalidPassword",
|
||||
LoadServerProperties().ProductUri,
|
||||
new LocalizedText(info)));
|
||||
}
|
||||
|
||||
return new UserIdentity(userNameToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a certificate user token is trusted.
|
||||
/// </summary>
|
||||
private void VerifyUserTokenCertificate(X509Certificate2 certificate)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_userCertificateValidator != null)
|
||||
{
|
||||
m_userCertificateValidator.Validate(certificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
CertificateValidator.Validate(certificate);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TranslationInfo info;
|
||||
StatusCode result = StatusCodes.BadIdentityTokenRejected;
|
||||
ServiceResultException se = e as ServiceResultException;
|
||||
if (se != null && se.StatusCode == StatusCodes.BadCertificateUseNotAllowed)
|
||||
{
|
||||
info = new TranslationInfo(
|
||||
"InvalidCertificate",
|
||||
"en-US",
|
||||
"'{0}' is an invalid user certificate.",
|
||||
certificate.Subject);
|
||||
|
||||
result = StatusCodes.BadIdentityTokenInvalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
// construct translation object with default text.
|
||||
info = new TranslationInfo(
|
||||
"UntrustedCertificate",
|
||||
"en-US",
|
||||
"'{0}' is not a trusted user certificate.",
|
||||
certificate.Subject);
|
||||
}
|
||||
|
||||
// create an exception with a vendor defined sub-code.
|
||||
throw new ServiceResultException(new ServiceResult(
|
||||
result,
|
||||
info.Key,
|
||||
LoadServerProperties().ProductUri,
|
||||
new LocalizedText(info)));
|
||||
}
|
||||
}
|
||||
|
||||
private static INodeManagerFactory IsINodeManagerFactoryType(Type type)
|
||||
{
|
||||
var nodeManagerTypeInfo = type.GetTypeInfo();
|
||||
if (nodeManagerTypeInfo.IsAbstract ||
|
||||
!typeof(INodeManagerFactory).IsAssignableFrom(type))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Activator.CreateInstance(type) as INodeManagerFactory;
|
||||
}
|
||||
|
||||
private void AddDefaultFactories()
|
||||
{
|
||||
var assembly = GetType().Assembly;
|
||||
var factories = assembly.GetExportedTypes().Select(type => IsINodeManagerFactoryType(type)).Where(type => type != null);
|
||||
m_nodeManagerFactory = new List<INodeManagerFactory>();
|
||||
foreach (var nodeManagerFactory in factories)
|
||||
{
|
||||
m_nodeManagerFactory.Add(nodeManagerFactory);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
private IList<INodeManagerFactory> m_nodeManagerFactory;
|
||||
private ICertificateValidator m_userCertificateValidator;
|
||||
#endregion
|
||||
}
|
||||
}
|
82
Plugins/Plugin/UA.Server/ReferenceServerConfiguration.cs
Normal file
82
Plugins/Plugin/UA.Server/ReferenceServerConfiguration.cs
Normal file
@ -0,0 +1,82 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Quickstarts.ReferenceServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the configuration the data access node manager.
|
||||
/// </summary>
|
||||
[DataContract(Namespace = Namespaces.ReferenceServer)]
|
||||
public class ReferenceServerConfiguration
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// The default constructor.
|
||||
/// </summary>
|
||||
public ReferenceServerConfiguration()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object during deserialization.
|
||||
/// </summary>
|
||||
[OnDeserializing()]
|
||||
private void Initialize(StreamingContext context)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets private members to default values.
|
||||
/// </summary>
|
||||
private static void Initialize()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
/// <summary>
|
||||
/// Whether the user dialog for accepting invalid certificates should be displayed.
|
||||
/// </summary>
|
||||
[DataMember(Order = 1)]
|
||||
public bool ShowCertificateValidationDialog
|
||||
{
|
||||
get { return m_showCertificateValidationDialog; }
|
||||
set { m_showCertificateValidationDialog = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Members
|
||||
private bool m_showCertificateValidationDialog;
|
||||
#endregion
|
||||
}
|
||||
}
|
267
Plugins/Plugin/UA.Server/UAServer.cs
Normal file
267
Plugins/Plugin/UA.Server/UAServer.cs
Normal file
@ -0,0 +1,267 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Configuration;
|
||||
using Opc.Ua.Server;
|
||||
|
||||
namespace Quickstarts
|
||||
{
|
||||
public class UAServer<T> where T : StandardServer, new()
|
||||
{
|
||||
public ApplicationInstance Application => m_application;
|
||||
public ApplicationConfiguration Configuration => m_application.ApplicationConfiguration;
|
||||
|
||||
public bool AutoAccept { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
public ExitCode ExitCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ctor of the server.
|
||||
/// </summary>
|
||||
/// <param name="writer">The text output.</param>
|
||||
public UAServer(TextWriter writer)
|
||||
{
|
||||
m_output = writer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the application configuration.
|
||||
/// </summary>
|
||||
public async Task LoadAsync(string applicationName, string configSectionName)
|
||||
{
|
||||
try
|
||||
{
|
||||
ExitCode = ExitCode.ErrorNotStarted;
|
||||
|
||||
ApplicationInstance.MessageDlg = new ApplicationMessageDlg(m_output);
|
||||
CertificatePasswordProvider PasswordProvider = new CertificatePasswordProvider(Password);
|
||||
m_application = new ApplicationInstance {
|
||||
ApplicationName = applicationName,
|
||||
ApplicationType = ApplicationType.Server,
|
||||
ConfigSectionName = configSectionName,
|
||||
CertificatePasswordProvider = PasswordProvider
|
||||
};
|
||||
|
||||
// load the application configuration.
|
||||
await m_application.LoadApplicationConfiguration(false).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ErrorExitException(ex.Message, ExitCode);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the application configuration.
|
||||
/// </summary>
|
||||
public async Task CheckCertificateAsync(bool renewCertificate)
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = m_application.ApplicationConfiguration;
|
||||
if (renewCertificate)
|
||||
{
|
||||
//await m_application.DeleteApplicationInstanceCertificate().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// check the application certificate.
|
||||
bool haveAppCertificate = await m_application.CheckApplicationInstanceCertificate(false, minimumKeySize: 0).ConfigureAwait(false);
|
||||
if (!haveAppCertificate)
|
||||
{
|
||||
throw new Exception("Application instance certificate invalid!");
|
||||
}
|
||||
|
||||
if (!config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
|
||||
{
|
||||
config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ErrorExitException(ex.Message, ExitCode);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the server.
|
||||
/// </summary>
|
||||
public async Task StartAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// create the server.
|
||||
m_server = new T();
|
||||
|
||||
// start the server
|
||||
await m_application.Start(m_server).ConfigureAwait(false);
|
||||
|
||||
// save state
|
||||
ExitCode = ExitCode.ErrorRunning;
|
||||
|
||||
// print endpoint info
|
||||
var endpoints = m_application.Server.GetEndpoints().Select(e => e.EndpointUrl).Distinct();
|
||||
foreach (var endpoint in endpoints)
|
||||
{
|
||||
Console.WriteLine(endpoint);
|
||||
}
|
||||
|
||||
// start the status thread
|
||||
m_status = Task.Run(StatusThreadAsync);
|
||||
|
||||
// print notification on session events
|
||||
m_server.CurrentInstance.SessionManager.SessionActivated += EventStatus;
|
||||
m_server.CurrentInstance.SessionManager.SessionClosing += EventStatus;
|
||||
m_server.CurrentInstance.SessionManager.SessionCreated += EventStatus;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ErrorExitException(ex.Message, ExitCode);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the server.
|
||||
/// </summary>
|
||||
public async Task StopAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_server != null)
|
||||
{
|
||||
using (T server = m_server)
|
||||
{
|
||||
// Stop status thread
|
||||
m_server = null;
|
||||
await m_status.ConfigureAwait(false);
|
||||
|
||||
// Stop server and dispose
|
||||
server.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
ExitCode = ExitCode.Ok;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ErrorExitException(ex.Message, ExitCode.ErrorStopping);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The certificate validator is used
|
||||
/// if auto accept is not selected in the configuration.
|
||||
/// </summary>
|
||||
private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
|
||||
{
|
||||
if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
|
||||
{
|
||||
if (AutoAccept)
|
||||
{
|
||||
Console.WriteLine("Accepted Certificate: [{0}] [{1}]", e.Certificate.Subject, e.Certificate.Thumbprint);
|
||||
e.Accept = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Console.WriteLine("Rejected Certificate: {0} [{1}] [{2}]", e.Error, e.Certificate.Subject, e.Certificate.Thumbprint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the session status.
|
||||
/// </summary>
|
||||
private void EventStatus(Session session, SessionEventReason reason)
|
||||
{
|
||||
m_lastEventTime = DateTime.UtcNow;
|
||||
PrintSessionStatus(session, reason.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output the status of a connected session.
|
||||
/// </summary>
|
||||
private void PrintSessionStatus(Session session, string reason, bool lastContact = false)
|
||||
{
|
||||
lock (session.DiagnosticsLock)
|
||||
{
|
||||
StringBuilder item = new StringBuilder();
|
||||
item.AppendFormat("{0,9}:{1,20}:", reason, session.SessionDiagnostics.SessionName);
|
||||
if (lastContact)
|
||||
{
|
||||
item.AppendFormat("Last Event:{0:HH:mm:ss}", session.SessionDiagnostics.ClientLastContactTime.ToLocalTime());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (session.Identity != null)
|
||||
{
|
||||
item.AppendFormat(":{0,20}", session.Identity.DisplayName);
|
||||
}
|
||||
item.AppendFormat(":{0}", session.Id);
|
||||
}
|
||||
Console.WriteLine(item.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status thread, prints connection status every 10 seconds.
|
||||
/// </summary>
|
||||
private async Task StatusThreadAsync()
|
||||
{
|
||||
while (m_server != null)
|
||||
{
|
||||
if (DateTime.UtcNow - m_lastEventTime > TimeSpan.FromMilliseconds(10000))
|
||||
{
|
||||
IList<Session> sessions = m_server.CurrentInstance.SessionManager.GetSessions();
|
||||
for (int ii = 0; ii < sessions.Count; ii++)
|
||||
{
|
||||
Session session = sessions[ii];
|
||||
PrintSessionStatus(session, "-Status-", true);
|
||||
}
|
||||
m_lastEventTime = DateTime.UtcNow;
|
||||
}
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
#region Private Members
|
||||
private readonly TextWriter m_output;
|
||||
private ApplicationInstance m_application;
|
||||
private T m_server;
|
||||
private Task m_status;
|
||||
private DateTime m_lastEventTime;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
42
Plugins/Plugin/UAService.cs
Normal file
42
Plugins/Plugin/UAService.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Opc.Ua;
|
||||
using Quickstarts;
|
||||
using Quickstarts.ReferenceServer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Plugin
|
||||
{
|
||||
public class UAService : IHostedService
|
||||
{
|
||||
string applicationName = "ConsoleReferenceServer";
|
||||
string configSectionName = "Quickstarts.ReferenceServer";
|
||||
|
||||
UAServer<ReferenceServer> server = null;
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
server = new UAServer<ReferenceServer>(null)
|
||||
{
|
||||
AutoAccept = false,
|
||||
Password = null
|
||||
};
|
||||
|
||||
server.LoadAsync(applicationName, configSectionName).ConfigureAwait(false);
|
||||
server.CheckCertificateAsync(false).ConfigureAwait(false);
|
||||
server.StartAsync().ConfigureAwait(false);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
server.StopAsync().ConfigureAwait(false);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user