Коммит
68b9437d8a
|
@ -17,7 +17,8 @@
|
||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.pdb
|
*.pdb
|
||||||
/src/GatewayApp.NetCore/Logs
|
|
||||||
*.der
|
*.der
|
||||||
*.exe
|
*.exe
|
||||||
/src/out
|
/src/out
|
||||||
|
/src/Logs
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,19 @@ namespace Opc.Ua.Publisher
|
||||||
Configuration.ClientConfiguration = new ClientConfiguration();
|
Configuration.ClientConfiguration = new ClientConfiguration();
|
||||||
Configuration.ServerConfiguration = new ServerConfiguration();
|
Configuration.ServerConfiguration = new ServerConfiguration();
|
||||||
|
|
||||||
|
// enable logging
|
||||||
|
Configuration.TraceConfiguration = new TraceConfiguration();
|
||||||
|
Configuration.TraceConfiguration.TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security | Utils.TraceMasks.StackTrace | Utils.TraceMasks.StartStop;
|
||||||
|
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_LOGP")))
|
||||||
|
{
|
||||||
|
Configuration.TraceConfiguration.OutputFilePath = Environment.GetEnvironmentVariable("_GW_LOGP");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Configuration.TraceConfiguration.OutputFilePath = "./Logs/" + Configuration.ApplicationName + ".log.txt";
|
||||||
|
}
|
||||||
|
Configuration.TraceConfiguration.ApplySettings();
|
||||||
|
|
||||||
if (Configuration.SecurityConfiguration == null)
|
if (Configuration.SecurityConfiguration == null)
|
||||||
{
|
{
|
||||||
Configuration.SecurityConfiguration = new SecurityConfiguration();
|
Configuration.SecurityConfiguration = new SecurityConfiguration();
|
||||||
|
@ -161,15 +174,15 @@ namespace Opc.Ua.Publisher
|
||||||
newPolicy.SecurityPolicyUri = SecurityPolicies.Basic128Rsa15;
|
newPolicy.SecurityPolicyUri = SecurityPolicies.Basic128Rsa15;
|
||||||
Configuration.ServerConfiguration.SecurityPolicies.Add(newPolicy);
|
Configuration.ServerConfiguration.SecurityPolicies.Add(newPolicy);
|
||||||
|
|
||||||
// enable logging
|
|
||||||
Configuration.TraceConfiguration = new TraceConfiguration();
|
|
||||||
Configuration.TraceConfiguration.TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security | Utils.TraceMasks.StackTrace | Utils.TraceMasks.StartStop;
|
|
||||||
Configuration.TraceConfiguration.OutputFilePath = "./Logs/" + Configuration.ApplicationName + ".log.txt";
|
|
||||||
Configuration.TraceConfiguration.ApplySettings();
|
|
||||||
|
|
||||||
// the OperationTimeout should be twice the minimum value for PublishingInterval * KeepAliveCount, so set to 120s
|
// the OperationTimeout should be twice the minimum value for PublishingInterval * KeepAliveCount, so set to 120s
|
||||||
Configuration.TransportQuotas.OperationTimeout = 120000;
|
Configuration.TransportQuotas.OperationTimeout = 120000;
|
||||||
|
|
||||||
|
// allow SHA1 certificates for now as many OPC Servers still use them
|
||||||
|
Configuration.SecurityConfiguration.RejectSHA1SignedCertificates = false;
|
||||||
|
|
||||||
|
// allow 1024 minimum key size as many OPC Servers still use them
|
||||||
|
Configuration.SecurityConfiguration.MinimumCertificateKeySize = 1024;
|
||||||
|
|
||||||
// validate the configuration now
|
// validate the configuration now
|
||||||
Configuration.Validate(Configuration.ApplicationType).Wait();
|
Configuration.Validate(Configuration.ApplicationType).Wait();
|
||||||
}
|
}
|
||||||
|
|
299
src/Program.cs
299
src/Program.cs
|
@ -29,192 +29,195 @@ namespace Opc.Ua.Publisher
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Trace(string message, params object[] args)
|
public static void Trace(string message, params object[] args)
|
||||||
{
|
{
|
||||||
Utils.Trace(message, args);
|
Utils.Trace(Utils.TraceMasks.Error, message, args);
|
||||||
Console.WriteLine(message, args);
|
Console.WriteLine(DateTime.Now.ToString() + ": " + message, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Trace(int traceMask, string format, params object[] args)
|
public static void Trace(int traceMask, string format, params object[] args)
|
||||||
{
|
{
|
||||||
Utils.Trace(traceMask, format, args);
|
Utils.Trace(traceMask, format, args);
|
||||||
Console.WriteLine(format, args);
|
Console.WriteLine(DateTime.Now.ToString() + ": " + format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Trace(Exception e, string format, params object[] args)
|
public static void Trace(Exception e, string format, params object[] args)
|
||||||
{
|
{
|
||||||
Utils.Trace(e, format, args);
|
Utils.Trace(e, format, args);
|
||||||
Console.WriteLine(e.ToString());
|
Console.WriteLine(DateTime.Now.ToString() + ": " + e.Message.ToString());
|
||||||
Console.WriteLine(format, args);
|
Console.WriteLine(DateTime.Now.ToString() + ": " + format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
if ((args.Length == 0) || string.IsNullOrEmpty(args[0]))
|
try
|
||||||
{
|
{
|
||||||
Console.WriteLine("Please specify an application name as argument!");
|
if ((args.Length == 0) || string.IsNullOrEmpty(args[0]))
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_applicationName = args[0];
|
|
||||||
string ownerConnectionString = string.Empty;
|
|
||||||
|
|
||||||
// check if we also received an owner connection string
|
|
||||||
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");
|
Trace("Please specify an application name as argument!");
|
||||||
}
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
else
|
||||||
{
|
{
|
||||||
Trace("Could not register ourselves with IoT Hub using owner connection string: " + ownerConnectionString);
|
Trace("Publisher is starting up...");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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
|
m_applicationName = args[0];
|
||||||
Trace("Attemping to read connection string from secure store with certificate name: " + m_applicationName);
|
ModuleConfiguration moduleConfiguration = new ModuleConfiguration(m_applicationName);
|
||||||
string connectionString = SecureIoTHubToken.Read(m_applicationName);
|
m_configuration = moduleConfiguration.Configuration;
|
||||||
if (!string.IsNullOrEmpty(connectionString))
|
m_configuration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
|
||||||
{
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleConfiguration moduleConfiguration = new ModuleConfiguration(m_applicationName);
|
|
||||||
m_configuration = moduleConfiguration.Configuration;
|
|
||||||
m_configuration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
|
|
||||||
|
|
||||||
// update log configuration, if available
|
// start our server interface
|
||||||
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_LOGP")))
|
try
|
||||||
{
|
|
||||||
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("Starting server on endpoint " + m_configuration.ServerConfiguration.BaseAddresses[0].ToString() + "...");
|
||||||
|
m_server.Start(m_configuration);
|
||||||
|
Trace("Server started.");
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
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);
|
Trace("Starting server failed with: " + ex.Message);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// start the server
|
// check if we also received an owner connection string
|
||||||
try
|
string ownerConnectionString = string.Empty;
|
||||||
{
|
if ((args.Length > 1) && !string.IsNullOrEmpty(args[1]))
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect to 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);
|
ownerConnectionString = args[1];
|
||||||
connectionAttempts.Add(EndpointConnect(endpointUrl));
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all sessions to be connected
|
// register ourselves with IoT Hub
|
||||||
Task.WaitAll(connectionAttempts.ToArray());
|
if (ownerConnectionString != string.Empty)
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
Trace("Attemping to register ourselves with IoT Hub using owner connection string: " + ownerConnectionString);
|
||||||
{
|
RegistryManager manager = RegistryManager.CreateFromConnectionString(ownerConnectionString);
|
||||||
Trace("Exception: " + ex.ToString() + "\r\n" + ex.InnerException != null ? ex.InnerException.ToString() : null);
|
|
||||||
}
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
// subscribe to preconfigured nodes
|
|
||||||
Trace("Attemping to subscribe to published nodes...");
|
|
||||||
if (m_nodesLookups != null)
|
|
||||||
{
|
|
||||||
foreach (NodeLookup nodeLookup in m_nodesLookups)
|
foreach (NodeLookup nodeLookup in m_nodesLookups)
|
||||||
{
|
{
|
||||||
try
|
if (!m_endpointUrls.Contains(nodeLookup.EndPointURL))
|
||||||
{
|
{
|
||||||
CreateMonitoredItem(nodeLookup);
|
m_endpointUrls.Add(nodeLookup.EndPointURL);
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace("Unexpected error publishing node: " + ex.Message + "\r\nIgnoring node: " + nodeLookup.EndPointURL.AbsoluteUri + ", " + nodeLookup.NodeID.ToString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Trace("Publisher is running. Press enter to quit.");
|
||||||
|
Console.ReadLine();
|
||||||
|
|
||||||
|
foreach (Session session in m_sessions)
|
||||||
|
{
|
||||||
|
session.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_deviceClient != null)
|
||||||
|
{
|
||||||
|
m_deviceClient.CloseAsync().Wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
Console.WriteLine("Publisher is running. Press enter to quit.");
|
|
||||||
Console.ReadLine();
|
|
||||||
|
|
||||||
foreach (Session session in m_sessions)
|
|
||||||
{
|
{
|
||||||
session.Close();
|
Trace(e, "Unhandled exception in Publisher, exiting!");
|
||||||
}
|
|
||||||
|
|
||||||
if (m_deviceClient != null)
|
|
||||||
{
|
|
||||||
m_deviceClient.CloseAsync().Wait();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче