/* Copyright (c) 1996-2016, OPC Foundation. All rights reserved. The source code in this file is covered under a dual-license scenario: - RCL: for OPC Foundation members in good-standing - GPL V2: everybody else RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ GNU General Public License as published by the Free Software Foundation; version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 This source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ using System; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using Mono.Options; using Opc.Ua; using Opc.Ua.Client; 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 { static ExitCode exitCode; public static int Main(string[] args) { Console.WriteLine(".Net Core OPC UA Console Client sample"); // command line options bool showHelp = false; int stopTimeout = Timeout.Infinite; bool autoAccept = true; 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 * 1000 } }; IList 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]; } try { ConsoleSampleClient(endpointURL, stopTimeout, autoAccept).Wait(); } catch (Exception e) { Console.WriteLine("Exit due to Exception: {0}", e.Message); } return (int)exitCode; } public static async Task ConsoleSampleClient(string endpointURL, int timeOut, bool autoAccept) { Console.WriteLine("1 - Create an Application Configuration."); exitCode = ExitCode.ErrorCreateApplication; Utils.SetTraceOutput(Utils.TraceOutput.DebugAndFile); var config = new ApplicationConfiguration() { ApplicationName = "UA Core Sample Client", ApplicationType = ApplicationType.Client, ApplicationUri = "urn:" + Utils.GetHostName() + ":OPCFoundation:CoreSampleClient", SecurityConfiguration = new SecurityConfiguration { ApplicationCertificate = new CertificateIdentifier { StoreType = "X509Store", StorePath = "CurrentUser\\My", SubjectName = "UA Core Sample Client" }, TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = "OPC Foundation/CertificateStores/UA Applications", }, TrustedIssuerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = "OPC Foundation/CertificateStores/UA Certificate Authorities", }, RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = "OPC Foundation/CertificateStores/RejectedCertificates", }, NonceLength = 32, AutoAcceptUntrustedCertificates = autoAccept }, TransportConfigurations = new TransportConfigurationCollection(), TransportQuotas = new TransportQuotas { OperationTimeout = 15000 }, ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 } }; await config.Validate(ApplicationType.Client); bool haveAppCertificate = config.SecurityConfiguration.ApplicationCertificate.Certificate != null; if (!haveAppCertificate) { Console.WriteLine(" INFO: Creating new application certificate: {0}", config.ApplicationName); X509Certificate2 certificate = CertificateFactory.CreateCertificate( config.SecurityConfiguration.ApplicationCertificate.StoreType, config.SecurityConfiguration.ApplicationCertificate.StorePath, null, config.ApplicationUri, config.ApplicationName, config.SecurityConfiguration.ApplicationCertificate.SubjectName, null, CertificateFactory.defaultKeySize, DateTime.UtcNow - TimeSpan.FromDays(1), CertificateFactory.defaultLifeTime, CertificateFactory.defaultHashSize, false, null, null ); config.SecurityConfiguration.ApplicationCertificate.Certificate = certificate; } haveAppCertificate = config.SecurityConfiguration.ApplicationCertificate.Certificate != null; if (haveAppCertificate) { config.ApplicationUri = Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate); if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates) { 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); var session = await Session.Create(config, endpoint, false, ".Net Core OPC UA Console Client", 60000, new UserIdentity(new AnonymousIdentityToken()), null); 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 { 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; ManualResetEvent quitEvent = new ManualResetEvent(false); try { Console.CancelKeyPress += (sender, eArgs) => { quitEvent.Set(); eArgs.Cancel = true; }; } catch { } // wait for timeout or Ctrl-C quitEvent.WaitOne(timeOut); // return error conditions if (session.KeepAliveStopped) { exitCode = ExitCode.ErrorNoKeepAlive; return; } exitCode = ExitCode.Ok; } 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) { Console.WriteLine("Accepted Certificate: {0}", e.Certificate.Subject); e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted); } } }