/// <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()); }
static void Main(string[] args) { var options = new Options(4); string file = "bingo.txt"; string dir = "."; uint count = 30; bool rtl = false; var opts = new Mono.Options.OptionSet { { "title=", "title of the bingo cards", t => options.title = t }, { "file=", "text file containing the values to put in the bingo cells (an answer to a line)", f => file = f }, { "center=", "value to be entered in center tile of all cards", c => options.center = c }, { "size=", "dimensions of the bingo cards", s => options.size = UInt32.Parse(s) }, { "count=", "number of bingo cards to create", c => count = UInt32.Parse(c) }, { "rtl", "create Right To Left cards", x => rtl = true }, }; try { opts.Parse(args); } catch (OptionException e) { Console.WriteLine(e.Message); return; } if (options.center != null) { if ((options.size & 1) != 1) { Console.WriteLine("Center should only be specified with odd sized cards"); return; } } var words = new List <string>(); using (var r = new System.IO.StreamReader(Path.Combine(dir, file))) { while (!r.EndOfStream) { string line = r.ReadLine(); if (!String.IsNullOrWhiteSpace(line)) { words.Add(line); } } } Console.WriteLine("Read {0} lines from {1}", words.Count, file); using (var w = new System.IO.StreamWriter(Path.Combine(dir, "output.html"), false, Encoding.UTF8)) { writeCards(w, options, words, dir, count, rtl); } Console.WriteLine("done"); }
public static int Main(string[] args) { Console.WriteLine( (Utils.IsRunningOnMono() ? "Mono" : ".Net Core") + " OPC UA Console Server sample"); // command line options bool showHelp = false; int stopTimeout = 0; bool autoAccept = false; string reverseConnectUrlString = null; Uri reverseConnectUrl = null; 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 server stops.", (int t) => stopTimeout = t }, { "r|reverse=", "a url for a reverse connection", (string r) => reverseConnectUrlString = r } }; try { IList <string> extraArgs = options.Parse(args); foreach (string extraArg in extraArgs) { Console.WriteLine("Error: Unknown option: {0}", extraArg); showHelp = true; } if (reverseConnectUrlString != null) { reverseConnectUrl = new Uri(reverseConnectUrlString); } } catch (OptionException e) { Console.WriteLine(e.Message); showHelp = true; } if (showHelp) { Console.WriteLine(Utils.IsRunningOnMono() ? "Usage: mono NetCoreConsoleServer.exe [OPTIONS]" : "Usage: dotnet NetCoreConsoleServer.dll [OPTIONS]"); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return((int)ExitCode.ErrorInvalidCommandLine); } MySampleServer server = new MySampleServer(autoAccept, stopTimeout, reverseConnectUrl); server.Run(); return((int)MySampleServer.ExitCode); }
public static int Main(string[] args) { #region Open a thread for pipeServer int i; Thread[] servers = new Thread[Globals.numberThreads]; Console.WriteLine("Waiting for client connect...\n"); for (i = 0; i < Globals.numberThreads; i++) { servers[i] = new Thread(PipeServerThread); servers[i].Start(); } #endregion Console.WriteLine("{0} OPC UA Reference Server", Utils.IsRunningOnMono() ? "Mono" : ".Net Core"); // command line options bool showHelp = false; 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 } }; try { IList <string> extraArgs = options.Parse(args); 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) { Console.WriteLine(Utils.IsRunningOnMono() ? "Usage: mono MonoReferenceServer.exe [OPTIONS]" : "Usage: dotnet ConsoleReferenceServer.dll [OPTIONS]"); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return((int)ExitCode.ErrorInvalidCommandLine); } MyRefServer server = new MyRefServer(autoAccept); server.Run(); return((int)MyRefServer.ExitCode); }
public static async Task <int> MainAsync(string[] args) { string tests = ""; string correlationId = ""; string correlationIdFile = ""; bool waitForJobCompletion = false; var options = new Mono.Options.OptionSet { { "tests=", "Tests to run", param => { if (param != null) { tests = param; } } }, { "correlationIdFile=", "File to write correlation ID to", param => { if (param != null) { correlationIdFile = param; } } }, { "wait=", "Wait for job to complete", param => { if(param != null) { correlationId = param; waitForJobCompletion = true; } } }, }; try { options.Parse(args); } catch (Mono.Options.OptionException e) { Console.WriteLine("Option error: {0}", e.Message); return(1); } if (tests == "mainline" || tests == "mainline-cxx") { var t = new MainlineTests(tests); correlationId = await t.CreateJob().SendJob(); if (!String.IsNullOrEmpty(correlationIdFile)) { File.WriteAllText(correlationIdFile, correlationId); } return(0); } if (waitForJobCompletion) { var success = await new HelixBase().WaitForJobCompletion(correlationId); return(success ? 0 : 1); } Console.Error.WriteLine("Error: Invalid arguments."); return(1); }
public static async Task <int> Main(string[] args) { Console.WriteLine("{0} OPC UA Reference Server", Utils.IsRunningOnMono() ? "Mono" : ".Net Core"); // command line options bool showHelp = false; bool autoAccept = false; bool console = false; string password = null; 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 }, { "c|console", "log trace to console", c => console = c != null }, { "p|password="******"optional password for private key", (string p) => password = p } }; try { IList <string> extraArgs = options.Parse(args); 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) { Console.WriteLine(Utils.IsRunningOnMono() ? "Usage: mono MonoReferenceServer.exe [OPTIONS]" : "Usage: dotnet ConsoleReferenceServer.dll [OPTIONS]"); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return((int)ExitCode.ErrorInvalidCommandLine); } var server = new MyRefServer() { AutoAccept = autoAccept, LogConsole = console, Password = password }; await server.Run().ConfigureAwait(false); return((int)server.ExitCode); }
static void Main(string[] args) { var shouldShowHelp = false; var nodesetFile = default(string); var ns = default(string); var assembliesNames = new List <string>(); var p = new Mono.Options.OptionSet { { "f|nodeset=", "the nodeset file", f => nodesetFile = f }, { "n|namespace=", "the .NET namespace.", n => ns = n }, { "a|additional-assemblies=", "the .NET namespace.", a => assembliesNames.Add(a) }, { "h|help", "show this message and exit", h => shouldShowHelp = h != null }, }; try { p.Parse(args); } catch (OptionException e) { Console.Write("UaTypeGenerator.exe: "); Console.WriteLine(e.Message); Console.WriteLine("Try `UaTypeGenerator.exe --help' for more information."); return; } if (shouldShowHelp) { ShowHelp(p); return; } var assemblies = assembliesNames .Select(n => Assembly.LoadFrom(n)) .Prepend(typeof(OpenSecureChannelRequest).Assembly) .ToArray(); var nodeset = ReadNodeSet(nodesetFile); var typeset = new TypeSet(nodeset, assemblies); var typewriter = new TypeSetWriter(typeset, ns); var outputFile = Path.ChangeExtension(nodesetFile, ".cs"); using (var writer = File.CreateText(outputFile)) { var intendedWriter = new IndentedTextWriter(writer); typewriter.Write(intendedWriter); } }
public static int Main(string[] args) { Console.WriteLine(Name); // command line options bool showHelp = false; var opcVaultOptions = new OpcVaultApiOptions(); var azureADOptions = new OpcVaultAzureADOptions(); Mono.Options.OptionSet options = new Mono.Options.OptionSet { { "v|vault=", "OpcVault Url", g => opcVaultOptions.BaseAddress = g }, { "r|resource=", "OpcVault Resource Id", r => opcVaultOptions.ResourceId = r }, { "c|clientid=", "AD Client Id", c => azureADOptions.ClientId = c }, { "s|secret=", "AD Client Secret", s => azureADOptions.ClientSecret = s }, { "a|authority=", "Authority", a => azureADOptions.Authority = a }, { "t|tenantid=", "Tenant Id", t => azureADOptions.TenantId = t }, { "h|help", "show this message and exit", h => showHelp = h != null }, }; try { IList <string> extraArgs = options.Parse(args); 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) { Console.WriteLine("Usage: dotnet Microsoft.Azure.IIoT.OpcUa.Modules.Vault.dll [OPTIONS]"); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return((int)ExitCode.ErrorInvalidCommandLine); } var server = new VaultGlobalDiscoveryServer(); server.Run(opcVaultOptions, azureADOptions); return((int)VaultGlobalDiscoveryServer.ExitCode); }
public static int Main(string[] args) { Console.WriteLine("OPC UA Reference Server.Net Core"); // command line options bool showHelp = false; bool autoAccept = false; bool console = 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 }, { "c|console", "log trace to console", c => console = c != null } }; try { IList <string> extraArgs = options.Parse(args); 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) { Console.WriteLine("Usage: dotnet ConsoleReferenceServer.dll [OPTIONS]"); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return((int)ExitCode.ErrorInvalidCommandLine); } MyRefServer server = new MyRefServer(autoAccept, console); server.Run(); return((int)MyRefServer.ExitCode); }
/// <summary> /// The main entry point for the application. /// </summary> public static int Main(string[] args) { Console.WriteLine("SampleCompany {0} OPC UA Sample Server", Utils.IsRunningOnMono() ? "Mono" : ".NET Core"); // command line options var showHelp = false; var stopTimeout = 0; var autoAccept = false; var 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 server stops.", (int t) => stopTimeout = t }, }; try { IList <string> extraArgs = options.Parse(args); foreach (var extraArg in extraArgs) { Console.WriteLine("Error: Unknown option: {0}", extraArg); showHelp = true; } } catch (OptionException e) { Console.WriteLine(e.Message); showHelp = true; } if (showHelp) { Console.WriteLine(Utils.IsRunningOnMono() ? "Usage: mono SampleCompany.SampleServer.exe [OPTIONS]" : "Usage: dotnet SampleCompany.SampleServer.dll [OPTIONS]"); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return((int)ExitCode.ErrorInvalidCommandLine); } var server = new MySampleServer(autoAccept, stopTimeout); server.Run(); return((int)MySampleServer.ExitCode); }
public static int Main(string[] args) { Console.WriteLine(".Net Core OPC UA Global Discovery Server"); // command line options bool showHelp = false; Mono.Options.OptionSet options = new Mono.Options.OptionSet { { "h|help", "show this message and exit", h => showHelp = h != null }, }; try { IList <string> extraArgs = options.Parse(args); 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) { Console.WriteLine("Usage: dotnet NetCoreGlobalDiscoveryServer.dll [OPTIONS]"); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return((int)ExitCode.ErrorInvalidCommandLine); } NetCoreGlobalDiscoveryServer server = new NetCoreGlobalDiscoveryServer(); server.Run(); return((int)NetCoreGlobalDiscoveryServer.ExitCode); }
public Collage(string [] args) { bool showHelp = false; var options = new Mono.Options.OptionSet() { { "cols=", (int cols) => columns = cols }, { "cellsize=", (int cell) => cellsize = cell }, { "output=", (string file) => output = file }, { "h|?|help", v => showHelp = true }, }; void Help() { Console.WriteLine("collage [options] DIRECTORY\n" + $"Default output is {output}, columns {columns}, cell size {cellsize}"); options.WriteOptionDescriptions(Console.Error); Environment.Exit(0); } if (showHelp) { Help(); } var dir = options.Parse(args).FirstOrDefault(); if (dir == null) { Help(); } if (dir != null) { directory = dir; } }
public static int Main(string[] args) { Console.WriteLine(".Net Core OPC UA Complex Types Client sample"); // command line options bool showHelp = false; int stopTimeout = Timeout.Infinite; bool autoAccept = false; bool writeComplexInt = false; bool noloadTypes = false; bool verbose = false; bool json = 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 }, { "w|writeint", "Read and increment all complex types with an Int32.", w => writeComplexInt = w != null }, { "n|noloadtypes", "Load the type system dictionary from the server.", n => noloadTypes = n != null }, { "v|verbose", "Verbose output.", v => verbose = v != null }, { "j|json", "Print custom nodes as Json.", j => json = j != null }, }; 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("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) { Verbose = verbose, LoadTypeSystem = !noloadTypes, WriteComplexInt = writeComplexInt, PrintAsJson = json }; return((int)client.Run()); }
/// <summary> /// Asynchronous part of the main method of the app. /// </summary> public async static Task MainAsync(string[] args) { var shouldShowHelp = false; // command line options Mono.Options.OptionSet options = new Mono.Options.OptionSet { // opc server configuration options { "lf|logfile=", $"the filename of the logfile to use.\nDefault: '{_logFileName}'", (string l) => _logFileName = l }, { "ll|loglevel=", "the loglevel to use (allowed: fatal, error, warn, info, debug, verbose).\nDefault: info", (string l) => { List <string> logLevels = new List <string> { "fatal", "error", "warn", "info", "debug", "verbose" }; if (logLevels.Contains(l.ToLowerInvariant())) { _logLevel = l.ToLowerInvariant(); } else { throw new OptionException("The loglevel must be one of: fatal, error, warn, info, debug, verbose", "loglevel"); } } }, { "pn|portnum=", $"the server port of the OPC server endpoint.\nDefault: {ServerPort}", (ushort p) => ServerPort = p }, { "op|path=", $"the enpoint URL path part of the OPC server endpoint.\nDefault: '{ServerPath}'", (string a) => ServerPath = a }, { "ga|generatealerts", $"the station should generate alerts.\nDefault: {GenerateAlerts}", g => GenerateAlerts = g != null }, { "pc|powerconsumption=", $"the stations average power consumption in kW\nDefault: {PowerConsumption} kW", (double d) => PowerConsumption = d }, { "ct|cycletime=", $"the stations cycle time in seconds\nDefault: {IdealCycleTimeDefault} sec", (ulong ul) => IdealCycleTimeDefault = ul * 1000 }, { "lr|ldsreginterval=", $"the LDS(-ME) registration interval in ms. If 0, then the registration is disabled.\nDefault: {LdsRegistrationInterval}", (int i) => { if (i >= 0) { LdsRegistrationInterval = i; } else { throw new OptionException("The ldsreginterval must be larger or equal 0.", "ldsreginterval"); } } }, { "aa|autoacceptcerts", $"all certs are trusted when a connection is established.\nDefault: {AutoAcceptCerts}", a => AutoAcceptCerts = a != null }, { "to|trustowncert", $"the cfstation certificate is put into the trusted certificate store automatically.\nDefault: {TrustMyself}", t => TrustMyself = t != null }, // cert store options { "at|appcertstoretype=", $"the own application cert store type. \n(allowed values: Directory, X509Store)\nDefault: '{OpcOwnCertStoreType}'", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcOwnCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : CertificateStoreType.Directory; OpcOwnCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcOwnCertX509StorePathDefault : OpcOwnCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ap|appcertstorepath=", "the path where the own application cert should be stored\nDefault (depends on store type):\n" + $"X509Store: '{OpcOwnCertX509StorePathDefault}'\n" + $"Directory: '{OpcOwnCertDirectoryStorePathDefault}'", (string s) => OpcOwnCertStorePath = s }, { "tp|trustedcertstorepath=", $"the path of the trusted cert store\nDefault: '{OpcTrustedCertDirectoryStorePathDefault}'", (string s) => OpcTrustedCertStorePath = s }, { "rp|rejectedcertstorepath=", $"the path of the rejected cert store\nDefault '{OpcRejectedCertDirectoryStorePathDefault}'", (string s) => OpcRejectedCertStorePath = s }, { "ip|issuercertstorepath=", $"the path of the trusted issuer cert store\nDefault '{OpcIssuerCertDirectoryStorePathDefault}'", (string s) => OpcIssuerCertStorePath = s }, { "csr", $"show data to create a certificate signing request\nDefault '{ShowCreateSigningRequestInfo}'", c => ShowCreateSigningRequestInfo = c != null }, { "ab|applicationcertbase64=", "update/set this applications certificate with the certificate passed in as bas64 string", (string s) => NewCertificateBase64String = s }, { "af|applicationcertfile=", "update/set this applications certificate with the certificate file specified", (string s) => { if (File.Exists(s)) { NewCertificateFileName = s; } else { throw new OptionException("The file '{s}' does not exist.", "applicationcertfile"); } } }, { "pb|privatekeybase64=", "initial provisioning of the application certificate (with a PEM or PFX fomat) requires a private key passed in as base64 string", (string s) => PrivateKeyBase64String = s }, { "pk|privatekeyfile=", "initial provisioning of the application certificate (with a PEM or PFX fomat) requires a private key passed in as file", (string s) => { if (File.Exists(s)) { PrivateKeyFileName = s; } else { throw new OptionException("The file '{s}' does not exist.", "privatekeyfile"); } } }, { "cp|certpassword="******"the optional password for the PEM or PFX or the installed application certificate", (string s) => CertificatePassword = s }, { "tb|addtrustedcertbase64=", "adds the certificate to the applications trusted cert store passed in as base64 string (multiple strings supported)", (string s) => TrustedCertificateBase64Strings.AddRange(ParseListOfStrings(s)) }, { "tf|addtrustedcertfile=", "adds the certificate file(s) to the applications trusted cert store passed in as base64 string (multiple filenames supported)", (string s) => TrustedCertificateFileNames.AddRange(ParseListOfFileNames(s, "addtrustedcertfile")) }, { "ib|addissuercertbase64=", "adds the specified issuer certificate to the applications trusted issuer cert store passed in as base64 string (multiple strings supported)", (string s) => IssuerCertificateBase64Strings.AddRange(ParseListOfStrings(s)) }, { "if|addissuercertfile=", "adds the specified issuer certificate file(s) to the applications trusted issuer cert store (multiple filenames supported)", (string s) => IssuerCertificateFileNames.AddRange(ParseListOfFileNames(s, "addissuercertfile")) }, { "rb|updatecrlbase64=", "update the CRL passed in as base64 string to the corresponding cert store (trusted or trusted issuer)", (string s) => CrlBase64String = s }, { "uc|updatecrlfile=", "update the CRL passed in as file to the corresponding cert store (trusted or trusted issuer)", (string s) => { if (File.Exists(s)) { CrlFileName = s; } else { throw new OptionException("The file '{s}' does not exist.", "updatecrlfile"); } } }, { "rc|removecert=", "remove cert(s) with the given thumbprint(s) (multiple thumbprints supported)", (string s) => ThumbprintsToRemove.AddRange(ParseListOfStrings(s)) }, // misc { "h|help", "show this message and exit", h => shouldShowHelp = h != null }, }; List <string> extraArgs = new List <string>(); try { // parse the command line extraArgs = options.Parse(args); } catch (OptionException e) { // initialize logging InitLogging(); // show message Logger.Fatal(e, "Error in command line options"); // show usage Usage(options); return; } // initialize logging InitLogging(); // check args if (extraArgs.Count != 0 || shouldShowHelp) { // show usage Usage(options); return; } try { await ConsoleServerAsync().ConfigureAwait(false); } catch (Exception ex) { Logger.Fatal(ex, "OPC UA server failed unexpectedly."); } Logger.Information("OPC UA server exiting..."); }
public static void Main(string[] args) { var opcTraceInitialized = false; try { var shouldShowHelp = false; // command line options configuration Mono.Options.OptionSet options = new Mono.Options.OptionSet { // Publishing configuration options { "pf|publishfile=", $"the filename to configure the nodes to publish.\nDefault: '{NodesToPublishAbsFilenameDefault}'", (string p) => NodesToPublishAbsFilename = p }, { "sd|shopfloordomain=", $"the domain of the shopfloor. if specified this domain is appended (delimited by a ':' to the 'ApplicationURI' property when telemetry is sent to IoTHub.\n" + "The value must follow the syntactical rules of a DNS hostname.\nDefault: not set", (string s) => { Regex domainNameRegex = new Regex("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"); if (domainNameRegex.IsMatch(s)) { ShopfloorDomain = s; } else { throw new OptionException("The shopfloor domain is not a valid DNS hostname.", "shopfloordomain"); } } }, { "sw|sessionconnectwait=", $"specify the wait time in seconds publisher is trying to connect to disconnected endpoints and starts monitoring unmonitored items\nMin: 10\nDefault: {PublisherSessionConnectWaitSec}", (int i) => { if (i > 10) { PublisherSessionConnectWaitSec = i; } else { throw new OptionException("The sessionconnectwait must be greater than 10 sec", "sessionconnectwait"); } } }, { "vc|verboseconsole=", $"the output of publisher is shown on the console.\nDefault: {VerboseConsole}", (bool b) => VerboseConsole = b }, // IoTHub specific options { "ih|iothubprotocol=", $"the protocol to use for communication with Azure IoTHub (allowed values: {string.Join(", ", Enum.GetNames(IotHubProtocol.GetType()))}).\nDefault: {Enum.GetName(IotHubProtocol.GetType(), IotHubProtocol)}", (Microsoft.Azure.Devices.Client.TransportType p) => IotHubProtocol = p }, { "ms|iothubmessagesize=", $"the max size of a message which can be send to IoTHub. when telemetry of this size is available it will be sent.\n0 will enforce immediate send when telemetry is available\nMin: 0\nMax: 256 * 1024\nDefault: {_MaxSizeOfIoTHubMessageBytes}", (uint u) => { if (u >= 0 && u <= 256 * 1024) { _MaxSizeOfIoTHubMessageBytes = u; } else { throw new OptionException("The iothubmessagesize must be in the range between 1 and 256*1024.", "iothubmessagesize"); } } }, { "si|iothubsendinterval=", $"the interval in seconds when telemetry should be send to IoTHub. If 0, then only the iothubmessagesize parameter controls when telemetry is sent.\nDefault: '{_DefaultSendIntervalSeconds}'", (int i) => { if (i >= 0) { _DefaultSendIntervalSeconds = i; } else { throw new OptionException("The iothubsendinterval must be larger or equal 0.", "iothubsendinterval"); } } }, // opc server configuration options { "lf|logfile=", $"the filename of the logfile to use.\nDefault: './Logs/<applicationname>.log.txt'", (string l) => LogFileName = l }, { "pn|portnum=", $"the server port of the publisher OPC server endpoint.\nDefault: {PublisherServerPort}", (ushort p) => PublisherServerPort = p }, { "pa|path=", $"the enpoint URL path part of the publisher OPC server endpoint.\nDefault: '{PublisherServerPath}'", (string a) => PublisherServerPath = a }, { "lr|ldsreginterval=", $"the LDS(-ME) registration interval in ms. If 0, then the registration is disabled.\nDefault: {LdsRegistrationInterval}", (int i) => { if (i >= 0) { LdsRegistrationInterval = i; } else { throw new OptionException("The ldsreginterval must be larger or equal 0.", "ldsreginterval"); } } }, { "ot|operationtimeout=", $"the operation timeout of the publisher OPC UA client in ms.\nDefault: {OpcOperationTimeout}", (int i) => { if (i >= 0) { OpcOperationTimeout = i; } else { throw new OptionException("The operation timeout must be larger or equal 0.", "operationtimeout"); } } }, { "oi|opcsamplinginterval=", "the publisher is using this as default value in milliseconds to request the servers to sample the nodes with this interval\n" + "this value might be revised by the OPC UA servers to a supported sampling interval.\n" + "please check the OPC UA specification for details how this is handled by the OPC UA stack.\n" + "a negative value will set the sampling interval to the publishing interval of the subscription this node is on.\n" + $"0 will configure the OPC UA server to sample in the highest possible resolution and should be taken with care.\nDefault: {OpcSamplingInterval}", (int i) => OpcSamplingInterval = i }, { "op|opcpublishinginterval=", "the publisher is using this as default value in milliseconds for the publishing interval setting of the subscriptions established to the OPC UA servers.\n" + "please check the OPC UA specification for details how this is handled by the OPC UA stack.\n" + $"a value less than or equal zero will let the server revise the publishing interval.\nDefault: {OpcPublishingInterval}", (int i) => { if (i > 0 && i >= OpcSamplingInterval) { OpcPublishingInterval = i; } else { if (i <= 0) { OpcPublishingInterval = 0; } else { throw new OptionException($"The opcpublishinterval ({i}) must be larger than the opcsamplinginterval ({OpcSamplingInterval}).", "opcpublishinterval"); } } } }, { "ct|createsessiontimeout=", $"specify the timeout in seconds used when creating a session to an endpoint. On unsuccessful connection attemps a backoff up to {OpcSessionCreationBackoffMax} times the specified timeout value is used.\nMin: 1\nDefault: {OpcSessionCreationTimeout}", (uint u) => { if (u > 1) { OpcSessionCreationTimeout = u; } else { throw new OptionException("The createsessiontimeout must be greater than 1 sec", "createsessiontimeout"); } } }, { "ki|keepaliveinterval=", $"specify the interval in seconds the publisher is sending keep alive messages to the OPC servers on the endpoints it is connected to.\nMin: 2\nDefault: {OpcKeepAliveIntervalInSec}", (int i) => { if (i >= 2) { OpcKeepAliveIntervalInSec = i; } else { throw new OptionException("The keepaliveinterval must be greater or equal 2", "keepalivethreshold"); } } }, { "kt|keepalivethreshold=", $"specify the number of keep alive packets a server can miss, before the session is disconneced\nMin: 1\nDefault: {OpcKeepAliveDisconnectThreshold}", (uint u) => { if (u > 1) { OpcKeepAliveDisconnectThreshold = u; } else { throw new OptionException("The keepalivethreshold must be greater than 1", "keepalivethreshold"); } } }, { "st|opcstacktracemask=", $"the trace mask for the OPC stack. See github OPC .NET stack for definitions.\nTo enable IoTHub telemetry tracing set it to 711.\nDefault: {OpcStackTraceMask:X} ({Program.OpcStackTraceMask})", (int i) => { if (i >= 0) { OpcStackTraceMask = i; } else { throw new OptionException("The OPC stack trace mask must be larger or equal 0.", "opcstacktracemask"); } } }, { "as|autotrustservercerts=", $"the publisher trusts all servers it is establishing a connection to.\nDefault: {OpcPublisherAutoTrustServerCerts}", (bool b) => OpcPublisherAutoTrustServerCerts = b }, // trust own public cert option { "tm|trustmyself=", $"the publisher certificate is put into the trusted certificate store automatically.\nDefault: {TrustMyself}", (bool b) => TrustMyself = b }, // own cert store options { "at|appcertstoretype=", $"the own application cert store type. \n(allowed values: Directory, X509Store)\nDefault: '{OpcOwnCertStoreType}'", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { OpcOwnCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; OpcOwnCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? _opcOwnCertX509StorePathDefault : _opcOwnCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ap|appcertstorepath=", $"the path where the own application cert should be stored\nDefault (depends on store type):\n" + $"X509Store: '{_opcOwnCertX509StorePathDefault}'\n" + $"Directory: '{_opcOwnCertDirectoryStorePathDefault}'", (string s) => OpcOwnCertStorePath = s }, // trusted cert store options { "tt|trustedcertstoretype=", $"the trusted cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcTrustedCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { OpcTrustedCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; OpcTrustedCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcTrustedCertX509StorePathDefault : OpcTrustedCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "tp|trustedcertstorepath=", $"the path of the trusted cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcTrustedCertX509StorePathDefault}'\n" + $"Directory: '{OpcTrustedCertDirectoryStorePathDefault}'", (string s) => OpcTrustedCertStorePath = s }, // rejected cert store options { "rt|rejectedcertstoretype=", $"the rejected cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcRejectedCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { OpcRejectedCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; OpcRejectedCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? _opcRejectedCertX509StorePathDefault : _opcRejectedCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "rp|rejectedcertstorepath=", $"the path of the rejected cert store\nDefault (depends on store type):\n" + $"X509Store: '{_opcRejectedCertX509StorePathDefault}'\n" + $"Directory: '{_opcRejectedCertDirectoryStorePathDefault}'", (string s) => OpcRejectedCertStorePath = s }, // issuer cert store options { "it|issuercertstoretype=", $"the trusted issuer cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcIssuerCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { OpcIssuerCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; OpcIssuerCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? _opcIssuerCertX509StorePathDefault : _opcIssuerCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ip|issuercertstorepath=", $"the path of the trusted issuer cert store\nDefault (depends on store type):\n" + $"X509Store: '{_opcIssuerCertX509StorePathDefault}'\n" + $"Directory: '{_opcIssuerCertDirectoryStorePathDefault}'", (string s) => OpcIssuerCertStorePath = s }, // device connection string cert store options { "dt|devicecertstoretype=", $"the iothub device cert store type. \n(allowed values: Directory, X509Store)\nDefault: {IotDeviceCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { IotDeviceCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; IotDeviceCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? _iotDeviceCertX509StorePathDefault : _iotDeviceCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "dp|devicecertstorepath=", $"the path of the iot device cert store\nDefault Default (depends on store type):\n" + $"X509Store: '{_iotDeviceCertX509StorePathDefault}'\n" + $"Directory: '{_iotDeviceCertDirectoryStorePathDefault}'", (string s) => IotDeviceCertStorePath = s }, // misc { "h|help", "show this message and exit", h => shouldShowHelp = h != null }, }; List <string> arguments; try { // parse the command line arguments = options.Parse(args); } catch (OptionException e) { // show message WriteLine($"Error: {e.Message}"); // show usage Usage(options); return; } // Validate and parse arguments. if (arguments.Count > 2 || shouldShowHelp) { Usage(options); return; } else if (arguments.Count == 2) { ApplicationName = arguments[0]; _IotHubOwnerConnectionString = arguments[1]; } else if (arguments.Count == 1) { ApplicationName = arguments[0]; } else { ApplicationName = Utils.GetHostName(); } WriteLine("Publisher is starting up..."); // init OPC configuration and tracing Init(OpcStackTraceMask, VerboseConsole); OpcStackConfiguration opcStackConfiguration = new OpcStackConfiguration(ApplicationName); opcTraceInitialized = true; OpcConfiguration = opcStackConfiguration.Configuration; // log shopfloor domain setting if (string.IsNullOrEmpty(ShopfloorDomain)) { Trace("There is no shopfloor domain configured."); } else { Trace($"Publisher is in shopfloor domain '{ShopfloorDomain}'."); } // Set certificate validator. if (OpcPublisherAutoTrustServerCerts) { Trace("Publisher configured to auto trust server certificates of the servers it is connecting to."); OpcConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_AutoTrustServerCerts); } else { Trace("Publisher configured to not auto trust server certificates. When connecting to servers, you need to manually copy the rejected server certs to the trusted store to trust them."); OpcConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_Default); } // start our server interface try { Trace($"Starting server on endpoint {OpcConfiguration.ServerConfiguration.BaseAddresses[0].ToString()} ..."); _publisherServer = new PublisherServer(); _publisherServer.Start(OpcConfiguration); Trace("Server started."); } catch (Exception e) { Trace(e, $"Failed to start Publisher OPC UA server."); Trace("exiting..."); return; } // get information on the nodes to publish and validate the json by deserializing it. try { PublishDataSemaphore.Wait(); if (string.IsNullOrEmpty(NodesToPublishAbsFilename)) { // check if we have an env variable specifying the published nodes path, otherwise use the default NodesToPublishAbsFilename = NodesToPublishAbsFilenameDefault; if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_PNFP"))) { Trace("Publishing node configuration file path read from environment."); NodesToPublishAbsFilename = Environment.GetEnvironmentVariable("_GW_PNFP"); } } Trace($"Attempting to load nodes file from: {NodesToPublishAbsFilename}"); PublisherConfigFileEntries = JsonConvert.DeserializeObject <List <PublisherConfigFileEntry> >(File.ReadAllText(NodesToPublishAbsFilename)); Trace($"Loaded {PublisherConfigFileEntries.Count.ToString()} config file entry/entries."); foreach (var publisherConfigFileEntry in PublisherConfigFileEntries) { if (publisherConfigFileEntry.NodeId == null) { // new node configuration syntax. foreach (var opcNode in publisherConfigFileEntry.OpcNodes) { PublishConfig.Add(new NodeToPublishConfig(ExpandedNodeId.Parse(opcNode.ExpandedNodeId), publisherConfigFileEntry.EndpointUri, opcNode.OpcSamplingInterval ?? OpcSamplingInterval, opcNode.OpcPublishingInterval ?? OpcPublishingInterval)); } } else { // legacy (using ns=) node configuration syntax using default sampling and publishing interval. PublishConfig.Add(new NodeToPublishConfig(publisherConfigFileEntry.NodeId, publisherConfigFileEntry.EndpointUri, OpcSamplingInterval, OpcPublishingInterval)); } } } catch (Exception e) { Trace(e, "Loading of the node configuration file failed. Does the file exist and has correct syntax?"); Trace("exiting..."); return; } finally { PublishDataSemaphore.Release(); } Trace($"There are {PublishConfig.Count.ToString()} nodes to publish."); // initialize and start IoTHub messaging IotHubCommunication = new IotHubMessaging(); if (!IotHubCommunication.Init(_IotHubOwnerConnectionString, _MaxSizeOfIoTHubMessageBytes, _DefaultSendIntervalSeconds)) { return; } // create a list to manage sessions, subscriptions and monitored items. try { PublishDataSemaphore.Wait(); OpcSessionsSemaphore.Wait(); var uniqueEndpointUrls = PublishConfig.Select(n => n.EndpointUri).Distinct(); foreach (var endpointUrl in uniqueEndpointUrls) { // create new session info. OpcSession opcSession = new OpcSession(endpointUrl, OpcSessionCreationTimeout); // create a subscription for each distinct publishing inverval var nodesDistinctPublishingInterval = PublishConfig.Where(n => n.EndpointUri.Equals(endpointUrl)).Select(c => c.OpcPublishingInterval).Distinct(); foreach (var nodeDistinctPublishingInterval in nodesDistinctPublishingInterval) { // create a subscription for the publishing interval and add it to the session. OpcSubscription opcSubscription = new OpcSubscription(nodeDistinctPublishingInterval); // add all nodes with this OPC publishing interval to this subscription. var nodesWithSamePublishingInterval = PublishConfig.Where(n => n.EndpointUri.Equals(endpointUrl)).Where(n => n.OpcPublishingInterval == nodeDistinctPublishingInterval); foreach (var nodeInfo in nodesWithSamePublishingInterval) { // differentiate if legacy (using ns=) or new syntax (using nsu=) is used if (nodeInfo.NodeId == null) { // create a monitored item for the node OpcMonitoredItem opcMonitoredItem = new OpcMonitoredItem(nodeInfo.ExpandedNodeId, opcSession.EndpointUri) { RequestedSamplingInterval = nodeInfo.OpcSamplingInterval, SamplingInterval = nodeInfo.OpcSamplingInterval }; opcSubscription.OpcMonitoredItems.Add(opcMonitoredItem); } else { // give user a warning that the syntax is obsolete Trace($"Please update the syntax of the configuration file and use ExpandedNodeId instead of NodeId property name for node with identifier '{nodeInfo.NodeId.ToString()}' on EndpointUrl '{nodeInfo.EndpointUri.AbsolutePath}'."); // create a monitored item for the node with the configured or default sampling interval OpcMonitoredItem opcMonitoredItem = new OpcMonitoredItem(nodeInfo.NodeId, opcSession.EndpointUri) { RequestedSamplingInterval = nodeInfo.OpcSamplingInterval, SamplingInterval = nodeInfo.OpcSamplingInterval }; opcSubscription.OpcMonitoredItems.Add(opcMonitoredItem); } } // add subscription to session. opcSession.OpcSubscriptions.Add(opcSubscription); } // add session. OpcSessions.Add(opcSession); } } finally { OpcSessionsSemaphore.Release(); PublishDataSemaphore.Release(); } // kick off the task to maintain all sessions var cts = new CancellationTokenSource(); Task.Run(async() => await SessionConnector(cts.Token)); // Show notification on session events _publisherServer.CurrentInstance.SessionManager.SessionActivated += ServerEventStatus; _publisherServer.CurrentInstance.SessionManager.SessionClosing += ServerEventStatus; _publisherServer.CurrentInstance.SessionManager.SessionCreated += ServerEventStatus; // stop on user request WriteLine(""); WriteLine(""); WriteLine("Publisher is running. Press ENTER to quit."); WriteLine(""); WriteLine(""); ReadLine(); cts.Cancel(); WriteLine("Publisher is shuting down..."); // close all connected session PublisherShutdownInProgress = true; // stop the server _publisherServer.Stop(); // Clean up Publisher sessions Task.Run(async() => await SessionShutdown()).Wait(); // shutdown the IoTHub messaging IotHubCommunication.Shutdown(); } catch (Exception e) { if (opcTraceInitialized) { Trace(e, e.StackTrace); e = e.InnerException ?? null; while (e != null) { Trace(e, e.StackTrace); e = e.InnerException ?? null; } Trace("Publisher exiting... "); } else { WriteLine($"{DateTime.Now.ToString()}: {e.Message.ToString()}"); WriteLine($"{DateTime.Now.ToString()}: {e.StackTrace}"); e = e.InnerException ?? null; while (e != null) { WriteLine($"{DateTime.Now.ToString()}: {e.Message.ToString()}"); WriteLine($"{DateTime.Now.ToString()}: {e.StackTrace}"); e = e.InnerException ?? null; } WriteLine($"{DateTime.Now.ToString()}: Publisher exiting..."); } } }
/// <summary> /// Asynchronous part of the main method of the app. /// </summary> public static async Task MainAsync(string[] args) { Mono.Options.OptionSet options = InitCommandLineOptions(); InitAppLocation(); InitLogging(); List <string> extraArgs; try { // parse the command line extraArgs = options.Parse(args); } catch (OptionException e) { // show message Logger.Fatal(e, "Error in command line options"); Logger.Error($"Command line arguments: {string.Join(" ", args)}"); // show usage Usage(options); return; } // show usage if requested if (ShowHelp) { Usage(options); return; } // validate and parse extra arguments if (extraArgs.Count > 0) { Logger.Error("Error in command line options"); Logger.Error($"Command line arguments: {string.Join(" ", args)}"); Usage(options); return; } //show version var fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); Logger.Information($"{ProgramName} V{fileVersion.ProductMajorPart}.{fileVersion.ProductMinorPart}.{fileVersion.ProductBuildPart} starting up..."); Logger.Debug($"Informational version: V{(Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute)?.InformationalVersion}"); using var host = CreateHostBuilder(args); if (ShowPublisherConfigJsonIp || ShowPublisherConfigJsonPh) { StartWebServer(host); } try { await ConsoleServerAsync(args).ConfigureAwait(false); } catch (Exception ex) { Logger.Fatal(ex, "OPC UA server failed unexpectedly."); } Logger.Information("OPC UA server exiting..."); }
public static int Main(string[] args) { Console.WriteLine(".Net Core OPC UA Complex Types Client sample"); // command line options bool showHelp = false; int stopTimeout = Timeout.Infinite; bool autoAccept = false; bool writeComplexInt = false; bool noTypes = false; bool noBrowse = false; bool verbose = false; bool json = false; bool jsonReversible = false; string username = null; string pw = null; string reverseConnectUrlString = null; Uri reverseConnectUrl = null; 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 }, { "w|writeint", "Read and increment all complex types with an Int32.", w => writeComplexInt = w != null }, { "n|notypes", "Do not load the type system dictionary from the server.", n => noTypes = n != null }, { "b|nobrowse", "Do not browse the address space of the server.", n => noBrowse = n != null }, { "u|username="******"Username to access server.", (string n) => username = n }, { "p|password="******"Password to access server.", (string n) => pw = n }, { "v|verbose", "Verbose output.", v => verbose = v != null }, { "j|json", "Print custom nodes as Json.", j => json = j != null }, { "r|jsonreversible", "Use Json reversible encoding.", r => jsonReversible = r != null }, { "rc|reverseconnect=", "Connect using the reverse connection.", (string url) => reverseConnectUrlString = url }, }; 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; } } if (reverseConnectUrlString != null) { reverseConnectUrl = new Uri(reverseConnectUrlString); } } catch (OptionException e) { Console.WriteLine(e.Message); showHelp = true; } if (showHelp) { // show some app description message Console.WriteLine("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) { Verbose = verbose, LoadTypeSystem = !noTypes, BrowseAdddressSpace = !noBrowse, WriteComplexInt = writeComplexInt, PrintAsJson = json, JsonReversible = jsonReversible, Username = username, Password = pw, ReverseConnectUri = reverseConnectUrl }; return((int)client.Run()); }
// ENTRY POINT public static int Main(string directory) { try { // parse options try { Options.Parse(new string[0] { }); } catch (OptionException e) { SayOptionException(e); return(RC_BAD_OPTIONS); } // handle operations that don't require reading the assembly switch (OptionsIn.PerformOperation) { case ReceivedOptions.Operation.Help: SayHelp(Options); return(RC_NORMAL); case ReceivedOptions.Operation.Version: SayVersion(); return(RC_NORMAL); } SayHeader(); var managedDirectory = directory.PA("BattleTech_Data").PA("Managed"); // find managed directory, setup assembly resolver to look there Log.M.TWL(0, managedDirectory); managedAssemblyResolver = new ManagedAssemblyResolver(managedDirectory); // setup paths to DLLs and backups var gameDLLPath = Path.Combine(managedDirectory, GAME_DLL_FILE_NAME); var gameDLLPatchPath = Path.Combine(managedDirectory, GAME_DLL_FILE_NAME + RENAME_FILE_EXT); if (File.Exists(gameDLLPatchPath)) { File.Delete(gameDLLPatchPath); } File.Move(gameDLLPath, gameDLLPatchPath); File.Copy(gameDLLPatchPath, gameDLLPath); var gameDLLBackupPath = Path.Combine(managedDirectory, GAME_DLL_FILE_NAME + BACKUP_FILE_EXT); var modTekDLLPath = directory.PA("Mods").PA("ModTek").PA(MODTEK_DLL_FILE_NAME); // Path.Combine(directory, MODTEK_DLL_FILE_NAME); var modDirectory = directory.PA("Mods"); string HarmonySrcDLLPath = Path.Combine(Path.GetDirectoryName(modTekDLLPath), HARMONY_DLL_FILE_NAME); string HarmonyDstDLLPath = Path.Combine(managedDirectory, HARMONY_DLL_FILE_NAME); if (File.Exists(HarmonySrcDLLPath) == false) { SayModTekAssemblyMissingError(HarmonySrcDLLPath); return(RC_MISSING_MODTEK_ASSEMBLY); } if (File.Exists(HarmonyDstDLLPath) == false) { File.Copy(HarmonySrcDLLPath, HarmonyDstDLLPath); } if (!File.Exists(gameDLLPath)) { SayGameAssemblyMissingError(OptionsIn.ManagedDirectory); return(RC_BAD_MANAGED_DIRECTORY_PROVIDED); } if (!File.Exists(modTekDLLPath)) { SayModTekAssemblyMissingError(modTekDLLPath); return(RC_MISSING_MODTEK_ASSEMBLY); } // setup factionsPath /*var factionsPath = GetFactionPath(OptionsIn.FactionsPath); * if (!string.IsNullOrEmpty(OptionsIn.FactionsPath) && !File.Exists(factionsPath)) * { * SayFactionsFileMissing(factionsPath); * return RC_MISSING_FACTION_FILE; * } * * if (string.IsNullOrEmpty(factionsPath) && OptionsIn.RequireKeyPress) * { * var path = GetSingleZipFilePath(Directory.GetCurrentDirectory()); * * if (!string.IsNullOrEmpty(path)) * { * SayMaybeInjectFactionZip(path); * if (PromptForYesNo(OptionsIn.RequireKeyPress)) * factionsPath = path; * } * }*/ // read the assembly for game version and injected status bool btmlInjected, modTekInjected, anyInjected; string gameVersion; using (var game = ModuleDefinition.ReadModule(gameDLLPath)) { gameVersion = GetGameVersion(game); btmlInjected = IsBTMLInjected(game); modTekInjected = IsModTekInjected(game); anyInjected = btmlInjected || modTekInjected; } // handle operations that require reading the assembly if (modTekInjected) { SayAlreadyInjected(); Restore(gameDLLPath, gameDLLBackupPath); gameDLLBackupPath = null; } // have restored a non-injected assembly or have a non injected assembly at this point // if backups restored, path to backup nullified, so not backed up again if (!string.IsNullOrEmpty(gameDLLBackupPath)) { Backup(gameDLLPath, gameDLLBackupPath); } Inject(gameDLLPath, modTekDLLPath, string.Empty); //factionsPath); if (HasOldFiles(modDirectory, managedDirectory)) { SayHasOldFiles(); if (PromptForYesNo(OptionsIn.RequireKeyPress)) { DeleteOldFiles(modDirectory, managedDirectory); } } PromptForKey(OptionsIn.RequireKeyPress); return(RC_NORMAL); } catch (BackupFileNotFound e) { SayException(e); SayHowToRecoverMissingBackup(e.BackupFileName); PromptForKey(true); return(RC_MISSING_BACKUP_FILE); } catch (BackupFileInjected e) { SayException(e); SayHowToRecoverInjectedBackup(e.BackupFileName); PromptForKey(true); return(RC_BACKUP_FILE_INJECTED); } catch (Exception e) { SayException(e); PromptForKey(true); } return(RC_UNHANDLED_STATE); }
public static void Main(string[] args) { Console.WriteLine("OPC UA Console Reference Subscriber"); // command line options bool showHelp = false; bool useMqttJson = true; bool useMqttUadp = false; bool useUdpUadp = false; string subscriberUrl = null; Mono.Options.OptionSet options = new Mono.Options.OptionSet { { "h|help", "Show usage information", v => showHelp = v != null }, { "m|mqtt_json", "Use MQTT with Json encoding Profile. This is the default option.", v => useMqttJson = v != null }, { "p|mqtt_uadp", "Use MQTT with UADP encoding Profile.", v => useMqttUadp = v != null }, { "u|udp_uadp", "Use UDP with UADP encoding Profile", v => useUdpUadp = v != null }, { "url|subscriber_url=", "Subscriber Url Address", v => subscriberUrl = v }, }; IList <string> extraArgs = null; try { extraArgs = options.Parse(args); if (extraArgs.Count > 0) { 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) { Console.WriteLine("Usage: dotnet ConsoleReferenceSubscriber.dll [OPTIONS]"); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } try { InitializeLog(); PubSubConfigurationDataType pubSubConfiguration = null; if (useUdpUadp) { // set default UDP Subscriber Url to local multicast if not sent in args. if (string.IsNullOrEmpty(subscriberUrl)) { subscriberUrl = "opc.udp://239.0.0.1:4840"; } // Create configuration using UDP protocol and UADP Encoding pubSubConfiguration = CreateSubscriberConfiguration_UdpUadp(subscriberUrl); Console.WriteLine("The Pubsub Connection was initialized using UDP & UADP Profile."); } else { // set default MQTT Broker Url to localhost if not sent in args. if (string.IsNullOrEmpty(subscriberUrl)) { subscriberUrl = "mqtt://localhost:1883"; } if (useMqttUadp) { // Create configuration using MQTT protocol and UADP Encoding pubSubConfiguration = CreateSubscriberConfiguration_MqttUadp(subscriberUrl); Console.WriteLine("The PubSub Connection was initialized using MQTT & UADP Profile."); } else { // Create configuration using MQTT protocol and JSON Encoding pubSubConfiguration = CreateSubscriberConfiguration_MqttJson(subscriberUrl); Console.WriteLine("The PubSub Connection was initialized using MQTT & JSON Profile."); } } // Create the UA Publisher application using (UaPubSubApplication uaPubSubApplication = UaPubSubApplication.Create(pubSubConfiguration)) { // Subscribte to RawDataReceived event uaPubSubApplication.RawDataReceived += UaPubSubApplication_RawDataReceived; // Subscribte to DataReceived event uaPubSubApplication.DataReceived += UaPubSubApplication_DataReceived; // Subscribte to MetaDataReceived event uaPubSubApplication.MetaDataReceived += UaPubSubApplication_MetaDataDataReceived; uaPubSubApplication.ConfigurationUpdating += UaPubSubApplication_ConfigurationUpdating; // Start the publisher uaPubSubApplication.Start(); Console.WriteLine("Subscriber Started. Press Ctrl-C to exit..."); ManualResetEvent quitEvent = new ManualResetEvent(false); try { Console.CancelKeyPress += (sender, eArgs) => { quitEvent.Set(); eArgs.Cancel = true; }; } catch { } // wait for timeout or Ctrl-C quitEvent.WaitOne(); } Console.WriteLine("Program ended."); Console.WriteLine("Press any key to finish..."); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine(ex.Message); } }
public static int Main(string[] args) { Console.WriteLine("Technosoftware {0} OPC UA ModelDesign Client", Utils.IsRunningOnMono() ? "Mono" : ".NET Core"); // command line options var showHelp = false; var stopTimeout = Timeout.Infinite; var autoAccept = false; var noBrowse = false; var verbose = false; string username = null; string password = null; var options = new 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 }, { "b|nobrowse", "Do not browse the address space of the server.", n => noBrowse = n != null }, { "u|username="******"Username to access server.", n => username = n }, { "p|password="******"Password to access server.", n => password = n }, { "v|verbose", "Verbose output.", v => verbose = v != null }, }; IList <string> extraArgs = null; try { extraArgs = options.Parse(args); if (extraArgs.Count > 1) { foreach (var 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("Usage: dotnet Technosoftware.ModelDesignClient.dll [OPTIONS] [ENDPOINTURL]"); Console.WriteLine(); // output the options Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return((int)ExitCode.ErrorInvalidCommandLine); } string endpointUrl; if (extraArgs == null || extraArgs.Count == 0) { // use Technosoftware OPC UA ModelDesign Server endpointUrl = "opc.tcp://localhost:55552/TechnosoftwareModelDesignServer"; } else { endpointUrl = extraArgs[0]; } var client = new MySampleClient(endpointUrl, autoAccept, stopTimeout) { Verbose = verbose, BrowseAddressSpace = !noBrowse, Username = username, Password = password, }; return((int)client.Run()); }
private OptionSet ParseArguments(string[] args, Settings settings) { var optionSet = new Mono.Options.OptionSet() { { "h|help", "shows this help", s => settings.Action = AppAction.ShowHelp }, { "p|port=", "sets the name of the serialport (COM1, COM2, etc)", s => settings.PortName = s }, { "l|listports", "lists the name of all available COM ports", s => settings.Action = AppAction.ListPorts }, { "e|echo", "echoes the received byte back", s => settings.Echo = true }, { "b|baudrate=", "sets the baudrate of the serialport", s => settings.BaudRate = TryParseBaudRate(s) }, { "parity=", "sets the parity bit configuration of the serialport", s => settings.Parity = TryParseParity(s) }, { "db|databits=", "sets the databits configuration of the serialport", s => settings.DataBits = TryParseDataBits(s) }, { "sb|stopbits=", "sets the stopbits configuration of the serialport", s => settings.StopBits = TryParseStopBits(s) }, { "f|send-file=", "sends the specified file over the serialport", s => { settings.FilePath = s; settings.Action = AppAction.SendFile; } }, { "a|send-ascii=", "sends the specified ascii value over the serialport", s => { settings.Ascii = TryParseAscii(s); settings.Action = AppAction.SendAscii; } }, { "c|count=", "specifies the number of files or ascii characters that are sent over the serialport", s => settings.Count = TryParseCount(s) }, { "t|text=", "specifies the text that is to be sent over the serialport", s => { settings.Text = s; settings.Action = AppAction.SendText; } }, { "r|receive", "listens on the serialport and writes received data to a file", s => { settings.Action = AppAction.Receive; settings.FilePath = s; } }, { "d|dump-to-file=", "optional file to dump the received data to", s => settings.FilePath = s } }; optionSet.Parse(args); return optionSet; }
/// <summary> /// Asynchronous part of the main method of the app. /// </summary> public async static Task MainAsync(string[] args) { try { var shouldShowHelp = false; // shutdown token sources ShutdownTokenSource = new CancellationTokenSource(); // command line options Mono.Options.OptionSet options = new Mono.Options.OptionSet { { "cf|configfile=", $"the filename containing action configuration.\nDefault: '{OpcActionConfigurationFilename}'", (string p) => OpcActionConfigurationFilename = p }, { "tc|testconnectivity", $"tests connectivity with the server.\nDefault: {TestConnectivity}", b => TestConnectivity = b != null }, { "tu|testunsecureconnectivity", $"tests connectivity with the server using an unsecured endpoint.\nDefault: {TestUnsecureConnectivity}", b => TestUnsecureConnectivity = b != null }, { "de|defaultendpointurl=", $"endpoint OPC UA server used as default.\nDefault: {DefaultEndpointUrl}", (string s) => DefaultEndpointUrl = s }, { "sw|sessionconnectwait=", $"specify the wait time in seconds we try to connect to disconnected endpoints and starts monitoring unmonitored items\nMin: 10\nDefault: {SessionConnectWait}", (int i) => { if (i > 10) { SessionConnectWait = i; } else { throw new OptionException("The sessionconnectwait must be greater than 10 sec", "sessionconnectwait"); } } }, { "di|diagnosticsinterval=", $"shows diagnostic info at the specified interval in seconds (need log level info). 0 disables diagnostic output.\nDefault: {DiagnosticsInterval}", (uint u) => DiagnosticsInterval = u }, { "lf|logfile=", $"the filename of the logfile to use.\nDefault: don't write logfile.", (string l) => _logFileName = l }, { "lt|logflushtimespan=", $"the timespan in seconds when the logfile should be flushed.\nDefault: {_logFileFlushTimeSpanSec} sec", (int s) => { if (s > 0) { _logFileFlushTimeSpanSec = TimeSpan.FromSeconds(s); } else { throw new Mono.Options.OptionException("The logflushtimespan must be a positive number.", "logflushtimespan"); } } }, { "ll|loglevel=", $"the loglevel to use (allowed: fatal, error, warn, info, debug, verbose).\nDefault: info", (string l) => { List <string> logLevels = new List <string> { "fatal", "error", "warn", "info", "debug", "verbose" }; if (logLevels.Contains(l.ToLowerInvariant())) { _logLevel = l.ToLowerInvariant(); } else { throw new Mono.Options.OptionException("The loglevel must be one of: fatal, error, warn, info, debug, verbose", "loglevel"); } } }, // opc configuration options { "ol|opcmaxstringlen=", $"the max length of a string opc can transmit/receive.\nDefault: {OpcMaxStringLength}", (int i) => { if (i > 0) { OpcMaxStringLength = i; } else { throw new OptionException("The max opc string length must be larger than 0.", "opcmaxstringlen"); } } }, { "ot|operationtimeout=", $"the operation timeout of the OPC UA client in ms.\nDefault: {OpcOperationTimeout}", (int i) => { if (i >= 0) { OpcOperationTimeout = i; } else { throw new OptionException("The operation timeout must be larger or equal 0.", "operationtimeout"); } } }, { "ct|createsessiontimeout=", $"specify the timeout in seconds used when creating a session to an endpoint. On unsuccessful connection attemps a backoff up to {OpcSessionCreationBackoffMax} times the specified timeout value is used.\nMin: 1\nDefault: {OpcSessionCreationTimeout}", (uint u) => { if (u > 1) { OpcSessionCreationTimeout = u; } else { throw new OptionException("The createsessiontimeout must be greater than 1 sec", "createsessiontimeout"); } } }, { "ki|keepaliveinterval=", $"specify the interval in seconds se send keep alive messages to the OPC servers on the endpoints it is connected to.\nMin: 2\nDefault: {OpcKeepAliveInterval}", (int i) => { if (i >= 2) { OpcKeepAliveInterval = i; } else { throw new OptionException("The keepaliveinterval must be greater or equal 2", "keepalivethreshold"); } } }, { "kt|keepalivethreshold=", $"specify the number of keep alive packets a server can miss, before the session is disconneced\nMin: 1\nDefault: {OpcKeepAliveDisconnectThreshold}", (uint u) => { if (u > 1) { OpcKeepAliveDisconnectThreshold = u; } else { throw new OptionException("The keepalivethreshold must be greater than 1", "keepalivethreshold"); } } }, { "csvOutput=|csvoutput=", $"filename to store readed values in CSV format.\nDefault: './{_csvFileName}'", (string path) => _csvFileName = path }, { "aa|autoaccept", $"trusts all servers we establish a connection to.\nDefault: {AutoAcceptCerts}", b => AutoAcceptCerts = b != null }, { "to|trustowncert", $"our own certificate is put into the trusted certificate store automatically.\nDefault: {TrustMyself}", t => TrustMyself = t != null }, // cert store options { "at|appcertstoretype=", $"the own application cert store type. \n(allowed values: Directory, X509Store)\nDefault: '{OpcOwnCertStoreType}'", (string s) => { if (s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcOwnCertStoreType = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? CertificateStoreType.X509Store : CertificateStoreType.Directory; OpcOwnCertStorePath = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? OpcOwnCertX509StorePathDefault : OpcOwnCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ap|appcertstorepath=", $"the path where the own application cert should be stored\nDefault (depends on store type):\n" + $"X509Store: '{OpcOwnCertX509StorePathDefault}'\n" + $"Directory: '{OpcOwnCertDirectoryStorePathDefault}'", (string s) => OpcOwnCertStorePath = s }, { "tp|trustedcertstorepath=", $"the path of the trusted cert store\nDefault '{OpcTrustedCertDirectoryStorePathDefault}'", (string s) => OpcTrustedCertStorePath = s }, { "rp|rejectedcertstorepath=", $"the path of the rejected cert store\nDefault '{OpcRejectedCertDirectoryStorePathDefault}'", (string s) => OpcRejectedCertStorePath = s }, { "ip|issuercertstorepath=", $"the path of the trusted issuer cert store\nDefault '{OpcIssuerCertDirectoryStorePathDefault}'", (string s) => OpcIssuerCertStorePath = s }, { "csr", $"show data to create a certificate signing request\nDefault '{ShowCreateSigningRequestInfo}'", c => ShowCreateSigningRequestInfo = c != null }, { "ab|applicationcertbase64=", $"update/set this applications certificate with the certificate passed in as bas64 string", (string s) => { NewCertificateBase64String = s; } }, { "af|applicationcertfile=", $"update/set this applications certificate with the certificate file specified", (string s) => { if (File.Exists(s)) { NewCertificateFileName = s; } else { throw new OptionException($"The file '{s}' does not exist.", "applicationcertfile"); } } }, { "pb|privatekeybase64=", $"initial provisioning of the application certificate (with a PEM or PFX fomat) requires a private key passed in as base64 string", (string s) => { PrivateKeyBase64String = s; } }, { "pk|privatekeyfile=", $"initial provisioning of the application certificate (with a PEM or PFX fomat) requires a private key passed in as file", (string s) => { if (File.Exists(s)) { PrivateKeyFileName = s; } else { throw new OptionException($"The file '{s}' does not exist.", "privatekeyfile"); } } }, { "cp|certpassword="******"the optional password for the PEM or PFX or the installed application certificate", (string s) => { CertificatePassword = s; } }, { "tb|addtrustedcertbase64=", $"adds the certificate to the applications trusted cert store passed in as base64 string (multiple strings supported)", (string s) => { TrustedCertificateBase64Strings = ParseListOfStrings(s); } }, { "tf|addtrustedcertfile=", $"adds the certificate file(s) to the applications trusted cert store passed in as base64 string (multiple filenames supported)", (string s) => { TrustedCertificateFileNames = ParseListOfFileNames(s, "addtrustedcertfile"); } }, { "ib|addissuercertbase64=", $"adds the specified issuer certificate to the applications trusted issuer cert store passed in as base64 string (multiple strings supported)", (string s) => { IssuerCertificateBase64Strings = ParseListOfStrings(s); } }, { "if|addissuercertfile=", $"adds the specified issuer certificate file(s) to the applications trusted issuer cert store (multiple filenames supported)", (string s) => { IssuerCertificateFileNames = ParseListOfFileNames(s, "addissuercertfile"); } }, { "rb|updatecrlbase64=", $"update the CRL passed in as base64 string to the corresponding cert store (trusted or trusted issuer)", (string s) => { CrlBase64String = s; } }, { "uc|updatecrlfile=", $"update the CRL passed in as file to the corresponding cert store (trusted or trusted issuer)", (string s) => { if (File.Exists(s)) { CrlFileName = s; } else { throw new OptionException($"The file '{s}' does not exist.", "updatecrlfile"); } } }, { "rc|removecert=", $"remove cert(s) with the given thumbprint(s) (multiple thumbprints supported)", (string s) => { ThumbprintsToRemove = ParseListOfStrings(s); } }, // misc { "h|help", "show this message and exit", h => shouldShowHelp = h != null }, }; List <string> extraArgs = new List <string>(); try { // parse the command line extraArgs = options.Parse(args); } catch (OptionException e) { // initialize logging InitLogging(); // show message Logger.Fatal(e, "Error in command line options"); Environment.ExitCode = 1; Logger.Error($"Command line arguments: {String.Join(" ", args)}"); // show usage Usage(options); return; } // initialize logging InitLogging(); // show usage if requested if (shouldShowHelp) { Usage(options); return; } // validate and parse extra arguments if (extraArgs.Count > 0) { Logger.Error("Error in command line options"); Environment.ExitCode = 1; Logger.Error($"Command line arguments: {String.Join(" ", args)}"); Usage(options); return; } //show version Logger.Information($"{ProgramName} V{FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion} starting up..."); Logger.Debug($"Informational version: V{(Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute).InformationalVersion}"); // allow canceling the application var quitEvent = new ManualResetEvent(false); try { Console.CancelKeyPress += (sender, eArgs) => { quitEvent.Set(); eArgs.Cancel = true; ShutdownTokenSource.Cancel(); }; } catch { } // init OPC configuration and tracing OpcApplicationConfiguration applicationStackConfiguration = new OpcApplicationConfiguration(); await applicationStackConfiguration.ConfigureAsync(); // read OPC action configuration OpcConfiguration.Init(); if (!await ReadOpcConfigurationAsync()) { return; } // add well known actions if (!await CreateOpcActionDataAsync()) { return; } // kick off OPC client activities await SessionStartAsync(); // initialize diagnostics Diagnostics.Init(); await OpcSessionsListSemaphore.WaitAsync(); var pendingOperations = OpcSessions.Sum(s => s.OpcActions.Count); OpcSessionsListSemaphore.Release(); while (pendingOperations > 0 && ShutdownTokenSource.IsCancellationRequested != true) { Logger.Information($"{pendingOperations} pending operation(s)."); Logger.Information(""); Logger.Information($"{ProgramName} is running. Press CTRL-C to quit."); Thread.Sleep(1000); await OpcSessionsListSemaphore.WaitAsync(); pendingOperations = OpcSessions.Sum(s => s.OpcActions.Count); OpcSessionsListSemaphore.Release(); } Logger.Information("No pending operations."); Logger.Information(""); ShutdownTokenSource.Cancel(); Logger.Information($"{ProgramName} is shutting down..."); // shutdown all OPC sessions await SessionShutdownAsync(); // shutdown diagnostics await ShutdownAsync(); ShutdownTokenSource = null; } catch (Exception e) { Logger.Fatal(e, e.StackTrace); Environment.ExitCode = 1; e = e.InnerException ?? null; while (e != null) { Logger.Fatal(e, e.StackTrace); e = e.InnerException ?? null; } Logger.Fatal($"{ProgramName} exiting... "); } }
/// <summary> /// Asynchronous part of the main method of the app. /// </summary> public async static Task MainAsync(string[] args) { try { var shouldShowHelp = false; // command line options configuration Mono.Options.OptionSet options = new Mono.Options.OptionSet { // Publishing configuration options { "pf|publishfile=", $"the filename to configure the nodes to publish (fallback procedure).\nDefault: '{PublisherNodeConfigurationFilename}'", (string p) => PublisherNodeConfigurationFilename = p }, { "tc|telemetryconfigfile=", $"the filename to configure the ingested telemetry\nDefault: '{PublisherTelemetryConfigurationFilename}'", (string p) => PublisherTelemetryConfigurationFilename = p }, { "sd|shopfloordomain=", $"the domain of the shopfloor. if specified this domain is appended (delimited by a ':' to the 'ApplicationURI' property when telemetry is sent to IoTHub.\n" + "The value must follow the syntactical rules of a DNS hostname.\nDefault: not set", (string s) => { Regex domainNameRegex = new Regex("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"); if (domainNameRegex.IsMatch(s)) { ShopfloorDomain = s; } else { throw new OptionException("The shopfloor domain is not a valid DNS hostname.", "shopfloordomain"); } } }, { "sw|sessionconnectwait=", $"specify the wait time in seconds publisher is trying to connect to disconnected endpoints and starts monitoring unmonitored items\nMin: 10\nDefault: {_publisherSessionConnectWaitSec}", (int i) => { if (i > 10) { _publisherSessionConnectWaitSec = i; } else { throw new OptionException("The sessionconnectwait must be greater than 10 sec", "sessionconnectwait"); } } }, { "mq|monitoreditemqueuecapacity=", $"specify how many notifications of monitored items can be stored in the internal queue, if the data can not be sent quick enough to IoTHub\nMin: 1024\nDefault: {MonitoredItemsQueueCapacity}", (int i) => { if (i >= 1024) { MonitoredItemsQueueCapacity = i; } else { throw new OptionException("The monitoreditemqueueitems must be greater than 1024", "monitoreditemqueueitems"); } } }, { "di|diagnosticsinterval=", $"shows publisher diagnostic info at the specified interval in seconds. 0 disables diagnostic output.\nDefault: {DiagnosticsInterval}", (uint u) => DiagnosticsInterval = u }, { "vc|verboseconsole=", $"the output of publisher is shown on the console.\nDefault: {VerboseConsole}", (bool b) => VerboseConsole = b }, { "ns|noshutdown=", $"publisher can not be stopped by pressing a key on the console, but will run forever.\nDefault: {_noShutdown}", (bool b) => _noShutdown = b }, // IoTHub specific options { "ih|iothubprotocol=", $"the protocol to use for communication with Azure IoTHub (allowed values: {string.Join(", ", Enum.GetNames(IotHubProtocol.GetType()))}).\nDefault: {Enum.GetName(IotHubProtocol.GetType(), IotHubProtocol)}", (Microsoft.Azure.Devices.Client.TransportType p) => IotHubProtocol = p }, { "ms|iothubmessagesize=", $"the max size of a message which can be send to IoTHub. when telemetry of this size is available it will be sent.\n0 will enforce immediate send when telemetry is available\nMin: 0\nMax: {IotHubMessageSizeMax}\nDefault: {IotHubMessageSize}", (uint u) => { if (u >= 0 && u <= IotHubMessageSizeMax) { IotHubMessageSize = u; } else { throw new OptionException("The iothubmessagesize must be in the range between 1 and 256*1024.", "iothubmessagesize"); } } }, { "si|iothubsendinterval=", $"the interval in seconds when telemetry should be send to IoTHub. If 0, then only the iothubmessagesize parameter controls when telemetry is sent.\nDefault: '{DefaultSendIntervalSeconds}'", (int i) => { if (i >= 0) { DefaultSendIntervalSeconds = i; } else { throw new OptionException("The iothubsendinterval must be larger or equal 0.", "iothubsendinterval"); } } }, { "dc|deviceconnectionstring=", $"if publisher is not able to register itself with IoTHub, you can create a device with name <applicationname> manually and pass in the connectionstring of this device.\nDefault: none", (string dc) => DeviceConnectionString = dc }, { "lf|logfile=", $"the filename of the logfile to use.\nDefault: './Logs/<applicationname>.log.txt'", (string l) => LogFileName = l }, { "ot|operationtimeout=", $"the operation timeout of the publisher OPC UA client in ms.\nDefault: {OpcOperationTimeout}", (int i) => { if (i >= 0) { OpcOperationTimeout = i; } else { throw new OptionException("The operation timeout must be larger or equal 0.", "operationtimeout"); } } }, { "oi|opcsamplinginterval=", "the publisher is using this as default value in milliseconds to request the servers to sample the nodes with this interval\n" + "this value might be revised by the OPC UA servers to a supported sampling interval.\n" + "please check the OPC UA specification for details how this is handled by the OPC UA stack.\n" + "a negative value will set the sampling interval to the publishing interval of the subscription this node is on.\n" + $"0 will configure the OPC UA server to sample in the highest possible resolution and should be taken with care.\nDefault: {OpcSamplingInterval}", (int i) => OpcSamplingInterval = i }, { "op|opcpublishinginterval=", "the publisher is using this as default value in milliseconds for the publishing interval setting of the subscriptions established to the OPC UA servers.\n" + "please check the OPC UA specification for details how this is handled by the OPC UA stack.\n" + $"a value less than or equal zero will let the server revise the publishing interval.\nDefault: {OpcPublishingInterval}", (int i) => { if (i > 0 && i >= OpcSamplingInterval) { OpcPublishingInterval = i; } else { if (i <= 0) { OpcPublishingInterval = 0; } else { throw new OptionException($"The opcpublishinterval ({i}) must be larger than the opcsamplinginterval ({OpcSamplingInterval}).", "opcpublishinterval"); } } } }, { "ct|createsessiontimeout=", $"specify the timeout in seconds used when creating a session to an endpoint. On unsuccessful connection attemps a backoff up to {OpcSessionCreationBackoffMax} times the specified timeout value is used.\nMin: 1\nDefault: {OpcSessionCreationTimeout}", (uint u) => { if (u > 1) { OpcSessionCreationTimeout = u; } else { throw new OptionException("The createsessiontimeout must be greater than 1 sec", "createsessiontimeout"); } } }, { "ki|keepaliveinterval=", $"specify the interval in seconds the publisher is sending keep alive messages to the OPC servers on the endpoints it is connected to.\nMin: 2\nDefault: {OpcKeepAliveIntervalInSec}", (int i) => { if (i >= 2) { OpcKeepAliveIntervalInSec = i; } else { throw new OptionException("The keepaliveinterval must be greater or equal 2", "keepalivethreshold"); } } }, { "kt|keepalivethreshold=", $"specify the number of keep alive packets a server can miss, before the session is disconneced\nMin: 1\nDefault: {OpcKeepAliveDisconnectThreshold}", (uint u) => { if (u > 1) { OpcKeepAliveDisconnectThreshold = u; } else { throw new OptionException("The keepalivethreshold must be greater than 1", "keepalivethreshold"); } } }, { "st|opcstacktracemask=", $"the trace mask for the OPC stack. See github OPC .NET stack for definitions.\nTo enable IoTHub telemetry tracing set it to 711.\nDefault: {OpcStackTraceMask:X} ({OpcStackTraceMask})", (int i) => { if (i >= 0) { OpcStackTraceMask = i; } else { throw new OptionException("The OPC stack trace mask must be larger or equal 0.", "opcstacktracemask"); } } }, { "as|autotrustservercerts=", $"the publisher trusts all servers it is establishing a connection to.\nDefault: {OpcPublisherAutoTrustServerCerts}", (bool b) => OpcPublisherAutoTrustServerCerts = b }, // trust own public cert option { "tm|trustmyself=", $"the publisher certificate is put into the trusted certificate store automatically.\nDefault: {TrustMyself}", (bool b) => TrustMyself = b }, // read the display name of the nodes to publish from the server and publish them instead of the node id { "fd|fetchdisplayname=", $"enable to read the display name of a published node from the server. this will increase the runtime.\nDefault: {FetchOpcNodeDisplayName}", (bool b) => FetchOpcNodeDisplayName = b }, // own cert store options { "at|appcertstoretype=", $"the own application cert store type. \n(allowed values: Directory, X509Store)\nDefault: '{OpcOwnCertStoreType}'", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { OpcOwnCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; OpcOwnCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcOwnCertX509StorePathDefault : OpcOwnCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ap|appcertstorepath=", $"the path where the own application cert should be stored\nDefault (depends on store type):\n" + $"X509Store: '{OpcOwnCertX509StorePathDefault}'\n" + $"Directory: '{OpcOwnCertDirectoryStorePathDefault}'", (string s) => OpcOwnCertStorePath = s }, // trusted cert store options { "tt|trustedcertstoretype=", $"the trusted cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcTrustedCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { OpcTrustedCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; OpcTrustedCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcTrustedCertX509StorePathDefault : OpcTrustedCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "tp|trustedcertstorepath=", $"the path of the trusted cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcTrustedCertX509StorePathDefault}'\n" + $"Directory: '{OpcTrustedCertDirectoryStorePathDefault}'", (string s) => OpcTrustedCertStorePath = s }, // rejected cert store options { "rt|rejectedcertstoretype=", $"the rejected cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcRejectedCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { OpcRejectedCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; OpcRejectedCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcRejectedCertX509StorePathDefault : OpcRejectedCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "rp|rejectedcertstorepath=", $"the path of the rejected cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcRejectedCertX509StorePathDefault}'\n" + $"Directory: '{OpcRejectedCertDirectoryStorePathDefault}'", (string s) => OpcRejectedCertStorePath = s }, // issuer cert store options { "it|issuercertstoretype=", $"the trusted issuer cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcIssuerCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { OpcIssuerCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; OpcIssuerCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcIssuerCertX509StorePathDefault : OpcIssuerCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ip|issuercertstorepath=", $"the path of the trusted issuer cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcIssuerCertX509StorePathDefault}'\n" + $"Directory: '{OpcIssuerCertDirectoryStorePathDefault}'", (string s) => OpcIssuerCertStorePath = s }, // device connection string cert store options { "dt|devicecertstoretype=", $"the iothub device cert store type. \n(allowed values: Directory, X509Store)\nDefault: {IotDeviceCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(Directory, StringComparison.OrdinalIgnoreCase)) { IotDeviceCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : Directory; IotDeviceCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? IotDeviceCertX509StorePathDefault : IotDeviceCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "dp|devicecertstorepath=", $"the path of the iot device cert store\nDefault Default (depends on store type):\n" + $"X509Store: '{IotDeviceCertX509StorePathDefault}'\n" + $"Directory: '{IotDeviceCertDirectoryStorePathDefault}'", (string s) => IotDeviceCertStorePath = s }, // misc { "h|help", "show this message and exit", h => shouldShowHelp = h != null }, }; List <string> arguments = new List <string>(); int maxAllowedArguments = 2; int applicationNameIndex = 0; int connectionStringIndex = 1; if (IsIotEdgeModule) { maxAllowedArguments = 3; applicationNameIndex = 1; connectionStringIndex = 2; } try { // parse the command line arguments = options.Parse(args); } catch (OptionException e) { // show message WriteLine($"Error: {e.Message}"); // show usage Usage(options); return; } // Validate and parse arguments. if (arguments.Count > maxAllowedArguments || shouldShowHelp) { Usage(options); return; } else if (arguments.Count == maxAllowedArguments) { ApplicationName = arguments[applicationNameIndex]; IotHubOwnerConnectionString = arguments[connectionStringIndex]; } else if (arguments.Count == maxAllowedArguments - 1) { ApplicationName = arguments[applicationNameIndex]; } else { ApplicationName = Utils.GetHostName(); } WriteLine("Publisher is starting up..."); // Shutdown token sources. ShutdownTokenSource = new CancellationTokenSource(); // init OPC configuration and tracing OpcStackConfiguration opcStackConfiguration = new OpcStackConfiguration(); await opcStackConfiguration.ConfigureAsync(); _opcTraceInitialized = true; // log shopfloor domain setting if (string.IsNullOrEmpty(ShopfloorDomain)) { Trace("There is no shopfloor domain configured."); } else { Trace($"Publisher is in shopfloor domain '{ShopfloorDomain}'."); } // Set certificate validator. if (OpcPublisherAutoTrustServerCerts) { Trace("Publisher configured to auto trust server certificates of the servers it is connecting to."); PublisherOpcApplicationConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_AutoTrustServerCerts); } else { Trace("Publisher configured to not auto trust server certificates. When connecting to servers, you need to manually copy the rejected server certs to the trusted store to trust them."); PublisherOpcApplicationConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_Default); } // read telemetry configuration file PublisherTelemetryConfiguration.Init(); if (!await PublisherTelemetryConfiguration.ReadConfigAsync()) { return; } // read node configuration file // read it before loading desired properties in case of misconfigurations PublisherNodeConfiguration.Init(); if (!await PublisherNodeConfiguration.ReadConfigAsync()) { return; } // initialize and start IoTHub messaging IotHubCommunication = new IotHubMessaging(); if (!await IotHubCommunication.InitAsync()) { return; } if (!await PublisherNodeConfiguration.CreateOpcPublishingDataAsync()) { return; } // kick off the task to maintain all sessions Task sessionConnectorAsync = Task.Run(async() => await SessionConnectorAsync(ShutdownTokenSource.Token)); // initialize publisher diagnostics Diagnostics.Init(); // stop on user request WriteLine(""); WriteLine(""); if (_noShutdown) { // wait forever if asked to do so WriteLine("Publisher is running infinite..."); await Task.Delay(Timeout.Infinite); } else { WriteLine("Publisher is running. Press any key to quit."); try { ReadKey(true); } catch { // wait forever if there is no console WriteLine("There is no console. Publisher is running infinite..."); await Task.Delay(Timeout.Infinite); } } WriteLine(""); WriteLine(""); ShutdownTokenSource.Cancel(); WriteLine("Publisher is shutting down..."); // Wait for session connector completion await sessionConnectorAsync; // Clean up Publisher sessions await SessionShutdownAsync(); // shutdown the IoTHub messaging await IotHubCommunication.ShutdownAsync(); IotHubCommunication = null; // shutdown diagnostics await ShutdownAsync(); // free resources PublisherTelemetryConfiguration.Deinit(); PublisherNodeConfiguration.Deinit(); ShutdownTokenSource = null; } catch (Exception e) { if (_opcTraceInitialized) { Trace(e, e.StackTrace); e = e.InnerException ?? null; while (e != null) { Trace(e, e.StackTrace); e = e.InnerException ?? null; } Trace("Publisher exiting... "); } else { WriteLine($"{DateTime.Now.ToString()}: {e.Message.ToString()}"); WriteLine($"{DateTime.Now.ToString()}: {e.StackTrace}"); e = e.InnerException ?? null; while (e != null) { WriteLine($"{DateTime.Now.ToString()}: {e.Message.ToString()}"); WriteLine($"{DateTime.Now.ToString()}: {e.StackTrace}"); e = e.InnerException ?? null; } WriteLine($"{DateTime.Now.ToString()}: Publisher exiting..."); } } }
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); }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously /// <summary> /// Asynchronous part of the main method of the app. /// </summary> public async static Task MainAsync(string[] args) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { Logger = new LoggerConfiguration() .WriteTo.Console() .MinimumLevel.Debug() .CreateLogger(); Logger.Information($"OPC Publisher testclient"); // command line options bool showHelp = false; int testTimeMillisec = Timeout.Infinite; bool opcMethods = false; bool iotHubMethods = false; string iotHubConnectionString = string.Empty; string iotHubPublisherDeviceName = string.Empty; string iotHubPublisherModuleName = string.Empty; int initialWait = 10000; Mono.Options.OptionSet options = new Mono.Options.OptionSet { { "h|help", "show this message and exit", h => showHelp = h != null }, { "ip|initialpause=", $"initial wait in sec to allow other services to start up.\nDefault: {initialWait/1000}", (int i) => initialWait = i * 1000 }, { "aa|autoaccept", "auto accept certificates (for testing only)", a => AutoAccept = a != null }, { "ne|noexclusive", "do not execute any exclusive tests", ne => RunExclusiveTests = ne == null }, { "tt|testtime=", "the number of seconds to run the different tests", (int t) => testTimeMillisec = t * 1000 }, { "tu|testserverurl=", "URL of the OPC UA test server", (string s) => TestserverUrl = s }, { "pu|publisherurl=", "URL of the OPC Publisher (required when using OPC UA methods)", (string s) => PublisherUrl = s }, { "o1|opcmethods", "use the OPC UA methods calls to test", b => opcMethods = b != null }, { "ic|iothubconnectionstring=", "IoTHub owner connectionstring", (string s) => iotHubConnectionString = s }, { "id|iothubdevicename=", "IoTHub device name of the OPC Publisher (required when using IoT methods)", (string s) => iotHubPublisherDeviceName = s }, { "im|iothubmodulename=", "IoTEdge module name of the OPC Publisher which runs in IoTEdge specified by im/iothubdevicename(required when using IoT methods and IoTEdge)", (string s) => iotHubPublisherModuleName = s }, { "i1|iothubmethods", "use IoTHub direct methods calls to test", b => iotHubMethods = b != null }, { "lf|logfile=", $"the filename of the logfile to use.\nDefault: './{_logFileName}'", (string l) => _logFileName = l }, { "ll|loglevel=", $"the loglevel to use (allowed: fatal, error, warn, info, debug, verbose).\nDefault: info", (string l) => { List <string> logLevels = new List <string> { "fatal", "error", "warn", "info", "debug", "verbose" }; #pragma warning disable CA1308 // Normalize strings to uppercase if (logLevels.Contains(l.ToLowerInvariant())) { _logLevel = l.ToLowerInvariant(); } #pragma warning restore CA1308 // Normalize strings to uppercase else { throw new OptionException("The loglevel must be one of: fatal, error, warn, info, debug, verbose", "loglevel"); } } } }; IList <string> extraArgs = null; try { extraArgs = options.Parse(args); } catch (OptionException e) { // initialize logging InitLogging(); // show message Logger.Fatal(e, "Error in command line options"); // show usage Usage(options, args); return; } // initialize logging InitLogging(); // show usage if requested if (showHelp) { Usage(options); return; } // by default we are connecting to the OPC UA servers in the testbed if (extraArgs.Count > 0) { for (int i = 1; i < extraArgs.Count; i++) { Logger.Error("Error: Unknown option: {0}", extraArgs[i]); } Usage(options, args); return; } // initial wait Logger.Information($"Waiting for {initialWait/1000} secondes..."); Thread.Sleep(initialWait); // sanity check parameters if (opcMethods == false && iotHubMethods == false) { Logger.Information($"No specific test area specified, enabling all."); opcMethods = iotHubMethods = true; } if (opcMethods) { Logger.Information($"Publisher URL: {PublisherUrl}"); } if (iotHubMethods) { if (string.IsNullOrEmpty(iotHubConnectionString) || string.IsNullOrEmpty(iotHubPublisherDeviceName)) { Logger.Fatal("For any tests via IoTHub communication an IoTHub connection string and the publisher devicename (and modulename) must be specified."); return; } Logger.Information($"IoTHub connectionstring: {iotHubConnectionString}"); if (string.IsNullOrEmpty(iotHubPublisherModuleName)) { Logger.Information($"Testing OPC Publisher device."); Logger.Information($"IoTHub Publisher device name: {iotHubPublisherDeviceName}"); } else { Logger.Information($"Testing OPC Publisher IoTEdge module."); Logger.Information($"IoTEdge device name: {iotHubPublisherDeviceName}"); Logger.Information($"IoTHub Publisher device name: {iotHubPublisherModuleName}"); } } CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken ct = cts.Token; ManualResetEvent quitEvent = new ManualResetEvent(false); try { Console.CancelKeyPress += (sender, eArgs) => { quitEvent.Set(); eArgs.Cancel = true; }; } catch { } // instantiate test objectes OpcMethodTest opcMethodTest = null; IotHubMethodTest iotHubMethodTest = null; if (opcMethods) { opcMethodTest = new OpcMethodTest(TestserverUrl, MaxShortWaitSec, MaxLongWaitSec, ct); } if (iotHubMethods) { iotHubMethodTest = new IotHubMethodTest(iotHubConnectionString, iotHubPublisherDeviceName, iotHubPublisherModuleName, TestserverUrl, MaxShortWaitSec, MaxLongWaitSec, ct); } // run all tests with need exclusive access to the server if (RunExclusiveTests && opcMethods) { opcMethodTest.RunExclusiveTests(ct); } if (RunExclusiveTests && iotHubMethods) { iotHubMethodTest.RunExclusiveTests(ct); } // run all tests which can be executed concurrently List <Task> testTasks = new List <Task>(); if (opcMethods) { testTasks.AddRange(opcMethodTest.RunConcurrentTests(ct)); } if (iotHubMethods) { testTasks.AddRange(iotHubMethodTest.RunConcurrentTests(ct)); } // run all tests for the specified time or Ctrl-C is pressed Logger.Information($"Run tests {(testTimeMillisec != Timeout.Infinite ? $"for {testTimeMillisec/1000} seconds or" : "till")} CTRL-C is pressed"); quitEvent.WaitOne(testTimeMillisec); Logger.Information($"Signal cancellation and wait will everything is completed."); cts.Cancel(); // wait till all tasks are completed Task.WaitAll(testTasks.ToArray()); Logger.Information($"Exiting...."); return; }
/// <summary> /// Asynchronous part of the main method of the app. /// </summary> public async static Task MainAsync(string[] args) { Logger = new LoggerConfiguration() .WriteTo.Console() .MinimumLevel.Debug() .CreateLogger(); Logger.Information($"OPC Publisher node configuration tool"); // command line options bool showHelp = false; string iotHubConnectionString = string.Empty; string iotHubPublisherDeviceName = string.Empty; string iotHubPublisherModuleName = string.Empty; Mono.Options.OptionSet options = new Mono.Options.OptionSet { { "h|help", "show this message and exit", h => showHelp = h != null }, { "ic|iotHubConnectionString=", "IoTHub owner or service connectionstring", (string s) => iotHubConnectionString = s }, { "id|iothubdevicename=", "IoTHub device name of the OPC Publisher", (string s) => iotHubPublisherDeviceName = s }, { "im|iothubmodulename=", "IoT Edge module name of the OPC Publisher which runs in the IoT Edge device specified by id/iothubdevicename", (string s) => iotHubPublisherModuleName = s }, { "pc|purgeconfig", "remove all configured nodes before pushing new ones", b => _purgeConfig = b != null }, { "bf|backupfile=", $"the filename to store the existing node configuration of OPC Publisher\nDefault: './{_backupFileName}'", (string l) => _backupFileName = l }, { "nc|nodeconfigfile=", $"the filename of the new node configuration to be set", (string l) => _nodeConfigFileName = l }, { "lf|logfile=", $"the filename of the logfile to use\nDefault: './{_logFileName}'", (string l) => _logFileName = l }, { "ll|loglevel=", $"the loglevel to use (allowed: fatal, error, warn, info, debug, verbose).\nDefault: info", (string l) => { List <string> logLevels = new List <string> { "fatal", "error", "warn", "info", "debug", "verbose" }; if (logLevels.Contains(l.ToLowerInvariant())) { _logLevel = l.ToLowerInvariant(); } else { throw new OptionException("The loglevel must be one of: fatal, error, warn, info, debug, verbose", "loglevel"); } } } }; IList <string> extraArgs = null; try { extraArgs = options.Parse(args); } catch (OptionException e) { // initialize logging InitLogging(); // show message Logger.Fatal(e, "Error in command line options"); // show usage Usage(options, args); return; } // initialize logging InitLogging(); // show usage if requested if (showHelp) { Usage(options); return; } // no extra options if (extraArgs.Count > 0) { for (int i = 1; i < extraArgs.Count; i++) { Logger.Error("Error: Unknown option: {0}", extraArgs[i]); } Usage(options, args); return; } // sanity check parameters if (string.IsNullOrEmpty(iotHubConnectionString) || string.IsNullOrEmpty(iotHubPublisherDeviceName)) { Logger.Fatal("For IoTHub communication an IoTHub connection string and the publisher devicename (and modulename) must be specified."); return; } Logger.Information($"IoTHub connectionstring: {iotHubConnectionString}"); if (string.IsNullOrEmpty(iotHubPublisherModuleName)) { Logger.Information($"OPC Publisher not running in IoT Edge."); Logger.Information($"IoTHub OPC Publisher device name: {iotHubPublisherDeviceName}"); } else { Logger.Information($"OPC Publisher running as IoT Edge module."); Logger.Information($"IoT Edge device name: {iotHubPublisherDeviceName}"); Logger.Information($"OPC Publisher module name: {iotHubPublisherModuleName}"); } CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken ct = cts.Token; // read new configuration if (!string.IsNullOrEmpty(_nodeConfigFileName)) { try { _configurationFileEntries = JsonConvert.DeserializeObject <List <PublisherConfigurationFileEntryLegacyModel> >(File.ReadAllText(_nodeConfigFileName)); } catch (Exception e) { Logger.Fatal(e, $"Error reading configuration file. Exiting..."); return; } Logger.Information($"The configuration file '{_nodeConfigFileName}' to be applied contains {_configurationFileEntries.Count} entries."); } // instantiate OPC Publisher interface Publisher publisher = new Publisher(iotHubConnectionString, iotHubPublisherDeviceName, iotHubPublisherModuleName, MAX_SHORT_WAIT_SEC, MAX_LONG_WAIT_SEC, ct); // read existing configuration List <PublisherConfigurationFileEntryModel> currentConfiguration = new List <PublisherConfigurationFileEntryModel>(); List <string> configuredEndpoints = publisher.GetConfiguredEndpoints(ct); if (configuredEndpoints.Count > 0) { Logger.Information($"OPC Publisher has the following node configuration:"); } else { Logger.Information($"OPC Publisher is not publishing any data."); } foreach (var configuredEndpoint in configuredEndpoints) { List <NodeModel> configuredNodesOnEndpoint = publisher.GetConfiguredNodesOnEndpoint(configuredEndpoint, ct); PublisherConfigurationFileEntryModel configEntry = new PublisherConfigurationFileEntryModel(); configEntry.EndpointUrl = new Uri(configuredEndpoint); List <OpcNodeOnEndpointModel> nodesOnEndpoint = new List <OpcNodeOnEndpointModel>(); Logger.Information($"For endpoint '{configuredEndpoint}' there are {configuredNodesOnEndpoint.Count} nodes configured."); foreach (var configuredNode in configuredNodesOnEndpoint) { Logger.Debug($"Id '{configuredNode.Id}', " + $"OpcPublishingInterval: {(configuredNode.OpcPublishingInterval == null ? "default" : configuredNode.OpcPublishingInterval.ToString())}, " + $"OpcSamplingInterval: {(configuredNode.OpcSamplingInterval == null ? "default" : configuredNode.OpcSamplingInterval.ToString())}"); OpcNodeOnEndpointModel opcNodeOnEndpoint = new OpcNodeOnEndpointModel(); opcNodeOnEndpoint.Id = configuredNode.Id; opcNodeOnEndpoint.OpcSamplingInterval = configuredNode.OpcSamplingInterval; opcNodeOnEndpoint.OpcPublishingInterval = configuredNode.OpcPublishingInterval; nodesOnEndpoint.Add(opcNodeOnEndpoint); } configEntry.OpcNodes = nodesOnEndpoint; currentConfiguration.Add(configEntry); } // save it on request if (!string.IsNullOrEmpty(_backupFileName) && currentConfiguration.Count > 0) { await File.WriteAllTextAsync(_backupFileName, JsonConvert.SerializeObject(currentConfiguration, Formatting.Indented)); Logger.Information($"The existing OPC Publisher node configuration was saved in '{_backupFileName}'"); } // remove existing configuration on request if (_purgeConfig) { publisher.UnpublishAllConfiguredNodes(ct); Logger.Information($"The existing node configuration was purged. OPC Publisher should no longer publish any data."); } // push new configuration, if required if (_configurationFileEntries != null) { var uniqueEndpoints = _configurationFileEntries.Select(e => e.EndpointUrl).Distinct(); Logger.Information($"The new node configuration will now be set in OPC Publisher."); foreach (var uniqueEndpoint in uniqueEndpoints) { var endpointConfigurationfileEntries = _configurationFileEntries.Where(e => e.EndpointUrl == uniqueEndpoint); List <NodeIdInfo> configurationNodeIdInfos = new List <NodeIdInfo>(); foreach (var endpointConfigurationFileEntry in endpointConfigurationfileEntries) { foreach (var opcNode in endpointConfigurationFileEntry.OpcNodes) { Logger.Debug($"Id '{opcNode.Id}', " + $"OpcPublishingInterval: {(opcNode.OpcPublishingInterval == null ? "default" : opcNode.OpcPublishingInterval.ToString())}, " + $"OpcSamplingInterval: {(opcNode.OpcSamplingInterval == null ? "default" : opcNode.OpcSamplingInterval.ToString())}"); NodeIdInfo nodeIdInfo = new NodeIdInfo(opcNode.Id); configurationNodeIdInfos.Add(nodeIdInfo); } } if (!publisher.PublishNodes(configurationNodeIdInfos, ct, uniqueEndpoint.AbsoluteUri)) { Logger.Error($"Not able to send the new node configuration to OPC Publisher."); } } } // done Logger.Information($"Done. Exiting...."); return; }
/// <summary> /// Parse arguments and set values in the environment the way the new configuration expects it. /// </summary> /// <param name="args">The specified command line arguments.</param> public StandaloneCliOptions(string[] args) { _logger = ConsoleLogger.Create(LogEventLevel.Warning); bool showHelp = false; List <string> unsupportedOptions = new List <string>(); List <string> legacyOptions = new List <string>(); // command line options var options = new Mono.Options.OptionSet { // Publisher configuration options { $"pf|publishfile=|{StandaloneCliConfigKeys.PublishedNodesConfigurationFilename}=", "The filename to configure the nodes to publish.", s => this[StandaloneCliConfigKeys.PublishedNodesConfigurationFilename] = s }, { $"pfs|publishfileschema=|{StandaloneCliConfigKeys.PublishedNodesConfigurationSchemaFilename}=", "The validation schema filename for publish file. Disabled by default.", s => this[StandaloneCliConfigKeys.PublishedNodesConfigurationSchemaFilename] = s }, { "s|site=", "The site OPC Publisher is working in.", s => this[StandaloneCliConfigKeys.PublisherSite] = s }, { $"di|diagnosticsinterval=|{StandaloneCliConfigKeys.DiagnosticsInterval}=", "Shows publisher diagnostic info at the specified interval " + "in seconds (need log level info).\n-1 disables remote diagnostic log and diagnostic output", (int i) => this[StandaloneCliConfigKeys.DiagnosticsInterval] = TimeSpan.FromSeconds(i).ToString() }, { $"lf|logfile=|{StandaloneCliConfigKeys.LogFileName}=", "The filename of the logfile to use.", s => this[StandaloneCliConfigKeys.LogFileName] = s }, { $"lt|logflushtimespan=|{StandaloneCliConfigKeys.LogFileFlushTimeSpanSec}=", "The timespan in seconds when the logfile should be flushed.", (int i) => this[StandaloneCliConfigKeys.LogFileFlushTimeSpanSec] = TimeSpan.FromSeconds(i).ToString() }, { "ll|loglevel=", "The loglevel to use (allowed: fatal, error, warn, info, debug, verbose).", (LogEventLevel l) => LogControl.Level.MinimumLevel = l }, { $"ih|iothubprotocol=|{StandaloneCliConfigKeys.HubTransport}=", "Protocol to use for communication with the hub. " + $"(allowed values: {string.Join(", ", Enum.GetNames(typeof(TransportOption)))}).", (TransportOption p) => this[StandaloneCliConfigKeys.HubTransport] = p.ToString() }, { "dc|deviceconnectionstring=", "A device or edge module connection string to use.", dc => this[StandaloneCliConfigKeys.EdgeHubConnectionString] = dc }, { $"ec|edgehubconnectionstring=|{StandaloneCliConfigKeys.EdgeHubConnectionString}=", "An edge module connection string to use", dc => this[StandaloneCliConfigKeys.EdgeHubConnectionString] = dc }, { $"{StandaloneCliConfigKeys.BypassCertVerificationKey}=", "Enables bypass of certificate verification for upstream communication to edgeHub.", (bool b) => this[StandaloneCliConfigKeys.BypassCertVerificationKey] = b.ToString() }, { $"{StandaloneCliConfigKeys.EnableMetricsKey}=", "Enables upstream metrics propagation.", (bool b) => this[StandaloneCliConfigKeys.EnableMetricsKey] = b.ToString() }, { $"hb|heartbeatinterval=|{StandaloneCliConfigKeys.HeartbeatIntervalDefault}=", "The publisher is using this as default value in seconds " + "for the heartbeat interval setting of nodes without a heartbeat interval setting.", (int i) => this[StandaloneCliConfigKeys.HeartbeatIntervalDefault] = TimeSpan.FromSeconds(i).ToString() }, // ToDo: Bring back once SkipFirst mechanism is implemented. //{ "sf|skipfirstevent=", "The publisher is using this as default value for the skip first " + // "event setting of nodes without a skip first event setting.", // (bool b) => this[StandaloneCliConfigKeys.SkipFirstDefault] = b.ToString() }, { $"fm|fullfeaturedmessage=|{StandaloneCliConfigKeys.FullFeaturedMessage}=", "The full featured mode for messages (all fields filled in)." + "Default is 'false' for legacy compatibility.", (bool b) => this[StandaloneCliConfigKeys.FullFeaturedMessage] = b.ToString() }, // Client settings { $"ot|operationtimeout=|{StandaloneCliConfigKeys.OpcOperationTimeout}=", "The operation timeout of the publisher OPC UA client in milliseconds.", (uint u) => this[StandaloneCliConfigKeys.OpcOperationTimeout] = u.ToString() }, { $"ol|opcmaxstringlen=|{StandaloneCliConfigKeys.OpcMaxStringLength}=", "The max length of a string opc can transmit/receive.", (uint u) => this[StandaloneCliConfigKeys.OpcMaxStringLength] = u.ToString() }, { $"{StandaloneCliConfigKeys.SecurityTokenLifetimeKey}=", "OPC UA Stack Transport Secure Channel - Security token lifetime in milliseconds.", (uint u) => this[StandaloneCliConfigKeys.SecurityTokenLifetimeKey] = u.ToString() }, { $"{StandaloneCliConfigKeys.ChannelLifetimeKey}=", "OPC UA Stack Transport Secure Channel - Channel lifetime in milliseconds.", (uint u) => this[StandaloneCliConfigKeys.ChannelLifetimeKey] = u.ToString() }, { $"{StandaloneCliConfigKeys.MaxBufferSizeKey}=", "OPC UA Stack Transport Secure Channel - Max buffer size.", (uint u) => this[StandaloneCliConfigKeys.MaxBufferSizeKey] = u.ToString() }, { $"{StandaloneCliConfigKeys.MaxMessageSizeKey}=", "OPC UA Stack Transport Secure Channel - Max message size.", (uint u) => this[StandaloneCliConfigKeys.MaxMessageSizeKey] = u.ToString() }, { $"{StandaloneCliConfigKeys.MaxArrayLengthKey}=", "OPC UA Stack Transport Secure Channel - Max array length.", (uint u) => this[StandaloneCliConfigKeys.MaxArrayLengthKey] = u.ToString() }, { $"{StandaloneCliConfigKeys.MaxByteStringLengthKey}=", "OPC UA Stack Transport Secure Channel - Max byte string length.", (uint u) => this[StandaloneCliConfigKeys.MaxByteStringLengthKey] = u.ToString() }, { $"oi|opcsamplinginterval=|{StandaloneCliConfigKeys.OpcSamplingInterval}=", "Default value in milliseconds to request the servers to " + "sample values.", (int i) => this[StandaloneCliConfigKeys.OpcSamplingInterval] = TimeSpan.FromMilliseconds(i).ToString() }, { $"op|opcpublishinginterval=|{StandaloneCliConfigKeys.OpcPublishingInterval}=", "Default value in milliseconds for the publishing interval " + "setting of the subscriptions against the OPC UA server.", (int i) => this[StandaloneCliConfigKeys.OpcPublishingInterval] = TimeSpan.FromMilliseconds(i).ToString() }, { $"{StandaloneCliConfigKeys.ApplicationUriKey}=", "OPC UA Client Application Config - Application URI as per OPC UA definition.", s => this[StandaloneCliConfigKeys.ApplicationUriKey] = s }, { $"{StandaloneCliConfigKeys.ProductUriKey}=", "OPC UA Client Application Config - Product URI as per OPC UA definition.", s => this[StandaloneCliConfigKeys.ProductUriKey] = s }, { $"ct|createsessiontimeout=|{StandaloneCliConfigKeys.OpcSessionCreationTimeout}=", "Maximum amount of time in seconds that a session should " + "remain open by the OPC server without any activity (session timeout) " + "- to request from the OPC server at session creation.", (uint u) => this[StandaloneCliConfigKeys.OpcSessionCreationTimeout] = u.ToString() }, { $"{StandaloneCliConfigKeys.MinSubscriptionLifetimeKey}=", "OPC UA Client Application Config - " + "Minimum subscription lifetime in seconds as per OPC UA definition.", (int i) => this[StandaloneCliConfigKeys.MinSubscriptionLifetimeKey] = i.ToString() }, { $"ki|keepaliveinterval=|{StandaloneCliConfigKeys.OpcKeepAliveIntervalInSec}=", "The interval in seconds the publisher is sending keep alive messages " + "to the OPC servers on the endpoints it is connected to.", (int i) => this[StandaloneCliConfigKeys.OpcKeepAliveIntervalInSec] = i.ToString() }, { $"kt|keepalivethreshold=|{StandaloneCliConfigKeys.OpcKeepAliveDisconnectThreshold}=", "Specify the number of keep alive packets a server can miss, before the session is disconneced.", (uint u) => this[StandaloneCliConfigKeys.OpcKeepAliveDisconnectThreshold] = u.ToString() }, { $"fd|fetchdisplayname=|{StandaloneCliConfigKeys.FetchOpcNodeDisplayName}=", "Fetches the displayname for the monitored items subscribed.", (bool b) => this[StandaloneCliConfigKeys.FetchOpcNodeDisplayName] = b.ToString() }, { $"mq|monitoreditemqueuecapacity=|{StandaloneCliConfigKeys.DefaultQueueSize}=", "Default queue size for monitored items.", (uint u) => this[StandaloneCliConfigKeys.DefaultQueueSize] = u.ToString() }, // cert store option { $"aa|autoaccept", "The publisher trusts all servers it is establishing a connection to.", b => this[StandaloneCliConfigKeys.AutoAcceptCerts] = (b != null).ToString() }, { $"{StandaloneCliConfigKeys.AutoAcceptCerts}=", "The publisher trusts all servers it is establishing a connection to.", (bool b) => this[StandaloneCliConfigKeys.AutoAcceptCerts] = b.ToString() }, { $"tm|trustmyself", "The publisher certificate is put into the trusted store automatically.", b => this[StandaloneCliConfigKeys.TrustMyself] = (b != null).ToString() }, { $"{StandaloneCliConfigKeys.TrustMyself}=", "The publisher certificate is put into the trusted store automatically.", (bool b) => this[StandaloneCliConfigKeys.TrustMyself] = b.ToString() }, { $"at|appcertstoretype=|{StandaloneCliConfigKeys.OpcOwnCertStoreType}=", "The own application cert store type (allowed: Directory, X509Store).", s => { if (s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { this[StandaloneCliConfigKeys.OpcOwnCertStoreType] = s; return; } throw new OptionException("Bad store type", "at"); } }, { $"ap|appcertstorepath=|{StandaloneCliConfigKeys.OpcOwnCertStorePath}=", "The path where the own application cert should be stored.", s => this[StandaloneCliConfigKeys.OpcOwnCertStorePath] = s }, { $"tp|trustedcertstorepath=|{StandaloneCliConfigKeys.OpcTrustedCertStorePath}=", "The path of the trusted cert store.", s => this[StandaloneCliConfigKeys.OpcTrustedCertStorePath] = s }, { $"sn|appcertsubjectname=|{StandaloneCliConfigKeys.OpcApplicationCertificateSubjectName}=", "The subject name for the app cert.", s => this[StandaloneCliConfigKeys.OpcApplicationCertificateSubjectName] = s }, { $"an|appname=|{StandaloneCliConfigKeys.OpcApplicationName}=", "The name for the app (used during OPC UA authentication).", s => this[StandaloneCliConfigKeys.OpcApplicationName] = s }, { "tt|trustedcertstoretype=", "Legacy - do not use.", b => { legacyOptions.Add("tt|trustedcertstoretype"); } }, { $"rp|rejectedcertstorepath=|{StandaloneCliConfigKeys.OpcRejectedCertStorePath}=", "The path of the rejected cert store.", s => this[StandaloneCliConfigKeys.OpcRejectedCertStorePath] = s }, { "rt|rejectedcertstoretype=", "Legacy - do not use.", b => { legacyOptions.Add("rt|rejectedcertstoretype"); } }, { $"ip|issuercertstorepath=|{StandaloneCliConfigKeys.OpcIssuerCertStorePath}=", "The path of the trusted issuer cert store.", s => this[StandaloneCliConfigKeys.OpcIssuerCertStorePath] = s }, { $"{StandaloneCliConfigKeys.PkiRootPathKey}=", "PKI certificate store root path.", s => this[StandaloneCliConfigKeys.PkiRootPathKey] = s }, { $"{StandaloneCliConfigKeys.TrustedIssuerCertificatesTypeKey}=", "Trusted issuer certificate types.", s => this[StandaloneCliConfigKeys.TrustedIssuerCertificatesTypeKey] = s }, { $"{StandaloneCliConfigKeys.TrustedPeerCertificatesTypeKey}=", "Trusted peer certificate types.", s => this[StandaloneCliConfigKeys.TrustedPeerCertificatesTypeKey] = s }, { $"{StandaloneCliConfigKeys.RejectedCertificateStoreTypeKey}=", "Rejected certificate types.", s => this[StandaloneCliConfigKeys.RejectedCertificateStoreTypeKey] = s }, { $"{StandaloneCliConfigKeys.RejectSha1SignedCertificatesKey}=", "The publisher rejects deprecated SHA1 certificates.", (bool b) => this[StandaloneCliConfigKeys.RejectSha1SignedCertificatesKey] = b.ToString() }, { $"{StandaloneCliConfigKeys.MinimumCertificateKeySizeKey}=", "Minimum accepted certificate size.", s => this[StandaloneCliConfigKeys.MinimumCertificateKeySizeKey] = s }, { "it|issuercertstoretype=", "Legacy - do not use.", b => { legacyOptions.Add("it|issuercertstoretype"); } }, { $"bs|batchsize=|{StandaloneCliConfigKeys.BatchSize}=", "The size of message batching buffer.", (int i) => this[StandaloneCliConfigKeys.BatchSize] = i.ToString() }, { $"si|iothubsendinterval=|{StandaloneCliConfigKeys.BatchTriggerInterval}=", "The trigger batching interval in seconds.", (int k) => this[StandaloneCliConfigKeys.BatchTriggerInterval] = TimeSpan.FromSeconds(k).ToString() }, { $"ms|iothubmessagesize=|{StandaloneCliConfigKeys.IoTHubMaxMessageSize}=", "The maximum size of the (IoT D2C) message.", (int i) => this[StandaloneCliConfigKeys.IoTHubMaxMessageSize] = i.ToString() }, { $"om|maxoutgressmessages=|{StandaloneCliConfigKeys.MaxOutgressMessages}=", "The maximum size of the (IoT D2C) message outgress buffer", (int i) => this[StandaloneCliConfigKeys.MaxOutgressMessages] = i.ToString() }, { $"{StandaloneCliConfigKeys.MaxNodesPerDataSet}=", "Maximum number of nodes within a DataSet/Subscription.", (int i) => this[StandaloneCliConfigKeys.MaxNodesPerDataSet] = i.ToString() }, { $"mm|messagingmode=|{StandaloneCliConfigKeys.MessagingMode}=", "The messaging mode for messages " + $"(allowed values: {string.Join(", ", Enum.GetNames(typeof(MessagingMode)))}).", (MessagingMode m) => this[StandaloneCliConfigKeys.MessagingMode] = m.ToString() }, { $"me|messageencoding=|{StandaloneCliConfigKeys.MessageEncoding}=", "The message encoding for messages " + $"(allowed values: {string.Join(", ", Enum.GetNames(typeof(MessageEncoding)))}).", (MessageEncoding m) => this[StandaloneCliConfigKeys.MessageEncoding] = m.ToString() }, { $"lc|legacycompatibility=|{StandaloneCliConfigKeys.LegacyCompatibility}=", "Run the publisher in legacy (2.5.x) compatibility mode. " + "Default is 'false'.", (bool b) => this[StandaloneCliConfigKeys.LegacyCompatibility] = b.ToString() }, { $"rs|runtimestatereporting=|{StandaloneCliConfigKeys.RuntimeStateReporting}=", "The publisher reports its restarts. By default this is disabled.", (bool b) => this[StandaloneCliConfigKeys.RuntimeStateReporting] = b.ToString() }, { $"ri|enableroutinginfo=|{StandaloneCliConfigKeys.EnableRoutingInfo}=", "Enable adding routing info to telemetry. By default this is disabled.", (bool b) => this[StandaloneCliConfigKeys.EnableRoutingInfo] = b.ToString() }, // testing purposes { "sc|scaletestcount=", "The number of monitored item clones in scale tests.", (int i) => this[StandaloneCliConfigKeys.ScaleTestCount] = i.ToString() }, // show help { "h|help", "show this message and exit.", b => showHelp = true }, // Legacy: unsupported { "tc|telemetryconfigfile=", "Legacy - do not use.", b => { legacyOptions.Add("tc|telemetryconfigfile"); } }, { "ic|iotcentral=", "Legacy - do not use.", b => { legacyOptions.Add("ic|iotcentral"); } }, { "ns|noshutdown=", "Legacy - do not use.", b => { legacyOptions.Add("ns|noshutdown"); } }, { "rf|runforever", "Legacy - do not use.", b => { legacyOptions.Add("rf|runforever"); } }, { "pn|portnum=", "Legacy - do not use.", b => { legacyOptions.Add("pn|portnum"); } }, { "pa|path=", "Legacy - do not use.", b => { legacyOptions.Add("pa|path"); } }, { "lr|ldsreginterval=", "Legacy - do not use.", b => { legacyOptions.Add("lr|ldsreginterval"); } }, { "ss|suppressedopcstatuscodes=", "Legacy - do not use.", b => { legacyOptions.Add("ss|suppressedopcstatuscodes"); } }, { "csr", "Legacy - do not use.", b => { legacyOptions.Add("csr"); } }, { "ab|applicationcertbase64=", "Legacy - do not use.", b => { legacyOptions.Add("ab|applicationcertbase64"); } }, { "af|applicationcertfile=", "Legacy - do not use.", b => { legacyOptions.Add("af|applicationcertfile"); } }, { "pk|privatekeyfile=", "Legacy - do not use.", b => { legacyOptions.Add("pk|privatekeyfile"); } }, { "pb|privatekeybase64=", "Legacy - do not use.", b => { legacyOptions.Add("pb|privatekeybase64"); } }, { "cp|certpassword="******"Legacy - do not use.", b => { legacyOptions.Add("cp|certpassword"); } }, { "tb|addtrustedcertbase64=", "Legacy - do not use.", b => { legacyOptions.Add("tb|addtrustedcertbase64"); } }, { "tf|addtrustedcertfile=", "Legacy - do not use.", b => { legacyOptions.Add("tf|addtrustedcertfile"); } }, { "ib|addissuercertbase64=", "Legacy - do not use.", b => { legacyOptions.Add("ib|addissuercertbase64"); } }, { "if|addissuercertfile=", "Legacy - do not use.", b => { legacyOptions.Add("if|addissuercertfile"); } }, { "rb|updatecrlbase64=", "Legacy - do not use.", b => { legacyOptions.Add("rb|updatecrlbase64"); } }, { "uc|updatecrlfile=", "Legacy - do not use.", b => { legacyOptions.Add("uc|updatecrlfile"); } }, { "rc|removecert=", "Legacy - do not use.", b => { legacyOptions.Add("rc|removecert"); } }, { "dt|devicecertstoretype=", "Legacy - do not use.", b => { legacyOptions.Add("dt|devicecertstoretype"); } }, { "dp|devicecertstorepath=", "Legacy - do not use.", b => { legacyOptions.Add("dp|devicecertstorepath"); } }, { "i|install", "Legacy - do not use.", b => { legacyOptions.Add("i|install"); } }, { "st|opcstacktracemask=", "Legacy - do not use.", b => { legacyOptions.Add("st|opcstacktracemask"); } }, { "sd|shopfloordomain=", "Legacy - do not use.", b => { legacyOptions.Add("sd|shopfloordomain"); } }, { "vc|verboseconsole=", "Legacy - do not use.", b => { legacyOptions.Add("vc|verboseconsole"); } }, { "as|autotrustservercerts=", "Legacy - do not use.", b => { legacyOptions.Add("as|autotrustservercerts"); } }, }; try { unsupportedOptions = options.Parse(args); } catch (Exception e) { Warning("Parse args exception: " + e.Message); Exit(160); } if (_logger.IsEnabled(LogEventLevel.Debug)) { foreach (var key in this.Keys) { Debug("Parsed command line option: '{key}'='{value}'", key, this[key]); } } if (unsupportedOptions.Count > 0) { foreach (var option in unsupportedOptions) { Warning("Option {option} wrong or not supported, " + "please use -h option to get all the supported options.", option); } } if (legacyOptions.Count > 0) { foreach (var option in legacyOptions) { Warning("Legacy option {option} not supported, please use -h option to get all the supported options.", option); } } if (showHelp) { options.WriteOptionDescriptions(Console.Out); Exit(0); } Config = ToAgentConfigModel(); }
// TODO: Figure out which are actually supported in the new publisher implementation /// <summary> /// Parse arguments and set values in the environment the way the new configuration expects it. /// </summary> /// <param name="args">The specified command line arguments.</param> public LegacyCliOptions(string[] args) { // command line options var options = new Mono.Options.OptionSet { // Publisher configuration options { "pf|publishfile=", "the filename to configure the nodes to publish.", s => this[LegacyCliConfigKeys.PublisherNodeConfigurationFilename] = s }, { "s|site=", "the site OPC Publisher is working in.", s => this[LegacyCliConfigKeys.PublisherSite] = s }, { "di|diagnosticsinterval=", "Shows publisher diagnostic info at the specified interval " + "in seconds (need log level info).\n-1 disables remote diagnostic log and diagnostic output", (int i) => this[LegacyCliConfigKeys.DiagnosticsInterval] = TimeSpan.FromSeconds(i).ToString() }, { "lf|logfile=", "the filename of the logfile to use.", s => this[LegacyCliConfigKeys.LogFileName] = s }, { "lt|logflushtimespan=", "the timespan in seconds when the logfile should be flushed.", (int i) => this[LegacyCliConfigKeys.LogFileFlushTimeSpanSec] = TimeSpan.FromSeconds(i).ToString() }, { "ll|loglevel=", "the loglevel to use (allowed: fatal, error, warn, info, debug, verbose).", (LogEventLevel l) => LogControl.Level.MinimumLevel = l }, { "ih|iothubprotocol=", "Protocol to use for communication with the hub. " + $"(allowed values: {string.Join(", ", Enum.GetNames(typeof(TransportOption)))}).", (TransportOption p) => this[LegacyCliConfigKeys.HubTransport] = p.ToString() }, { "dc|deviceconnectionstring=", "A device or edge module connection string to use.", dc => this[LegacyCliConfigKeys.EdgeHubConnectionString] = dc }, { "ec|edgehubconnectionstring=", "An edge module connection string to use", dc => this[LegacyCliConfigKeys.EdgeHubConnectionString] = dc }, { "hb|heartbeatinterval=", "the publisher is using this as default value in seconds " + "for the heartbeat interval setting of nodes without a heartbeat interval setting.", (int i) => this[LegacyCliConfigKeys.HeartbeatIntervalDefault] = TimeSpan.FromSeconds(i).ToString() }, { "sf|skipfirstevent=", "the publisher is using this as default value for the skip first " + "event setting of nodes without a skip first event setting.", (bool b) => this[LegacyCliConfigKeys.SkipFirstDefault] = b.ToString() }, { "mm|messagingmode=", "The messaging mode for messages " + $"(allowed values: {string.Join(", ", Enum.GetNames(typeof(MessagingMode)))}).", (MessagingMode m) => this[LegacyCliConfigKeys.MessagingMode] = m.ToString() }, // Client settings { "ot|operationtimeout=", "the operation timeout of the publisher OPC UA client in ms.", (uint i) => this[LegacyCliConfigKeys.OpcOperationTimeout] = TimeSpan.FromMilliseconds(i).ToString() }, { "ol|opcmaxstringlen=", "the max length of a string opc can transmit/receive.", (uint i) => this[LegacyCliConfigKeys.OpcMaxStringLength] = i.ToString() }, { "oi|opcsamplinginterval=", "Default value in milliseconds to request the servers to " + "sample values.", (int i) => this[LegacyCliConfigKeys.OpcSamplingInterval] = TimeSpan.FromMilliseconds(i).ToString() }, { "op|opcpublishinginterval=", "Default value in milliseconds for the publishing interval " + "setting of the subscriptions against the OPC UA server.", (int i) => this[LegacyCliConfigKeys.OpcPublishingInterval] = TimeSpan.FromMilliseconds(i).ToString() }, { "ct|createsessiontimeout=", "The timeout in seconds used when creating a session to an endpoint.", (uint u) => this[LegacyCliConfigKeys.OpcSessionCreationTimeout] = TimeSpan.FromSeconds(u).ToString() }, { "ki|keepaliveinterval=", "The interval in seconds the publisher is sending keep alive messages " + "to the OPC servers on the endpoints it is connected to.", (int i) => this[LegacyCliConfigKeys.OpcKeepAliveIntervalInSec] = TimeSpan.FromSeconds(i).ToString() }, { "kt|keepalivethreshold=", "specify the number of keep alive packets a server can miss, " + "before the session is disconneced.", (uint u) => this[LegacyCliConfigKeys.OpcKeepAliveDisconnectThreshold] = u.ToString() }, { "fd|fetchdisplayname=", "same as fetchname.", (bool b) => this[LegacyCliConfigKeys.FetchOpcNodeDisplayName] = b.ToString() }, { "sw|sessionconnectwait=", "Wait time in seconds publisher is trying to connect " + "to disconnected endpoints and starts monitoring unmonitored items.", (int s) => this[LegacyCliConfigKeys.SessionConnectWaitSec] = TimeSpan.FromSeconds(s).ToString() }, // cert store options { "aa|autoaccept", "the publisher trusts all servers it is establishing a connection to.", b => this[LegacyCliConfigKeys.AutoAcceptCerts] = (b != null).ToString() }, { "to|trustowncert", "the publisher certificate is put into the trusted store automatically.", t => this[LegacyCliConfigKeys.TrustMyself] = (t != null).ToString() }, { "at|appcertstoretype=", "the own application cert store type (allowed: Directory, X509Store).", s => { if (s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { this[LegacyCliConfigKeys.OpcOwnCertStoreType] = s; return; } throw new OptionException("Bad store type", "at"); } }, { "ap|appcertstorepath=", "the path where the own application cert should be stored.", s => this[LegacyCliConfigKeys.OpcOwnCertStorePath] = s }, { "tp|trustedcertstorepath=", "the path of the trusted cert store.", s => this[LegacyCliConfigKeys.OpcTrustedCertStorePath] = s }, { "tt|trustedcertstoretype=", "Legacy - do not use.", _ => {} }, { "rp|rejectedcertstorepath=", "the path of the rejected cert store.", s => this[LegacyCliConfigKeys.OpcRejectedCertStorePath] = s }, { "rt|rejectedcertstoretype=", "Legacy - do not use.", _ => {} }, { "ip|issuercertstorepath=", "the path of the trusted issuer cert store.", s => this[LegacyCliConfigKeys.OpcIssuerCertStorePath] = s }, { "it|issuercertstoretype=", "Legacy - do not use.", _ => {} }, // Legacy unsupported { "si|iothubsendinterval=", "Legacy - do not use.", _ => {} }, { "tc|telemetryconfigfile=", "Legacy - do not use.", _ => {} }, { "ic|iotcentral=", "Legacy - do not use.", _ => {} }, { "mq|monitoreditemqueuecapacity=", "Legacy - do not use.", _ => {} }, { "ns|noshutdown=", "Legacy - do not use.", _ => {} }, { "rf|runforever", "Legacy - do not use.", _ => {} }, { "ms|iothubmessagesize=", "Legacy - do not use.", _ => {} }, { "pn|portnum=", "Legacy - do not use.", _ => {} }, { "pa|path=", "Legacy - do not use.", _ => {} }, { "lr|ldsreginterval=", "Legacy - do not use.", _ => {} }, { "ss|suppressedopcstatuscodes=", "Legacy - do not use.", _ => {} }, { "csr", "Legacy - do not use.", _ => {} }, { "ab|applicationcertbase64=", "Legacy - do not use.", _ => {} }, { "af|applicationcertfile=", "Legacy - do not use.", _ => {} }, { "pk|privatekeyfile=", "Legacy - do not use.", _ => {} }, { "pb|privatekeybase64=", "Legacy - do not use.", _ => {} }, { "cp|certpassword="******"Legacy - do not use.", _ => {} }, { "tb|addtrustedcertbase64=", "Legacy - do not use.", _ => {} }, { "tf|addtrustedcertfile=", "Legacy - do not use.", _ => {} }, { "ib|addissuercertbase64=", "Legacy - do not use.", _ => {} }, { "if|addissuercertfile=", "Legacy - do not use.", _ => {} }, { "rb|updatecrlbase64=", "Legacy - do not use.", _ => {} }, { "uc|updatecrlfile=", "Legacy - do not use.", _ => {} }, { "rc|removecert=", "Legacy - do not use.", _ => {} }, { "dt|devicecertstoretype=", "Legacy - do not use.", _ => {} }, { "dp|devicecertstorepath=", "Legacy - do not use.", _ => {} }, { "i|install", "Legacy - do not use.", _ => {} }, { "st|opcstacktracemask=", "Legacy - do not use.", _ => {} }, { "sd|shopfloordomain=", "Legacy - do not use.", _ => {} }, { "vc|verboseconsole=", "Legacy - do not use.", _ => {} }, { "as|autotrustservercerts=", "Legacy - do not use.", _ => {} } }; options.Parse(args); Config = ToAgentConfigModel(); LegacyCliModel = ToLegacyCliModel(); }
/// <summary> /// Asynchronous part of the main method of the app. /// </summary> public async static Task MainAsync(string[] args) { try { var shouldShowHelp = false; // Shutdown token sources. ShutdownTokenSource = new CancellationTokenSource(); // detect the runtime environment. either we run standalone (native or containerized) or as IoT Edge module (containerized) // check if we have an environment variable containing an IoT Edge connectionstring, we run as IoT Edge module if (IsIotEdgeModule) { WriteLine("IoTEdge detected."); } // command line options Mono.Options.OptionSet options = new Mono.Options.OptionSet { // Publisher configuration options { "pf|publishfile=", $"the filename to configure the nodes to publish.\nDefault: '{PublisherNodeConfigurationFilename}'", (string p) => PublisherNodeConfigurationFilename = p }, { "tc|telemetryconfigfile=", $"the filename to configure the ingested telemetry\nDefault: '{PublisherTelemetryConfigurationFilename}'", (string p) => PublisherTelemetryConfigurationFilename = p }, { "s|site=", $"the site OPC Publisher is working in. if specified this domain is appended (delimited by a ':' to the 'ApplicationURI' property when telemetry is sent to IoTHub.\n" + "The value must follow the syntactical rules of a DNS hostname.\nDefault: not set", (string s) => { Regex siteNameRegex = new Regex("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"); if (siteNameRegex.IsMatch(s)) { PublisherSite = s; } else { throw new OptionException("The shopfloor site is not a valid DNS hostname.", "site"); } } }, { "sd|shopfloordomain=", $"same as site option, only there for backward compatibility\n" + "The value must follow the syntactical rules of a DNS hostname.\nDefault: not set", (string s) => { Regex siteNameRegex = new Regex("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"); if (siteNameRegex.IsMatch(s)) { PublisherSite = s; } else { throw new OptionException("The shopfloor domain is not a valid DNS hostname.", "shopfloordomain"); } } }, { "ic|iotcentral", $"publisher will send OPC UA data in IoTCentral compatible format (DisplayName of a node is used as key, this key is the Field name in IoTCentral). you need to ensure that all DisplayName's are unique. (Auto enables fetch display name)\nDefault: {IotCentralMode}", b => IotCentralMode = FetchOpcNodeDisplayName = b != null }, { "sw|sessionconnectwait=", $"specify the wait time in seconds publisher is trying to connect to disconnected endpoints and starts monitoring unmonitored items\nMin: 10\nDefault: {SessionConnectWaitSec}", (int i) => { if (i > 10) { SessionConnectWaitSec = i; } else { throw new OptionException("The sessionconnectwait must be greater than 10 sec", "sessionconnectwait"); } } }, { "mq|monitoreditemqueuecapacity=", $"specify how many notifications of monitored items can be stored in the internal queue, if the data can not be sent quick enough to IoTHub\nMin: 1024\nDefault: {MonitoredItemsQueueCapacity}", (int i) => { if (i >= 1024) { MonitoredItemsQueueCapacity = i; } else { throw new OptionException("The monitoreditemqueueitems must be greater than 1024", "monitoreditemqueueitems"); } } }, { "di|diagnosticsinterval=", $"shows publisher diagnostic info at the specified interval in seconds (need log level info). 0 disables diagnostic output.\nDefault: {DiagnosticsInterval}", (uint u) => DiagnosticsInterval = u }, { "vc|verboseconsole=", $"ignored, only supported for backward comaptibility.", b => {} }, { "ns|noshutdown=", $"same as runforever.\nDefault: {_noShutdown}", (bool b) => _noShutdown = b }, { "rf|runforever", $"publisher can not be stopped by pressing a key on the console, but will run forever.\nDefault: {_noShutdown}", b => _noShutdown = b != null }, { "lf|logfile=", $"the filename of the logfile to use.\nDefault: './{_logFileName}'", (string l) => _logFileName = l }, { "lt|logflushtimespan=", $"the timespan in seconds when the logfile should be flushed.\nDefault: {_logFileFlushTimeSpanSec} sec", (int s) => { if (s > 0) { _logFileFlushTimeSpanSec = TimeSpan.FromSeconds(s); } else { throw new Mono.Options.OptionException("The logflushtimespan must be a positive number.", "logflushtimespan"); } } }, { "ll|loglevel=", $"the loglevel to use (allowed: fatal, error, warn, info, debug, verbose).\nDefault: info", (string l) => { List <string> logLevels = new List <string> { "fatal", "error", "warn", "info", "debug", "verbose" }; if (logLevels.Contains(l.ToLowerInvariant())) { _logLevel = l.ToLowerInvariant(); } else { throw new Mono.Options.OptionException("The loglevel must be one of: fatal, error, warn, info, debug, verbose", "loglevel"); } } }, // IoTHub specific options { "ih|iothubprotocol=", $"{(IsIotEdgeModule ? "not supported when running as IoTEdge module (Mqtt_Tcp_Only is enforced)\n" : $"the protocol to use for communication with Azure IoTHub (allowed values: {string.Join(", ", Enum.GetNames(IotHubProtocol.GetType()))}).\nDefault: {Enum.GetName(IotHubProtocol.GetType(), IotHubProtocol)}")}", (Microsoft.Azure.Devices.Client.TransportType p) => { if (IsIotEdgeModule) { if (p != Microsoft.Azure.Devices.Client.TransportType.Mqtt_Tcp_Only) { WriteLine("When running as IoTEdge module Mqtt_Tcp_Only is enforced."); IotHubProtocol = Microsoft.Azure.Devices.Client.TransportType.Mqtt_Tcp_Only; } } else { IotHubProtocol = p; } } }, { "ms|iothubmessagesize=", $"the max size of a message which can be send to IoTHub. when telemetry of this size is available it will be sent.\n0 will enforce immediate send when telemetry is available\nMin: 0\nMax: {HubMessageSizeMax}\nDefault: {HubMessageSize}", (uint u) => { if (u >= 0 && u <= HubMessageSizeMax) { HubMessageSize = u; } else { throw new OptionException("The iothubmessagesize must be in the range between 1 and 256*1024.", "iothubmessagesize"); } } }, { "si|iothubsendinterval=", $"the interval in seconds when telemetry should be send to IoTHub. If 0, then only the iothubmessagesize parameter controls when telemetry is sent.\nDefault: '{DefaultSendIntervalSeconds}'", (int i) => { if (i >= 0) { DefaultSendIntervalSeconds = i; } else { throw new OptionException("The iothubsendinterval must be larger or equal 0.", "iothubsendinterval"); } } }, { "dc|deviceconnectionstring=", $"{(IsIotEdgeModule ? "not supported when running as IoTEdge module\n" : $"if publisher is not able to register itself with IoTHub, you can create a device with name <applicationname> manually and pass in the connectionstring of this device.\nDefault: none")}", (string dc) => DeviceConnectionString = (IsIotEdgeModule ? null : dc) }, { "c|connectionstring=", $"the IoTHub owner connectionstring.\nDefault: none", (string cs) => IotHubOwnerConnectionString = cs }, // opc server configuration options { "pn|portnum=", $"the server port of the publisher OPC server endpoint.\nDefault: {PublisherServerPort}", (ushort p) => PublisherServerPort = p }, { "pa|path=", $"the enpoint URL path part of the publisher OPC server endpoint.\nDefault: '{PublisherServerPath}'", (string a) => PublisherServerPath = a }, { "lr|ldsreginterval=", $"the LDS(-ME) registration interval in ms. If 0, then the registration is disabled.\nDefault: {LdsRegistrationInterval}", (int i) => { if (i >= 0) { LdsRegistrationInterval = i; } else { throw new OptionException("The ldsreginterval must be larger or equal 0.", "ldsreginterval"); } } }, { "ol|opcmaxstringlen=", $"the max length of a string opc can transmit/receive.\nDefault: {OpcMaxStringLength}", (int i) => { if (i > 0) { OpcMaxStringLength = i; } else { throw new OptionException("The max opc string length must be larger than 0.", "opcmaxstringlen"); } } }, { "ot|operationtimeout=", $"the operation timeout of the publisher OPC UA client in ms.\nDefault: {OpcOperationTimeout}", (int i) => { if (i >= 0) { OpcOperationTimeout = i; } else { throw new OptionException("The operation timeout must be larger or equal 0.", "operationtimeout"); } } }, { "oi|opcsamplinginterval=", "the publisher is using this as default value in milliseconds to request the servers to sample the nodes with this interval\n" + "this value might be revised by the OPC UA servers to a supported sampling interval.\n" + "please check the OPC UA specification for details how this is handled by the OPC UA stack.\n" + "a negative value will set the sampling interval to the publishing interval of the subscription this node is on.\n" + $"0 will configure the OPC UA server to sample in the highest possible resolution and should be taken with care.\nDefault: {OpcSamplingInterval}", (int i) => OpcSamplingInterval = i }, { "op|opcpublishinginterval=", "the publisher is using this as default value in milliseconds for the publishing interval setting of the subscriptions established to the OPC UA servers.\n" + "please check the OPC UA specification for details how this is handled by the OPC UA stack.\n" + $"a value less than or equal zero will let the server revise the publishing interval.\nDefault: {OpcPublishingInterval}", (int i) => { if (i > 0 && i >= OpcSamplingInterval) { OpcPublishingInterval = i; } else { if (i <= 0) { OpcPublishingInterval = 0; } else { throw new OptionException($"The opcpublishinterval ({i}) must be larger than the opcsamplinginterval ({OpcSamplingInterval}).", "opcpublishinterval"); } } } }, { "ct|createsessiontimeout=", $"specify the timeout in seconds used when creating a session to an endpoint. On unsuccessful connection attemps a backoff up to {OpcSessionCreationBackoffMax} times the specified timeout value is used.\nMin: 1\nDefault: {OpcSessionCreationTimeout}", (uint u) => { if (u > 1) { OpcSessionCreationTimeout = u; } else { throw new OptionException("The createsessiontimeout must be greater than 1 sec", "createsessiontimeout"); } } }, { "ki|keepaliveinterval=", $"specify the interval in seconds the publisher is sending keep alive messages to the OPC servers on the endpoints it is connected to.\nMin: 2\nDefault: {OpcKeepAliveIntervalInSec}", (int i) => { if (i >= 2) { OpcKeepAliveIntervalInSec = i; } else { throw new OptionException("The keepaliveinterval must be greater or equal 2", "keepalivethreshold"); } } }, { "kt|keepalivethreshold=", $"specify the number of keep alive packets a server can miss, before the session is disconneced\nMin: 1\nDefault: {OpcKeepAliveDisconnectThreshold}", (uint u) => { if (u > 1) { OpcKeepAliveDisconnectThreshold = u; } else { throw new OptionException("The keepalivethreshold must be greater than 1", "keepalivethreshold"); } } }, { "st|opcstacktracemask=", $"ignored, only supported for backward comaptibility.", i => {} }, { "as|autotrustservercerts=", $"same as autoaccept, only supported for backward cmpatibility.\nDefault: {OpcPublisherAutoTrustServerCerts}", (bool b) => OpcPublisherAutoTrustServerCerts = b }, { "aa|autoaccept", $"the publisher trusts all servers it is establishing a connection to.\nDefault: {OpcPublisherAutoTrustServerCerts}", b => OpcPublisherAutoTrustServerCerts = b != null }, // trust own public cert option { "tm|trustmyself=", $"same as trustowncert.\nDefault: {TrustMyself}", (bool b) => TrustMyself = b }, { "to|trustowncert", $"the publisher certificate is put into the trusted certificate store automatically.\nDefault: {TrustMyself}", t => TrustMyself = t != null }, // read the display name of the nodes to publish from the server and publish them instead of the node id { "fd|fetchdisplayname=", $"same as fetchname.\nDefault: {FetchOpcNodeDisplayName}", (bool b) => FetchOpcNodeDisplayName = IotCentralMode ? true : b }, { "fn|fetchname", $"enable to read the display name of a published node from the server. this will increase the runtime.\nDefault: {FetchOpcNodeDisplayName}", b => FetchOpcNodeDisplayName = IotCentralMode ? true : b != null }, // own cert store options { "at|appcertstoretype=", $"the own application cert store type. \n(allowed values: Directory, X509Store)\nDefault: '{OpcOwnCertStoreType}'", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcOwnCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : CertificateStoreType.Directory; OpcOwnCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcOwnCertX509StorePathDefault : OpcOwnCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ap|appcertstorepath=", $"the path where the own application cert should be stored\nDefault (depends on store type):\n" + $"X509Store: '{OpcOwnCertX509StorePathDefault}'\n" + $"Directory: '{OpcOwnCertDirectoryStorePathDefault}'", (string s) => OpcOwnCertStorePath = s }, // trusted cert store options { "tt|trustedcertstoretype=", $"the trusted cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcTrustedCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcTrustedCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : CertificateStoreType.Directory; OpcTrustedCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcTrustedCertX509StorePathDefault : OpcTrustedCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "tp|trustedcertstorepath=", $"the path of the trusted cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcTrustedCertX509StorePathDefault}'\n" + $"Directory: '{OpcTrustedCertDirectoryStorePathDefault}'", (string s) => OpcTrustedCertStorePath = s }, // rejected cert store options { "rt|rejectedcertstoretype=", $"the rejected cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcRejectedCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcRejectedCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : CertificateStoreType.Directory; OpcRejectedCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcRejectedCertX509StorePathDefault : OpcRejectedCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "rp|rejectedcertstorepath=", $"the path of the rejected cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcRejectedCertX509StorePathDefault}'\n" + $"Directory: '{OpcRejectedCertDirectoryStorePathDefault}'", (string s) => OpcRejectedCertStorePath = s }, // issuer cert store options { "it|issuercertstoretype=", $"the trusted issuer cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcIssuerCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcIssuerCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : CertificateStoreType.Directory; OpcIssuerCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? OpcIssuerCertX509StorePathDefault : OpcIssuerCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ip|issuercertstorepath=", $"the path of the trusted issuer cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcIssuerCertX509StorePathDefault}'\n" + $"Directory: '{OpcIssuerCertDirectoryStorePathDefault}'", (string s) => OpcIssuerCertStorePath = s }, // device connection string cert store options { "dt|devicecertstoretype=", $"the iothub device cert store type. \n(allowed values: Directory, X509Store)\nDefault: {IotDeviceCertStoreType}", (string s) => { if (s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { IotDeviceCertStoreType = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? X509Store : CertificateStoreType.Directory; IotDeviceCertStorePath = s.Equals(X509Store, StringComparison.OrdinalIgnoreCase) ? IotDeviceCertX509StorePathDefault : IotDeviceCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "dp|devicecertstorepath=", $"the path of the iot device cert store\nDefault Default (depends on store type):\n" + $"X509Store: '{IotDeviceCertX509StorePathDefault}'\n" + $"Directory: '{IotDeviceCertDirectoryStorePathDefault}'", (string s) => IotDeviceCertStorePath = s }, // misc { "i|install", $"register OPC Publisher with IoTHub and then exits.\nDefault: {_installOnly}", i => _installOnly = i != null }, { "h|help", "show this message and exit", h => shouldShowHelp = h != null }, }; List <string> extraArgs = new List <string>(); try { // parse the command line extraArgs = options.Parse(args); } catch (OptionException e) { // initialize logging InitLogging(); // show message Logger.Error(e, "Error in command line options"); Logger.Error($"Command line arguments: {String.Join(" ", args)}"); // show usage Usage(options); return; } // initialize logging InitLogging(); // show usage if requested if (shouldShowHelp) { Usage(options); return; } // Validate and parse extra arguments. const int APP_NAME_INDEX = 0; const int CS_INDEX = 1; switch (extraArgs.Count) { case 0: { ApplicationName = Utils.GetHostName(); break; } case 1: { ApplicationName = extraArgs[APP_NAME_INDEX]; break; } case 2: { ApplicationName = extraArgs[APP_NAME_INDEX]; if (IsIotEdgeModule) { WriteLine($"Warning: connection string parameter is not supported in IoTEdge context, given parameter is ignored"); } else { IotHubOwnerConnectionString = extraArgs[CS_INDEX]; } break; } default: { Logger.Error("Error in command line options"); Logger.Error($"Command line arguments: {String.Join(" ", args)}"); Usage(options); return; } } // install only if requested if (_installOnly) { // initialize and start IoTHub communication IotHubCommunication = new IotHubCommunication(ShutdownTokenSource.Token); if (!await IotHubCommunication.InitAsync()) { return; } Logger.Information("Installation completed. Exiting..."); return; } // start operation Logger.Information("Publisher is starting up..."); // allow canceling the application var quitEvent = new ManualResetEvent(false); try { Console.CancelKeyPress += (sender, eArgs) => { quitEvent.Set(); eArgs.Cancel = true; ShutdownTokenSource.Cancel(); }; } catch { } // init OPC configuration and tracing OpcStackConfiguration opcStackConfiguration = new OpcStackConfiguration(); await opcStackConfiguration.ConfigureAsync(); // log shopfloor site setting if (string.IsNullOrEmpty(PublisherSite)) { Logger.Information("There is no site configured."); } else { Logger.Information($"Publisher is in site '{PublisherSite}'."); } // Set certificate validator. if (OpcPublisherAutoTrustServerCerts) { Logger.Information("Publisher configured to auto trust server certificates of the servers it is connecting to."); PublisherOpcApplicationConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_AutoTrustServerCerts); } else { Logger.Information("Publisher configured to not auto trust server certificates. When connecting to servers, you need to manually copy the rejected server certs to the trusted store to trust them."); PublisherOpcApplicationConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_Default); } // start our server interface try { Logger.Information($"Starting server on endpoint {PublisherOpcApplicationConfiguration.ServerConfiguration.BaseAddresses[0].ToString()} ..."); _publisherServer = new PublisherServer(); _publisherServer.Start(PublisherOpcApplicationConfiguration); Logger.Information("Server started."); } catch (Exception e) { Logger.Fatal(e, "Failed to start Publisher OPC UA server."); Logger.Fatal("exiting..."); return; } // read telemetry configuration file PublisherTelemetryConfiguration.Init(ShutdownTokenSource.Token); if (!await PublisherTelemetryConfiguration.ReadConfigAsync()) { return; } // read node configuration file PublisherNodeConfiguration.Init(); if (!await PublisherNodeConfiguration.ReadConfigAsync()) { return; } // initialize hub communication if (IsIotEdgeModule) { // initialize and start EdgeHub communication IotEdgeHubCommunication = new IotEdgeHubCommunication(ShutdownTokenSource.Token); if (!await IotEdgeHubCommunication.InitAsync()) { return; } } else { // initialize and start IoTHub communication IotHubCommunication = new IotHubCommunication(ShutdownTokenSource.Token); if (!await IotHubCommunication.InitAsync()) { return; } } if (!await CreateOpcPublishingDataAsync()) { return; } // kick off OPC session creation and node monitoring await SessionStartAsync(); // Show notification on session events _publisherServer.CurrentInstance.SessionManager.SessionActivated += ServerEventStatus; _publisherServer.CurrentInstance.SessionManager.SessionClosing += ServerEventStatus; _publisherServer.CurrentInstance.SessionManager.SessionCreated += ServerEventStatus; // initialize publisher diagnostics Diagnostics.Init(); // stop on user request Logger.Information(""); Logger.Information(""); if (_noShutdown) { // wait forever if asked to do so Logger.Information("Publisher is running infinite..."); await Task.Delay(Timeout.Infinite); } else { Logger.Information("Publisher is running. Press CTRL-C to quit."); // wait for Ctrl-C quitEvent.WaitOne(Timeout.Infinite); } Logger.Information(""); Logger.Information(""); ShutdownTokenSource.Cancel(); Logger.Information("Publisher is shutting down..."); // stop the server _publisherServer.Stop(); // shutdown all OPC sessions await SessionShutdownAsync(); // shutdown the IoTHub messaging await IotHubCommunication.ShutdownAsync(); IotHubCommunication = null; // shutdown diagnostics await ShutdownAsync(); // free resources PublisherTelemetryConfiguration.Deinit(); PublisherNodeConfiguration.Deinit(); ShutdownTokenSource = null; } catch (Exception e) { Logger.Fatal(e, e.StackTrace); e = e.InnerException ?? null; while (e != null) { Logger.Fatal(e, e.StackTrace); e = e.InnerException ?? null; } Logger.Fatal("Publisher exiting... "); } }
/// <summary> /// Asynchronous part of the main method of the app. /// </summary> public async static Task MainAsync(string[] args) { var shouldShowHelp = false; // command line options Mono.Options.OptionSet options = new Mono.Options.OptionSet { // log configuration { "lf|logfile=", $"the filename of the logfile to use.\nDefault: './{_logFileName}'", (string l) => _logFileName = l }, { "lt|logflushtimespan=", $"the timespan in seconds when the logfile should be flushed.\nDefault: {_logFileFlushTimeSpanSec} sec", (int s) => { if (s > 0) { _logFileFlushTimeSpanSec = TimeSpan.FromSeconds(s); } else { throw new Mono.Options.OptionException("The logflushtimespan must be a positive number.", "logflushtimespan"); } } }, { "ll|loglevel=", $"the loglevel to use (allowed: fatal, error, warn, info, debug, verbose).\nDefault: info", (string l) => { List <string> logLevels = new List <string> { "fatal", "error", "warn", "info", "debug", "verbose" }; if (logLevels.Contains(l.ToLowerInvariant())) { _logLevel = l.ToLowerInvariant(); } else { throw new OptionException("The loglevel must be one of: fatal, error, warn, info, debug, verbose", "loglevel"); } } }, // simulation configuration { "sc|simulationcyclecount=", $"count of cycles in one simulation phase\nDefault: {SimulationCycleCount} cycles", (int i) => SimulationCycleCount = i }, { "ct|cycletime=", $"length of one cycle time in milliseconds\nDefault: {SimulationCycleLength} msec", (int i) => SimulationCycleLength = i }, { "ns|nospikes", $"do not generate spike data\nDefault: {!GenerateSpikes}", a => GenerateSpikes = a == null }, { "nd|nodips", $"do not generate dip data\nDefault: {!GenerateDips}", a => GenerateDips = a == null }, { "np|nopostrend", $"do not generate positive trend data\nDefault: {!GeneratePosTrend}", a => GeneratePosTrend = a == null }, { "nn|nonegtrend", $"do not generate negative trend data\nDefault: {!GenerateNegTrend}", a => GenerateNegTrend = a == null }, { "nv|nodatavalues", $"do not generate data values\nDefault: {!GenerateData}", a => GenerateData = a == null }, // opc configuration { "pn|portnum=", $"the server port of the OPC server endpoint.\nDefault: {ServerPort}", (ushort p) => ServerPort = p }, { "op|path=", $"the enpoint URL path part of the OPC server endpoint.\nDefault: '{ServerPath}'", (string a) => ServerPath = a }, { "ph|plchostname=", $"the fullqualified hostname of the plc.\nDefault: {Hostname}", (string a) => Hostname = a }, { "ol|opcmaxstringlen=", $"the max length of a string opc can transmit/receive.\nDefault: {OpcMaxStringLength}", (int i) => { if (i > 0) { OpcMaxStringLength = i; } else { throw new OptionException("The max opc string length must be larger than 0.", "opcmaxstringlen"); } } }, { "lr|ldsreginterval=", $"the LDS(-ME) registration interval in ms. If 0, then the registration is disabled.\nDefault: {LdsRegistrationInterval}", (int i) => { if (i >= 0) { LdsRegistrationInterval = i; } else { throw new OptionException("The ldsreginterval must be larger or equal 0.", "ldsreginterval"); } } }, { "aa|autoaccept", $"all certs are trusted when a connection is established.\nDefault: {AutoAcceptCerts}", a => AutoAcceptCerts = a != null }, { "ut|unsecuretransport", $"enables the unsecured transport.\nDefault: {EnableUnsecureTransport}", u => EnableUnsecureTransport = u != null }, { "to|trustowncert", $"the own certificate is put into the trusted certificate store automatically.\nDefault: {TrustMyself}", t => TrustMyself = t != null }, // cert store options { "at|appcertstoretype=", $"the own application cert store type. \n(allowed values: Directory, X509Store)\nDefault: '{OpcOwnCertStoreType}'", (string s) => { if (s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcOwnCertStoreType = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? CertificateStoreType.X509Store : CertificateStoreType.Directory; OpcOwnCertStorePath = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? OpcOwnCertX509StorePathDefault : OpcOwnCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ap|appcertstorepath=", $"the path where the own application cert should be stored\nDefault (depends on store type):\n" + $"X509Store: '{OpcOwnCertX509StorePathDefault}'\n" + $"Directory: '{OpcOwnCertDirectoryStorePathDefault}'", (string s) => OpcOwnCertStorePath = s }, { "tp|trustedcertstorepath=", $"the path of the trusted cert store\nDefault '{OpcTrustedCertDirectoryStorePathDefault}'", (string s) => OpcTrustedCertStorePath = s }, { "rp|rejectedcertstorepath=", $"the path of the rejected cert store\nDefault '{OpcRejectedCertDirectoryStorePathDefault}'", (string s) => OpcRejectedCertStorePath = s }, { "ip|issuercertstorepath=", $"the path of the trusted issuer cert store\nDefault '{OpcIssuerCertDirectoryStorePathDefault}'", (string s) => OpcIssuerCertStorePath = s }, { "csr", $"show data to create a certificate signing request\nDefault '{ShowCreateSigningRequestInfo}'", c => ShowCreateSigningRequestInfo = c != null }, { "ab|applicationcertbase64=", $"update/set this applications certificate with the certificate passed in as bas64 string", (string s) => { NewCertificateBase64String = s; } }, { "af|applicationcertfile=", $"update/set this applications certificate with the certificate file specified", (string s) => { if (File.Exists(s)) { NewCertificateFileName = s; } else { throw new OptionException("The file '{s}' does not exist.", "applicationcertfile"); } } }, { "pb|privatekeybase64=", $"initial provisioning of the application certificate (with a PEM or PFX fomat) requires a private key passed in as base64 string", (string s) => { PrivateKeyBase64String = s; } }, { "pk|privatekeyfile=", $"initial provisioning of the application certificate (with a PEM or PFX fomat) requires a private key passed in as file", (string s) => { if (File.Exists(s)) { PrivateKeyFileName = s; } else { throw new OptionException("The file '{s}' does not exist.", "privatekeyfile"); } } }, { "cp|certpassword="******"the optional password for the PEM or PFX or the installed application certificate", (string s) => { CertificatePassword = s; } }, { "tb|addtrustedcertbase64=", $"adds the certificate to the applications trusted cert store passed in as base64 string (multiple strings supported)", (string s) => { TrustedCertificateBase64Strings = ParseListOfStrings(s); } }, { "tf|addtrustedcertfile=", $"adds the certificate file(s) to the applications trusted cert store passed in as base64 string (multiple filenames supported)", (string s) => { TrustedCertificateFileNames = ParseListOfFileNames(s, "addtrustedcertfile"); } }, { "ib|addissuercertbase64=", $"adds the specified issuer certificate to the applications trusted issuer cert store passed in as base64 string (multiple strings supported)", (string s) => { IssuerCertificateBase64Strings = ParseListOfStrings(s); } }, { "if|addissuercertfile=", $"adds the specified issuer certificate file(s) to the applications trusted issuer cert store (multiple filenames supported)", (string s) => { IssuerCertificateFileNames = ParseListOfFileNames(s, "addissuercertfile"); } }, { "rb|updatecrlbase64=", $"update the CRL passed in as base64 string to the corresponding cert store (trusted or trusted issuer)", (string s) => { CrlBase64String = s; } }, { "uc|updatecrlfile=", $"update the CRL passed in as file to the corresponding cert store (trusted or trusted issuer)", (string s) => { if (File.Exists(s)) { CrlFileName = s; } else { throw new OptionException("The file '{s}' does not exist.", "updatecrlfile"); } } }, { "rc|removecert=", $"remove cert(s) with the given thumbprint(s) (multiple thumbprints supported)", (string s) => { ThumbprintsToRemove = ParseListOfStrings(s); } }, // misc { "h|help", "show this message and exit", h => shouldShowHelp = h != null }, }; List <string> extraArgs = new List <string>(); try { // parse the command line extraArgs = options.Parse(args); } catch (OptionException e) { // initialize logging InitLogging(); // show message Logger.Fatal(e, "Error in command line options"); Logger.Error($"Command line arguments: {String.Join(" ", args)}"); // show usage Usage(options); return; } // initialize logging InitLogging(); // show usage if requested if (shouldShowHelp) { Usage(options); return; } // validate and parse extra arguments if (extraArgs.Count > 0) { Logger.Error("Error in command line options"); Logger.Error($"Command line arguments: {String.Join(" ", args)}"); Usage(options); return; } //show version Logger.Information($"{ProgramName} V{FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion} starting up..."); Logger.Debug($"Informational version: V{(Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute).InformationalVersion}"); try { await ConsoleServerAsync(args); } catch (Exception ex) { Logger.Fatal(ex, "OPC UA server failed unexpectedly."); } Logger.Information("OPC UA server exiting..."); }
/// <summary> /// Asynchronous part of the main method of the app. /// </summary> public async static Task MainAsync(string[] args) { try { var shouldShowHelp = false; // Shutdown token sources. ShutdownTokenSource = new CancellationTokenSource(); // command line options Mono.Options.OptionSet options = new Mono.Options.OptionSet { { "cf|configfile=", $"the filename containing action configuration.\nDefault: '{OpcActionConfigurationFilename}'", (string p) => OpcActionConfigurationFilename = p }, { "tc|testconnectivity", $"URL of the OPC UA server used to test connectivity with.\nDefault: {TestConnectivity}", b => TestConnectivity = b != null }, { "tu|testurl=", $"URL of the OPC UA server used for tests.\nDefault: {TestConnectivityUrl}", (string s) => TestConnectivityUrl = s }, { "sw|sessionconnectwait=", $"specify the wait time in seconds we try to connect to disconnected endpoints and starts monitoring unmonitored items\nMin: 10\nDefault: {SessionConnectWaitSec}", (int i) => { if (i > 10) { SessionConnectWaitSec = i; } else { throw new OptionException("The sessionconnectwait must be greater than 10 sec", "sessionconnectwait"); } } }, { "di|diagnosticsinterval=", $"shows diagnostic info at the specified interval in seconds (need log level info). 0 disables diagnostic output.\nDefault: {DiagnosticsInterval}", (uint u) => DiagnosticsInterval = u }, { "lf|logfile=", $"the filename of the logfile to use.\nDefault: './{_logFileName}'", (string l) => _logFileName = l }, { "lt|logflushtimespan=", $"the timespan in seconds when the logfile should be flushed.\nDefault: {_logFileFlushTimeSpanSec} sec", (int s) => { if (s > 0) { _logFileFlushTimeSpanSec = TimeSpan.FromSeconds(s); } else { throw new Mono.Options.OptionException("The logflushtimespan must be a positive number.", "logflushtimespan"); } } }, { "ll|loglevel=", $"the loglevel to use (allowed: fatal, error, warn, info, debug, verbose).\nDefault: info", (string l) => { List <string> logLevels = new List <string> { "fatal", "error", "warn", "info", "debug", "verbose" }; if (logLevels.Contains(l.ToLowerInvariant())) { _logLevel = l.ToLowerInvariant(); } else { throw new Mono.Options.OptionException("The loglevel must be one of: fatal, error, warn, info, debug, verbose", "loglevel"); } } }, // opc configuration options { "ol|opcmaxstringlen=", $"the max length of a string opc can transmit/receive.\nDefault: {OpcMaxStringLength}", (int i) => { if (i > 0) { OpcMaxStringLength = i; } else { throw new OptionException("The max opc string length must be larger than 0.", "opcmaxstringlen"); } } }, { "ot|operationtimeout=", $"the operation timeout of the OPC UA client in ms.\nDefault: {OpcOperationTimeout}", (int i) => { if (i >= 0) { OpcOperationTimeout = i; } else { throw new OptionException("The operation timeout must be larger or equal 0.", "operationtimeout"); } } }, { "ct|createsessiontimeout=", $"specify the timeout in seconds used when creating a session to an endpoint. On unsuccessful connection attemps a backoff up to {OpcSessionCreationBackoffMax} times the specified timeout value is used.\nMin: 1\nDefault: {OpcSessionCreationTimeout}", (uint u) => { if (u > 1) { OpcSessionCreationTimeout = u; } else { throw new OptionException("The createsessiontimeout must be greater than 1 sec", "createsessiontimeout"); } } }, { "ki|keepaliveinterval=", $"specify the interval in seconds se send keep alive messages to the OPC servers on the endpoints it is connected to.\nMin: 2\nDefault: {OpcKeepAliveIntervalInSec}", (int i) => { if (i >= 2) { OpcKeepAliveIntervalInSec = i; } else { throw new OptionException("The keepaliveinterval must be greater or equal 2", "keepalivethreshold"); } } }, { "kt|keepalivethreshold=", $"specify the number of keep alive packets a server can miss, before the session is disconneced\nMin: 1\nDefault: {OpcKeepAliveDisconnectThreshold}", (uint u) => { if (u > 1) { OpcKeepAliveDisconnectThreshold = u; } else { throw new OptionException("The keepalivethreshold must be greater than 1", "keepalivethreshold"); } } }, { "aa|autoaccept", $"trusts all servers we establish a connection to.\nDefault: {OpcAutoTrustServerCerts}", b => OpcAutoTrustServerCerts = b != null }, // trust own public cert option { "to|trustowncert", $"our own certificate is put into the trusted certificate store automatically.\nDefault: {TrustMyself}", t => TrustMyself = t != null }, // own cert store options { "at|appcertstoretype=", $"the own application cert store type. \n(allowed values: Directory, X509Store)\nDefault: '{OpcOwnCertStoreType}'", (string s) => { if (s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcOwnCertStoreType = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? CertificateStoreType.X509Store : CertificateStoreType.Directory; OpcOwnCertStorePath = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? OpcOwnCertX509StorePathDefault : OpcOwnCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ap|appcertstorepath=", $"the path where the own application cert should be stored\nDefault (depends on store type):\n" + $"X509Store: '{OpcOwnCertX509StorePathDefault}'\n" + $"Directory: '{OpcOwnCertDirectoryStorePathDefault}'", (string s) => OpcOwnCertStorePath = s }, // trusted cert store options { "tt|trustedcertstoretype=", $"the trusted cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcTrustedCertStoreType}", (string s) => { if (s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcTrustedCertStoreType = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? CertificateStoreType.X509Store : CertificateStoreType.Directory; OpcTrustedCertStorePath = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? OpcTrustedCertX509StorePathDefault : OpcTrustedCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "tp|trustedcertstorepath=", $"the path of the trusted cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcTrustedCertX509StorePathDefault}'\n" + $"Directory: '{OpcTrustedCertDirectoryStorePathDefault}'", (string s) => OpcTrustedCertStorePath = s }, // rejected cert store options { "rt|rejectedcertstoretype=", $"the rejected cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcRejectedCertStoreType}", (string s) => { if (s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcRejectedCertStoreType = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? CertificateStoreType.X509Store : CertificateStoreType.Directory; OpcRejectedCertStorePath = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? OpcRejectedCertX509StorePathDefault : OpcRejectedCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "rp|rejectedcertstorepath=", $"the path of the rejected cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcRejectedCertX509StorePathDefault}'\n" + $"Directory: '{OpcRejectedCertDirectoryStorePathDefault}'", (string s) => OpcRejectedCertStorePath = s }, // issuer cert store options { "it|issuercertstoretype=", $"the trusted issuer cert store type. \n(allowed values: Directory, X509Store)\nDefault: {OpcIssuerCertStoreType}", (string s) => { if (s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) || s.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { OpcIssuerCertStoreType = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? CertificateStoreType.X509Store : CertificateStoreType.Directory; OpcIssuerCertStorePath = s.Equals(CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase) ? OpcIssuerCertX509StorePathDefault : OpcIssuerCertDirectoryStorePathDefault; } else { throw new OptionException(); } } }, { "ip|issuercertstorepath=", $"the path of the trusted issuer cert store\nDefault (depends on store type):\n" + $"X509Store: '{OpcIssuerCertX509StorePathDefault}'\n" + $"Directory: '{OpcIssuerCertDirectoryStorePathDefault}'", (string s) => OpcIssuerCertStorePath = s }, // misc { "h|help", "show this message and exit", h => shouldShowHelp = h != null }, }; List <string> extraArgs = new List <string>(); try { // parse the command line extraArgs = options.Parse(args); } catch (OptionException e) { // initialize logging InitLogging(); // show message Logger.Error(e, "Error in command line options"); Logger.Error($"Command line arguments: {String.Join(" ", args)}"); // show usage Usage(options); return; } // initialize logging InitLogging(); // show usage if requested if (shouldShowHelp) { Usage(options); return; } // Validate and parse extra arguments. if (extraArgs.Count > 1) { Logger.Error("Error in command line options"); Logger.Error($"Command line arguments: {String.Join(" ", args)}"); Usage(options); return; } // start operation Logger.Information($"{ProgramName} is starting up..."); // allow canceling the application var quitEvent = new ManualResetEvent(false); try { Console.CancelKeyPress += (sender, eArgs) => { quitEvent.Set(); eArgs.Cancel = true; ShutdownTokenSource.Cancel(); }; } catch { } // init OPC configuration and tracing OpcStackConfiguration opcStackConfiguration = new OpcStackConfiguration(); await opcStackConfiguration.ConfigureAsync(); // Set certificate validator. if (OpcAutoTrustServerCerts) { Logger.Information($"{ProgramName} configured to auto trust server certificates of the servers it is connecting to."); OpcApplicationConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_AutoTrustServerCerts); } else { Logger.Information($"{ProgramName} configured to not auto trust server certificates. When connecting to servers, you need to manually copy the rejected server certs to the trusted store to trust them."); OpcApplicationConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_Default); } // read OPC action configuration OpcConfiguration.Init(); if (!await ReadOpcConfigurationAsync()) { return; } // create OPC action data if (!await CreateOpcActionDataAsync()) { return; } // kick off OPC client activities await SessionStartAsync(); // initialize diagnostics Diagnostics.Init(); // stop on user request Logger.Information(""); Logger.Information(""); Logger.Information($"{ProgramName} is running. Press CTRL-C to quit."); // wait for Ctrl-C quitEvent.WaitOne(Timeout.Infinite); Logger.Information(""); Logger.Information(""); ShutdownTokenSource.Cancel(); Logger.Information($"{ProgramName} is shutting down..."); // shutdown all OPC sessions await SessionShutdownAsync(); // shutdown diagnostics await ShutdownAsync(); ShutdownTokenSource = null; } catch (Exception e) { Logger.Fatal(e, e.StackTrace); e = e.InnerException ?? null; while (e != null) { Logger.Fatal(e, e.StackTrace); e = e.InnerException ?? null; } Logger.Fatal($"{ProgramName} exiting... "); } }