Ejemplo n.º 1
0
        /// <summary>
        /// Create module, throws if configuration is bad
        /// </summary>
        public void Create(Broker broker, byte[] configuration)
        {
            Trace("Opc.Ua.Publisher.Module: Creating...");

            m_broker = broker;

            string configString = Encoding.UTF8.GetString(configuration);

            // Deserialize from configuration string
            ModuleConfiguration moduleConfiguration = null;

            try
            {
                moduleConfiguration = JsonConvert.DeserializeObject <ModuleConfiguration>(configString);
            }
            catch (Exception ex)
            {
                Trace("Opc.Ua.Publisher.Module: Module config string " + configString + " could not be deserialized: " + ex.Message);
                throw;
            }

            m_configuration = moduleConfiguration.Configuration;
            m_configuration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);

            // update log configuration, if available
            if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_LOGP")))
            {
                m_configuration.TraceConfiguration.OutputFilePath = Environment.GetEnvironmentVariable("_GW_LOGP");
                m_configuration.TraceConfiguration.ApplySettings();
            }

            // get a list of persisted endpoint URLs and create a session for each.
            try
            {
                // check if we have an env variable specifying the published nodes path, otherwise use current directory
                string publishedNodesFilePath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "publishednodes.json";
                if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_PNFP")))
                {
                    publishedNodesFilePath = Environment.GetEnvironmentVariable("_GW_PNFP");
                }

                Trace("Opc.Ua.Publisher.Module: Attemping to load nodes file from: " + publishedNodesFilePath);
                m_nodesLookups = JsonConvert.DeserializeObject <PublishedNodesCollection>(File.ReadAllText(publishedNodesFilePath));
                Trace("Opc.Ua.Publisher.Module: Loaded " + m_nodesLookups.Count.ToString() + " nodes.");
            }
            catch (Exception ex)
            {
                Trace("Opc.Ua.Publisher.Module: Nodes file loading failed with: " + ex.Message);
            }

            foreach (NodeLookup nodeLookup in m_nodesLookups)
            {
                if (!m_endpointUrls.Contains(nodeLookup.EndPointURL))
                {
                    m_endpointUrls.Add(nodeLookup.EndPointURL);
                }
            }

            // start the server
            try
            {
                Trace("Opc.Ua.Publisher.Module: Starting server on endpoint " + m_configuration.ServerConfiguration.BaseAddresses[0].ToString() + "...");
                m_server.Start(m_configuration);
                Trace("Opc.Ua.Publisher.Module: Server started.");
            }
            catch (Exception ex)
            {
                Trace("Opc.Ua.Publisher.Module: Starting server failed with: " + ex.Message);
            }

            // check if we have an environment variable to register ourselves with IoT Hub
            if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_HUB_CS")))
            {
                string ownerConnectionString = Environment.GetEnvironmentVariable("_HUB_CS");

                if ((m_configuration != null) && (!string.IsNullOrEmpty(m_configuration.ApplicationName)))
                {
                    Trace("Attemping to register ourselves with IoT Hub using owner connection string: " + ownerConnectionString);
                    string deviceConnectionString = IoTHubRegistration.RegisterDeviceWithIoTHub(m_configuration.ApplicationName, ownerConnectionString);
                    if (!string.IsNullOrEmpty(deviceConnectionString))
                    {
                        SecureIoTHubToken.Write(m_configuration.ApplicationName, deviceConnectionString);
                    }
                    else
                    {
                        Trace("Could not register ourselves with IoT Hub using owner connection string: " + ownerConnectionString);
                    }
                }
            }

            // try to configure our publisher component
            TryConfigurePublisherAsync().Wait();

            // connect to servers
            Trace("Opc.Ua.Publisher.Module: Attemping to connect to servers...");
            try
            {
                List <Task> connectionAttempts = new List <Task>();
                foreach (Uri endpointUrl in m_endpointUrls)
                {
                    Trace("Opc.Ua.Publisher.Module: Connecting to server: " + endpointUrl);
                    connectionAttempts.Add(EndpointConnect(endpointUrl));
                }

                // Wait for all sessions to be connected
                Task.WaitAll(connectionAttempts.ToArray());
            }
            catch (Exception ex)
            {
                Trace("Opc.Ua.Publisher.Module: Exception: " + ex.ToString() + "\r\n" + ex.InnerException != null ? ex.InnerException.ToString() : null);
            }

            Trace("Opc.Ua.Publisher.Module: Created.");
        }
