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<IoTBackgroundService>();
|
||||||
|
services.AddHostedService<UAService>();
|
||||||
services.AddSingleton<DeviceService>();
|
services.AddSingleton<DeviceService>();
|
||||||
services.AddSingleton<DrvierService>();
|
services.AddSingleton<DrvierService>();
|
||||||
services.AddSingleton<MyMqttClient>();
|
services.AddSingleton<MyMqttClient>();
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DynamicExpresso.Core" Version="2.9.7" />
|
<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="MQTTnet" Version="3.1.1" />
|
||||||
|
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.4.367.75" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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