/* ======================================================================== * 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 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 { 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); } } } } }