Ejemplo n.º 2
0
        public static void Main(string[] args)
        {
            try
            {
                if ((args.Length == 0) || string.IsNullOrEmpty(args[0]) || args[0].Equals("localhost", StringComparison.OrdinalIgnoreCase))
                {
                    m_applicationName = Utils.GetHostName();
                }
                else
                {
                    m_applicationName = args[0];
                }

                Trace("Publisher is starting up...");
                ModuleConfiguration moduleConfiguration = new ModuleConfiguration(m_applicationName);
                m_configuration = moduleConfiguration.Configuration;
                m_configuration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);

                // start our server interface
                try
                {
                    Trace("Starting server on endpoint " + m_configuration.ServerConfiguration.BaseAddresses[0].ToString() + "...");
                    m_server.Start(m_configuration);
                    Trace("Server started.");
                }
                catch (Exception ex)
                {
                    Trace("Starting server failed with: " + ex.Message);
                }

                // check if we also received an owner connection string
                string ownerConnectionString = string.Empty;
                if ((args.Length > 1) && !string.IsNullOrEmpty(args[1]))
                {
                    ownerConnectionString = args[1];
                }
                else
                {
                    Trace("IoT Hub owner connection string not passed as argument.");

                    // check if we have an environment variable to register ourselves with IoT Hub
                    if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_HUB_CS")))
                    {
                        ownerConnectionString = Environment.GetEnvironmentVariable("_HUB_CS");
                    }
                }

                // register ourselves with IoT Hub
                if (ownerConnectionString != string.Empty)
                {
                    Trace("Attemping to register ourselves with IoT Hub using owner connection string: " + ownerConnectionString);
                    RegistryManager manager = RegistryManager.CreateFromConnectionString(ownerConnectionString);

                    // remove any existing device
                    Device existingDevice = manager.GetDeviceAsync(m_applicationName).Result;
                    if (existingDevice != null)
                    {
                        manager.RemoveDeviceAsync(m_applicationName).Wait();
                    }

                    Device newDevice = manager.AddDeviceAsync(new Device(m_applicationName)).Result;
                    if (newDevice != null)
                    {
                        string hostname = ownerConnectionString.Substring(0, ownerConnectionString.IndexOf(";"));
                        string deviceConnectionString = hostname + ";DeviceId=" + m_applicationName + ";SharedAccessKey=" + newDevice.Authentication.SymmetricKey.PrimaryKey;
                        SecureIoTHubToken.Write(m_applicationName, deviceConnectionString);
                    }
                    else
                    {
                        Trace("Could not register ourselves with IoT Hub using owner connection string: " + ownerConnectionString);
                    }
                }
                else
                {
                    Trace("IoT Hub owner connection string not found, registration with IoT Hub abandoned.");
                }

                // try to read connection string from secure store and open IoTHub client
                Trace("Attemping to read connection string from secure store with certificate name: " + m_applicationName);
                string connectionString = SecureIoTHubToken.Read(m_applicationName);
                if (!string.IsNullOrEmpty(connectionString))
                {
                    Trace("Attemping to configure publisher with connection string: " + connectionString);
                    m_deviceClient             = DeviceClient.CreateFromConnectionString(connectionString, Microsoft.Azure.Devices.Client.TransportType.Mqtt);
                    m_deviceClient.RetryPolicy = RetryPolicyType.Exponential_Backoff_With_Jitter;
                    m_deviceClient.OpenAsync().Wait();
                }
                else
                {
                    Trace("Device connection string not found in secure store.");
                }

                // get a list of persisted endpoint URLs and create a session for each.
                try
                {
                    // check if we have an env variable specifying the published nodes path, otherwise use current directory
                    string publishedNodesFilePath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "publishednodes.json";
                    if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_PNFP")))
                    {
                        publishedNodesFilePath = Environment.GetEnvironmentVariable("_GW_PNFP");
                    }

                    Trace("Attemping to load nodes file from: " + publishedNodesFilePath);
                    m_nodesLookups = JsonConvert.DeserializeObject <PublishedNodesCollection>(File.ReadAllText(publishedNodesFilePath));
                    Trace("Loaded " + m_nodesLookups.Count.ToString() + " nodes.");
                }
                catch (Exception ex)
                {
                    Trace("Nodes file loading failed with: " + ex.Message);
                }

                foreach (NodeLookup nodeLookup in m_nodesLookups)
                {
                    if (!m_endpointUrls.Contains(nodeLookup.EndPointURL))
                    {
                        m_endpointUrls.Add(nodeLookup.EndPointURL);
                    }
                }

                // connect to the other servers
                Trace("Attemping to connect to servers...");
                try
                {
                    List <Task> connectionAttempts = new List <Task>();
                    foreach (Uri endpointUrl in m_endpointUrls)
                    {
                        Trace("Connecting to server: " + endpointUrl);
                        connectionAttempts.Add(EndpointConnect(endpointUrl));
                    }

                    // Wait for all sessions to be connected
                    Task.WaitAll(connectionAttempts.ToArray());
                }
                catch (Exception ex)
                {
                    Trace("Exception: " + ex.ToString() + "\r\n" + ex.InnerException != null ? ex.InnerException.ToString() : null);
                }

                // subscribe to preconfigured nodes
                Trace("Attemping to subscribe to published nodes...");
                if (m_nodesLookups != null)
                {
                    foreach (NodeLookup nodeLookup in m_nodesLookups)
                    {
                        try
                        {
                            CreateMonitoredItem(nodeLookup);
                        }
                        catch (Exception ex)
                        {
                            Trace("Unexpected error publishing node: " + ex.Message + "\r\nIgnoring node: " + nodeLookup.EndPointURL.AbsoluteUri + ", " + nodeLookup.NodeID.ToString());
                        }
                    }
                }


                Task dequeueAndSendTask = null;
                var  tokenSource        = new CancellationTokenSource();
                var  token = tokenSource.Token;

                Trace("Creating task to send OPC UA messages in batches to IoT Hub...");
                try
                {
                    dequeueAndSendTask = Task.Run(() => DeQueueMessagesAsync(token), token);
                }
                catch (Exception ex)
                {
                    Trace("Exception: " + ex.ToString());
                }

                Trace("Publisher is running. Press enter to quit.");
                Console.ReadLine();

                foreach (Session session in m_sessions)
                {
                    session.Close();
                }

                //Send cancellation token and wait for last IoT Hub message to be sent.
                try
                {
                    tokenSource.Cancel();
                    dequeueAndSendTask.Wait();
                }
                catch (Exception ex)
                {
                    Trace("Exception: " + ex.ToString());
                }

                if (m_deviceClient != null)
                {
                    m_deviceClient.CloseAsync().Wait();
                }
            }
            catch (Exception e)
            {
                Trace(e, "Unhandled exception in Publisher, exiting!");
            }
        }
