mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-22 01:26:20 +08:00
ddec44102e
Replaced the dual license with MIT to make it consistent with all other sample source files.
359 lines
14 KiB
C#
359 lines
14 KiB
C#
/* ========================================================================
|
|
* Copyright (c) 2005-2019 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 Mono.Options;
|
|
using Opc.Ua;
|
|
using Opc.Ua.Client;
|
|
using Opc.Ua.Configuration;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace NetCoreConsoleClient
|
|
{
|
|
|
|
public enum ExitCode : int
|
|
{
|
|
Ok = 0,
|
|
ErrorCreateApplication = 0x11,
|
|
ErrorDiscoverEndpoints = 0x12,
|
|
ErrorCreateSession = 0x13,
|
|
ErrorBrowseNamespace = 0x14,
|
|
ErrorCreateSubscription = 0x15,
|
|
ErrorMonitoredItem = 0x16,
|
|
ErrorAddSubscription = 0x17,
|
|
ErrorRunning = 0x18,
|
|
ErrorNoKeepAlive = 0x30,
|
|
ErrorInvalidCommandLine = 0x100
|
|
};
|
|
|
|
public class Program
|
|
{
|
|
public static int Main(string[] args)
|
|
{
|
|
Console.WriteLine(
|
|
(Utils.IsRunningOnMono() ? "Mono" : ".Net Core") +
|
|
" OPC UA Console Client sample");
|
|
|
|
// command line options
|
|
bool showHelp = false;
|
|
int stopTimeout = Timeout.Infinite;
|
|
bool autoAccept = false;
|
|
|
|
Mono.Options.OptionSet options = new Mono.Options.OptionSet {
|
|
{ "h|help", "show this message and exit", h => showHelp = h != null },
|
|
{ "a|autoaccept", "auto accept certificates (for testing only)", a => autoAccept = a != null },
|
|
{ "t|timeout=", "the number of seconds until the client stops.", (int t) => stopTimeout = t }
|
|
};
|
|
|
|
IList<string> extraArgs = null;
|
|
try
|
|
{
|
|
extraArgs = options.Parse(args);
|
|
if (extraArgs.Count > 1)
|
|
{
|
|
foreach (string extraArg in extraArgs)
|
|
{
|
|
Console.WriteLine("Error: Unknown option: {0}", extraArg);
|
|
showHelp = true;
|
|
}
|
|
}
|
|
}
|
|
catch (OptionException e)
|
|
{
|
|
Console.WriteLine(e.Message);
|
|
showHelp = true;
|
|
}
|
|
|
|
if (showHelp)
|
|
{
|
|
// show some app description message
|
|
Console.WriteLine(Utils.IsRunningOnMono() ?
|
|
"Usage: mono MonoConsoleClient.exe [OPTIONS] [ENDPOINTURL]" :
|
|
"Usage: dotnet NetCoreConsoleClient.dll [OPTIONS] [ENDPOINTURL]");
|
|
Console.WriteLine();
|
|
|
|
// output the options
|
|
Console.WriteLine("Options:");
|
|
options.WriteOptionDescriptions(Console.Out);
|
|
return (int)ExitCode.ErrorInvalidCommandLine;
|
|
}
|
|
|
|
string endpointURL;
|
|
if (extraArgs.Count == 0)
|
|
{
|
|
// use OPC UA .Net Sample server
|
|
endpointURL = "opc.tcp://localhost:51210/UA/SampleServer";
|
|
}
|
|
else
|
|
{
|
|
endpointURL = extraArgs[0];
|
|
}
|
|
|
|
MySampleClient client = new MySampleClient(endpointURL, autoAccept, stopTimeout);
|
|
client.Run();
|
|
|
|
return (int)MySampleClient.ExitCode;
|
|
}
|
|
}
|
|
|
|
public class MySampleClient
|
|
{
|
|
const int ReconnectPeriod = 10;
|
|
Session session;
|
|
SessionReconnectHandler reconnectHandler;
|
|
string endpointURL;
|
|
int clientRunTime = Timeout.Infinite;
|
|
static bool autoAccept = false;
|
|
static ExitCode exitCode;
|
|
|
|
public MySampleClient(string _endpointURL, bool _autoAccept, int _stopTimeout)
|
|
{
|
|
endpointURL = _endpointURL;
|
|
autoAccept = _autoAccept;
|
|
clientRunTime = _stopTimeout <= 0 ? Timeout.Infinite : _stopTimeout * 1000;
|
|
}
|
|
|
|
public void Run()
|
|
{
|
|
try
|
|
{
|
|
ConsoleSampleClient().Wait();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Utils.Trace("ServiceResultException:" + ex.Message);
|
|
Console.WriteLine("Exception: {0}", ex.Message);
|
|
return;
|
|
}
|
|
|
|
ManualResetEvent quitEvent = new ManualResetEvent(false);
|
|
try
|
|
{
|
|
Console.CancelKeyPress += (sender, eArgs) =>
|
|
{
|
|
quitEvent.Set();
|
|
eArgs.Cancel = true;
|
|
};
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
// wait for timeout or Ctrl-C
|
|
quitEvent.WaitOne(clientRunTime);
|
|
|
|
// return error conditions
|
|
if (session.KeepAliveStopped)
|
|
{
|
|
exitCode = ExitCode.ErrorNoKeepAlive;
|
|
return;
|
|
}
|
|
|
|
exitCode = ExitCode.Ok;
|
|
}
|
|
|
|
public static ExitCode ExitCode { get => exitCode; }
|
|
|
|
private async Task ConsoleSampleClient()
|
|
{
|
|
Console.WriteLine("1 - Create an Application Configuration.");
|
|
exitCode = ExitCode.ErrorCreateApplication;
|
|
|
|
ApplicationInstance application = new ApplicationInstance
|
|
{
|
|
ApplicationName = "UA Core Sample Client",
|
|
ApplicationType = ApplicationType.Client,
|
|
ConfigSectionName = Utils.IsRunningOnMono() ? "Opc.Ua.MonoSampleClient" : "Opc.Ua.SampleClient"
|
|
};
|
|
|
|
// load the application configuration.
|
|
ApplicationConfiguration config = await application.LoadApplicationConfiguration(false);
|
|
|
|
// check the application certificate.
|
|
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);
|
|
if (!haveAppCertificate)
|
|
{
|
|
throw new Exception("Application instance certificate invalid!");
|
|
}
|
|
|
|
if (haveAppCertificate)
|
|
{
|
|
config.ApplicationUri = Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate);
|
|
if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
|
|
{
|
|
autoAccept = true;
|
|
}
|
|
config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine(" WARN: missing application certificate, using unsecure connection.");
|
|
}
|
|
|
|
Console.WriteLine("2 - Discover endpoints of {0}.", endpointURL);
|
|
exitCode = ExitCode.ErrorDiscoverEndpoints;
|
|
var selectedEndpoint = CoreClientUtils.SelectEndpoint(endpointURL, haveAppCertificate, 15000);
|
|
Console.WriteLine(" Selected endpoint uses: {0}",
|
|
selectedEndpoint.SecurityPolicyUri.Substring(selectedEndpoint.SecurityPolicyUri.LastIndexOf('#') + 1));
|
|
|
|
Console.WriteLine("3 - Create a session with OPC UA server.");
|
|
exitCode = ExitCode.ErrorCreateSession;
|
|
var endpointConfiguration = EndpointConfiguration.Create(config);
|
|
var endpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);
|
|
session = await Session.Create(config, endpoint, false, "OPC UA Console Client", 60000, new UserIdentity(new AnonymousIdentityToken()), null);
|
|
|
|
// register keep alive handler
|
|
session.KeepAlive += Client_KeepAlive;
|
|
|
|
Console.WriteLine("4 - Browse the OPC UA server namespace.");
|
|
exitCode = ExitCode.ErrorBrowseNamespace;
|
|
ReferenceDescriptionCollection references;
|
|
Byte[] continuationPoint;
|
|
|
|
references = session.FetchReferences(ObjectIds.ObjectsFolder);
|
|
|
|
session.Browse(
|
|
null,
|
|
null,
|
|
ObjectIds.ObjectsFolder,
|
|
0u,
|
|
BrowseDirection.Forward,
|
|
ReferenceTypeIds.HierarchicalReferences,
|
|
true,
|
|
(uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method,
|
|
out continuationPoint,
|
|
out references);
|
|
|
|
Console.WriteLine(" DisplayName, BrowseName, NodeClass");
|
|
foreach (var rd in references)
|
|
{
|
|
Console.WriteLine(" {0}, {1}, {2}", rd.DisplayName, rd.BrowseName, rd.NodeClass);
|
|
ReferenceDescriptionCollection nextRefs;
|
|
byte[] nextCp;
|
|
session.Browse(
|
|
null,
|
|
null,
|
|
ExpandedNodeId.ToNodeId(rd.NodeId, session.NamespaceUris),
|
|
0u,
|
|
BrowseDirection.Forward,
|
|
ReferenceTypeIds.HierarchicalReferences,
|
|
true,
|
|
(uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method,
|
|
out nextCp,
|
|
out nextRefs);
|
|
|
|
foreach (var nextRd in nextRefs)
|
|
{
|
|
Console.WriteLine(" + {0}, {1}, {2}", nextRd.DisplayName, nextRd.BrowseName, nextRd.NodeClass);
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("5 - Create a subscription with publishing interval of 1 second.");
|
|
exitCode = ExitCode.ErrorCreateSubscription;
|
|
var subscription = new Subscription(session.DefaultSubscription) { PublishingInterval = 1000 };
|
|
|
|
Console.WriteLine("6 - Add a list of items (server current time and status) to the subscription.");
|
|
exitCode = ExitCode.ErrorMonitoredItem;
|
|
var list = new List<MonitoredItem> {
|
|
new MonitoredItem(subscription.DefaultItem)
|
|
{
|
|
DisplayName = "ServerStatusCurrentTime", StartNodeId = "i="+Variables.Server_ServerStatus_CurrentTime.ToString()
|
|
}
|
|
};
|
|
list.ForEach(i => i.Notification += OnNotification);
|
|
subscription.AddItems(list);
|
|
|
|
Console.WriteLine("7 - Add the subscription to the session.");
|
|
exitCode = ExitCode.ErrorAddSubscription;
|
|
session.AddSubscription(subscription);
|
|
subscription.Create();
|
|
|
|
Console.WriteLine("8 - Running...Press Ctrl-C to exit...");
|
|
exitCode = ExitCode.ErrorRunning;
|
|
}
|
|
|
|
private void Client_KeepAlive(Session sender, KeepAliveEventArgs e)
|
|
{
|
|
if (e.Status != null && ServiceResult.IsNotGood(e.Status))
|
|
{
|
|
Console.WriteLine("{0} {1}/{2}", e.Status, sender.OutstandingRequestCount, sender.DefunctRequestCount);
|
|
|
|
if (reconnectHandler == null)
|
|
{
|
|
Console.WriteLine("--- RECONNECTING ---");
|
|
reconnectHandler = new SessionReconnectHandler();
|
|
reconnectHandler.BeginReconnect(sender, ReconnectPeriod * 1000, Client_ReconnectComplete);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Client_ReconnectComplete(object sender, EventArgs e)
|
|
{
|
|
// ignore callbacks from discarded objects.
|
|
if (!Object.ReferenceEquals(sender, reconnectHandler))
|
|
{
|
|
return;
|
|
}
|
|
|
|
session = reconnectHandler.Session;
|
|
reconnectHandler.Dispose();
|
|
reconnectHandler = null;
|
|
|
|
Console.WriteLine("--- RECONNECTED ---");
|
|
}
|
|
|
|
private static void OnNotification(MonitoredItem item, MonitoredItemNotificationEventArgs e)
|
|
{
|
|
foreach (var value in item.DequeueValues())
|
|
{
|
|
Console.WriteLine("{0}: {1}, {2}, {3}", item.DisplayName, value.Value, value.SourceTimestamp, value.StatusCode);
|
|
}
|
|
}
|
|
|
|
private static void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
|
|
{
|
|
if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
|
|
{
|
|
e.Accept = autoAccept;
|
|
if (autoAccept)
|
|
{
|
|
Console.WriteLine("Accepted Certificate: {0}", e.Certificate.Subject);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("Rejected Certificate: {0}", e.Certificate.Subject);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|