Ejemplo n.º 3
0
        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 ingested 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 could 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");
                          }
                      } },
                    { "ds|defaultsamplingrate=", $"the sampling interval in milliseconds, the OPC UA servers should use to sample the values of the nodes to publish.\n" +
                      $"Please check the OPC UA spec for more details.\nMin: 100\nDefault: {OpcSamplingRateMillisec}", (int i) => {
                          if (i >= 100)
                          {
                              OpcSamplingRateMillisec = i;
                          }
                          else
                          {
                              throw new OptionException("The sampling rate must be larger or equal 100.", "defaultsamplingrate");
                          }
                      } },
                    { "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 could 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;
                          }
                          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;
                            }
                            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;
                          }
                          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;
                            }
                            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
                ModuleConfiguration moduleConfiguration = new ModuleConfiguration(ApplicationName);
                opcTraceInitialized = true;
                OpcConfiguration    = moduleConfiguration.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 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
                {
                    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}");
                    NodesToPublish = JsonConvert.DeserializeObject <List <NodeToPublish> >(File.ReadAllText(NodesToPublishAbsFilename));
                    Trace($"Loaded {NodesToPublish.Count.ToString()} nodes to publish.");
                }
                catch (Exception e)
                {
                    Trace(e, "Loading of the node configuration file failed. Does the file exist and has correct syntax?");
                    Trace("exiting...");
                    return;
                }

                // initialize and start IoTHub messaging
                IotHubMessaging = new IotHubMessaging();
                if (!IotHubMessaging.Init(_IotHubOwnerConnectionString, _MaxSizeOfIoTHubMessageBytes, _DefaultSendIntervalSeconds))
                {
                    return;
                }

                // create a list to manage sessions and monitored items.
                var uniqueEndpointUris = NodesToPublish.Select(n => n.EndPointUri).Distinct();
                foreach (var endpointUri in uniqueEndpointUris)
                {
                    if (!OpcSessions.Any(s => s.EndpointUri.Equals(endpointUri)))
                    {
                        // create new session info.
                        OpcSession opcSession = new OpcSession(endpointUri, OpcSessionCreationTimeout);

                        // add monitored item info for all nodes to publish for this endpoint URI.
                        var nodesOnEndpointUri = NodesToPublish.Where(n => n.EndPointUri.Equals(endpointUri));
                        foreach (var nodeInfo in nodesOnEndpointUri)
                        {
                            // differentiate if legacy syntax is used
                            if (nodeInfo.NodeId == null)
                            {
                                // differentiate if there is only a single node specified or a node list with a common sampling interval
                                if (nodeInfo.ExpandedNodeId == null)
                                {
                                    // create a monitored item for each node in the list and set the sampling interval if specified
                                    foreach (var singleExpandedNodeId in nodeInfo.Nodes.ExpandedNodeIds)
                                    {
                                        MonitoredItemInfo monitoredItemInfo = new MonitoredItemInfo(singleExpandedNodeId, opcSession.EndpointUri);
                                        if (nodeInfo.Nodes.SamplingInterval != 0)
                                        {
                                            monitoredItemInfo.SamplingInterval = nodeInfo.Nodes.SamplingInterval;
                                        }
                                        opcSession.MonitoredItemsInfo.Add(monitoredItemInfo);
                                    }
                                }
                                else
                                {
                                    // create a monitored item for the node
                                    MonitoredItemInfo monitoredItemInfo = new MonitoredItemInfo(nodeInfo.ExpandedNodeId, opcSession.EndpointUri);
                                    if (nodeInfo.SamplingInterval != 0)
                                    {
                                        monitoredItemInfo.SamplingInterval = nodeInfo.SamplingInterval;
                                    }
                                    opcSession.MonitoredItemsInfo.Add(monitoredItemInfo);
                                }
                            }
                            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}'.");

                                // create a monitored item for the node with the configured or default sampling interval
                                MonitoredItemInfo monitoredItemInfo = new MonitoredItemInfo(nodeInfo.NodeId, opcSession.EndpointUri);
                                if (nodeInfo.SamplingInterval != 0)
                                {
                                    monitoredItemInfo.SamplingInterval = nodeInfo.SamplingInterval;
                                }
                                opcSession.MonitoredItemsInfo.Add(monitoredItemInfo);
                            }
                        }

                        // add the session info.
                        OpcSessions.Add(opcSession);
                    }
                }

                // kick off the task to maintain all sessions
                var cts = new CancellationTokenSource();
                Task.Run(async() => await SessionConnector(cts.Token));

                // stop on user request
                WriteLine("");
                WriteLine("");
                WriteLine("Publisher is running. Press ENTER to quit.");
                WriteLine("");
                WriteLine("");
                ReadLine();
                cts.Cancel();

                // close all connected session
                Task.Run(async() => await SessionShutdown()).Wait();

                // shutdown the IoTHub messaging
                IotHubMessaging.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...");
                }
            }
        }