Fix publishing and unpublishing OPC UA server methods, protect and

persist publishednodes.json, smaller fixes.
This commit is contained in:
Hans Gschossmann 2017-09-18 22:18:37 +02:00
Родитель 26c301250a
Коммит 253a76f539
4 изменённых файлов: 185 добавлений и 161 удалений

Просмотреть файл

@ -128,6 +128,13 @@ namespace Opc.Ua.Publisher
null
);
Configuration.SecurityConfiguration.ApplicationCertificate.Certificate = certificate ?? throw new Exception("OPC UA application certificate could not be created! Cannot continue without it!");
}
else
{
Trace("Application certificate found in Application Certificate Store");
}
Configuration.ApplicationUri = Utils.GetApplicationUriFromCertificate(certificate);
Trace($"Application certificate is for Application URI: {Configuration.ApplicationUri}");
// Trust myself if requested.
if (Program.TrustMyself)
@ -163,18 +170,11 @@ namespace Opc.Ua.Publisher
{
Trace("Publisher certificate is not added to trusted peer store.");
}
}
else
{
Trace("Application certificate found in Application Certificate Store");
}
Configuration.ApplicationUri = Utils.GetApplicationUriFromCertificate(certificate);
Trace($"Application certificate is for Application URI: {Configuration.ApplicationUri}");
// patch our base address
if (Configuration.ServerConfiguration.BaseAddresses.Count == 0)
{
Configuration.ServerConfiguration.BaseAddresses.Add($"opc.tcp://{Configuration.ApplicationName.ToLowerInvariant()}:{Program.PublisherServerPort}{Program.PublisherServerPath}");
Configuration.ServerConfiguration.BaseAddresses.Add($"opc.tcp://{Utils.GetHostName().ToLowerInvariant()}:{Program.PublisherServerPort}{Program.PublisherServerPath}");
}
foreach (var endpoint in Configuration.ServerConfiguration.BaseAddresses)
{

Просмотреть файл

@ -207,8 +207,7 @@ namespace Opc.Ua.Publisher
{
Trace($"Connect and monitor session and nodes on endpoint '{EndpointUri.AbsoluteUri}'.");
EndpointDescription selectedEndpoint = CoreClientUtils.SelectEndpoint(EndpointUri.AbsoluteUri, true);
ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(selectedEndpoint.Server, EndpointConfiguration.Create(OpcConfiguration));
configuredEndpoint.Update(selectedEndpoint);
ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(OpcConfiguration));
try
{
@ -388,15 +387,21 @@ namespace Opc.Ua.Publisher
}
// shutdown unused sessions.
try
{
OpcSessionsSemaphore.Wait();
var unusedSessions = OpcSessions.Where(s => s.OpcSubscriptions.Count == 0);
foreach (var unusedSession in unusedSessions)
{
await unusedSession.Shutdown();
OpcSessions.Remove(unusedSession);
await unusedSession.Shutdown();
}
}
finally
{
OpcSessionsSemaphore.Release();
}
}
catch (Exception e)
{
Trace(e, "Error during ConnectAndMonitor.");
@ -468,49 +473,37 @@ namespace Opc.Ua.Publisher
/// If there is no spubscription with the requested publishing interval, one is created.
/// </summary>
/// <param name="publishingInterval"></param>
/// <param name="samplingInterval"></param>
/// <param name="nodeId"></param>
public void AddNodeForMonitoring(int publishingInterval, NodeId nodeId)
public void AddNodeForMonitoring(int publishingInterval, int samplingInterval, NodeId nodeId)
{
_opcSessionSemaphore.Wait();
OpcSubscription opcSubscription = null;
try
{
// find a subscription we could the node monitor on
try
{
opcSubscription = OpcSubscriptions.FirstOrDefault(s => s.RequestedPublishingInterval == publishingInterval);
}
catch
{
opcSubscription = null;
}
OpcSubscription opcSubscription = OpcSubscriptions.FirstOrDefault(s => s.RequestedPublishingInterval == publishingInterval);
// if there was none found, create one
if (opcSubscription == null)
{
int revisedPublishingInterval;
opcSubscription = new OpcSubscription(publishingInterval)
{
Subscription = CreateSubscription(publishingInterval, out revisedPublishingInterval),
PublishingInterval = revisedPublishingInterval
};
opcSubscription = new OpcSubscription(publishingInterval);
OpcSubscriptions.Add(opcSubscription);
Trace($"AddNodeForMonitoring: No matching subscription with publishing interval of {publishingInterval} found'. Requested to create a new one.");
}
// if it is already there, we just ignore it, otherwise we add a new item to monitor.
OpcMonitoredItem opcMonitoredItem = null;
try
{
opcMonitoredItem = opcSubscription.OpcMonitoredItems.FirstOrDefault(m => m.StartNodeId == nodeId);
}
catch
{
opcMonitoredItem = null;
}
OpcMonitoredItem opcMonitoredItem = opcSubscription.OpcMonitoredItems.FirstOrDefault(m => m.StartNodeId == nodeId);
// if there was none found, create one
if (opcMonitoredItem == null)
{
// add a new item to monitor
opcMonitoredItem = new OpcMonitoredItem(nodeId, EndpointUri);
opcMonitoredItem = new OpcMonitoredItem(nodeId, EndpointUri)
{
RequestedSamplingInterval = samplingInterval
};
opcSubscription.OpcMonitoredItems.Add(opcMonitoredItem);
Trace($"AddNodeForMonitoring: Added item with nodeId '{nodeId.ToString()}' for monitoring.");
}
}
finally
@ -585,12 +578,9 @@ namespace Opc.Ua.Publisher
finally
{
_opcSessionSemaphore.Release();
if (OpcSessions.Count(s => s.State == SessionState.Connected) == 0)
{
_opcSessionSemaphore.Dispose();
}
}
}
/// <summary>
/// Create a subscription in the session.
@ -630,10 +620,6 @@ namespace Opc.Ua.Publisher
var opcSessions = OpcSessions.Where(s => s.Session != null);
opcSession = opcSessions.Where(s => s.Session.ConfiguredEndpoint.EndpointUrl.Equals(session.ConfiguredEndpoint.EndpointUrl)).FirstOrDefault();
}
catch
{
opcSession = null;
}
finally
{
OpcSessionsSemaphore.Release();

Просмотреть файл

@ -28,6 +28,7 @@ namespace Opc.Ua.Publisher
public static List<PublishNodeConfig> PublishConfig = new List<PublishNodeConfig>();
public static string NodesToPublishAbsFilenameDefault = $"{System.IO.Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}publishednodes.json";
public static string NodesToPublishAbsFilename { get; set; }
public static SemaphoreSlim PublishDataSemaphore = new SemaphoreSlim(1);
public static string ShopfloorDomain { get; set; }
public static bool VerboseConsole { get; set; }
@ -68,7 +69,7 @@ namespace Opc.Ua.Publisher
public static string PublisherServerSecurityPolicy = SecurityPolicies.Basic128Rsa15;
public static string OpcOwnCertStoreType = X509Store;
public static string OpcOwnCertStoreType = Directory;
private const string _opcOwnCertDirectoryStorePathDefault = "CertificateStores/own";
private const string _opcOwnCertX509StorePathDefault = "CurrentUser\\UA_MachineDefault";
public static string OpcOwnCertStorePath = _opcOwnCertX509StorePathDefault;
@ -464,6 +465,7 @@ namespace Opc.Ua.Publisher
// 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
@ -502,6 +504,10 @@ namespace Opc.Ua.Publisher
Trace("exiting...");
return;
}
finally
{
PublishDataSemaphore.Release();
}
Trace($"There are {PublishConfig.Count.ToString()} nodes to publish.");
// initialize and start IoTHub messaging
@ -512,6 +518,9 @@ namespace Opc.Ua.Publisher
}
// 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)
@ -563,7 +572,12 @@ namespace Opc.Ua.Publisher
// add session.
OpcSessions.Add(opcSession);
}
}
finally
{
OpcSessionsSemaphore.Release();
PublishDataSemaphore.Release();
}
// kick off the task to maintain all sessions
var cts = new CancellationTokenSource();
@ -644,6 +658,9 @@ namespace Opc.Ua.Publisher
try
{
// get tasks for all disconnected sessions and start them
try
{
OpcSessionsSemaphore.Wait();
var shutdownSessionTaskList = OpcSessions.Select(s => s.Shutdown());
if (shutdownSessionTaskList.GetEnumerator().MoveNext())
{
@ -651,6 +668,11 @@ namespace Opc.Ua.Publisher
await Task.WhenAll(shutdownSessionTaskList);
}
}
finally
{
OpcSessionsSemaphore.Release();
}
}
catch (Exception e)
{
Trace(e, $"Failed to shutdown sessions. Inner Exception message: { e.InnerException?.ToString()}");

Просмотреть файл

@ -45,13 +45,13 @@ namespace Publisher
Uri endpointUri = null;
try
{
nodeId = inputArguments[0] as string;
endpointUri = inputArguments[1] as Uri;
if (string.IsNullOrEmpty(inputArguments[0] as string) || string.IsNullOrEmpty(inputArguments[1] as string))
{
Trace($"PublishNodeMethod: Arguments (0 (nodeId), 1 (endpointUrl)) are not valid strings!");
return ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide all arguments as strings!");
}
nodeId = inputArguments[0] as string;
endpointUri = new Uri(inputArguments[1] as string);
publishNodeConfig = new PublishNodeConfig(nodeId, endpointUri, OpcSamplingInterval, OpcPublishingInterval);
}
catch (UriFormatException)
@ -60,6 +60,15 @@ namespace Publisher
return ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide a valid OPC UA endpoint URL as second argument!");
}
// Process publishing and sampling intervals if passed in.
int publishingInterval = OpcPublishingInterval;
int samplingInterval = OpcSamplingInterval;
if (inputArguments.Count > 2)
{
int.TryParse(inputArguments[2] as string, out publishingInterval);
int.TryParse(inputArguments[3] as string, out samplingInterval);
}
// find/create a session to the endpoint URL and start monitoring the node.
try
{
@ -68,14 +77,7 @@ namespace Publisher
try
{
OpcSessionsSemaphore.Wait();
try
{
opcSession = OpcSessions.FirstOrDefault(s => s.EndpointUri == publishNodeConfig.EndpointUri);
}
catch
{
opcSession = null;
}
// add a new session.
if (opcSession == null)
@ -83,28 +85,26 @@ namespace Publisher
// create new session info.
opcSession = new OpcSession(publishNodeConfig.EndpointUri, OpcSessionCreationTimeout);
OpcSessions.Add(opcSession);
Trace($"PublishNodeMethod: No matching session found for endpoint '{publishNodeConfig.EndpointUri.AbsolutePath}'. Requested to create a new one.");
Trace($"PublishNodeMethod: No matching session found for endpoint '{publishNodeConfig.EndpointUri.OriginalString}'. Requested to create a new one.");
}
else
{
Trace($"PublishNodeMethod: Session found for endpoint '{publishNodeConfig.EndpointUri.AbsolutePath}'");
Trace($"PublishNodeMethod: Session found for endpoint '{publishNodeConfig.EndpointUri.OriginalString}'");
}
// add the node info to the subscription with the default publishing interval
opcSession.AddNodeForMonitoring(publishingInterval, samplingInterval, publishNodeConfig.NodeId);
Trace("PublishNodeMethod: Requested to monitor item.");
}
finally
{
OpcSessionsSemaphore.Release();
}
// add the node info to the subscription with the default publishing interval
opcSession.AddNodeForMonitoring(OpcPublishingInterval, publishNodeConfig.NodeId);
Trace("PublishNodeMethod: Requested to monitor item.");
// start monitoring the node
Task monitorTask = Task.Run(async () => await opcSession.ConnectAndMonitor());
monitorTask.Wait();
Trace("PublishNodeMethod: Session processing completed.");
// update our data
try
{
PublishDataSemaphore.Wait();
PublishConfig.Add(publishNodeConfig);
// add it also to the publish file
@ -115,6 +115,11 @@ namespace Publisher
};
PublishConfigFileEntries.Add(publishConfigFileEntry);
File.WriteAllText(NodesToPublishAbsFilename, JsonConvert.SerializeObject(PublishConfigFileEntries));
}
finally
{
PublishDataSemaphore.Release();
}
Trace($"PublishNodeMethod: Now publishing: {publishNodeConfig.NodeId.ToString()}");
return ServiceResult.Good;
@ -141,13 +146,13 @@ namespace Publisher
Uri endpointUri = null;
try
{
nodeId = inputArguments[0] as string;
endpointUri = inputArguments[1] as Uri;
if (string.IsNullOrEmpty(inputArguments[0] as string) || string.IsNullOrEmpty(inputArguments[1] as string))
{
Trace($"UnPublishNodeMethod: Arguments (0 (nodeId), 1 (endpointUrl)) are not valid strings!");
return ServiceResult.Create(StatusCodes.BadArgumentsMissing, "Please provide all arguments as strings!");
}
nodeId = inputArguments[0] as string;
endpointUri = new Uri(inputArguments[1] as string);
}
catch (UriFormatException)
{
@ -177,28 +182,31 @@ namespace Publisher
if (opcSession == null)
{
// do nothing if there is no session for this endpoint.
Trace($"UnPublishNodeMethod: Session for endpoint '{endpointUri.AbsolutePath}' not found.");
Trace($"UnPublishNodeMethod: Session for endpoint '{endpointUri.OriginalString}' not found.");
return ServiceResult.Create(StatusCodes.BadSessionIdInvalid, "Session for endpoint of published node not found!");
}
else
{
Trace($"UnPublishNodeMethod: Session found for endpoint '{endpointUri.AbsolutePath}'");
Trace($"UnPublishNodeMethod: Session found for endpoint '{endpointUri.OriginalString}'");
}
// remove the node from the sessions monitored items list.
opcSession.TagNodeForMonitoringStop(nodeId);
Trace("UnPublishNodeMethod: Requested to stop monitoring of node.");
// stop monitoring the node
Task monitorTask = Task.Run(async () => await opcSession.ConnectAndMonitor());
monitorTask.Wait();
Trace("UnPublishNodeMethod: Session processing completed.");
// remove node from persisted config file
try
{
PublishDataSemaphore.Wait();
var entryToRemove = PublishConfigFileEntries.Find(l => l.NodeId == nodeId && l.EndpointUri == endpointUri);
PublishConfigFileEntries.Remove(entryToRemove);
File.WriteAllText(NodesToPublishAbsFilename, JsonConvert.SerializeObject(PublishConfigFileEntries));
}
finally
{
PublishDataSemaphore.Release();
}
}
catch (Exception e)
{
Trace(e, $"DoPublish: Exception while trying to configure publishing node '{nodeId.ToString()}'");
@ -212,7 +220,15 @@ namespace Publisher
/// </summary>
private ServiceResult GetListOfPublishedNodesMethod(ISystemContext context, MethodState method, IList<object> inputArguments, IList<object> outputArguments)
{
try
{
PublishDataSemaphore.Wait();
outputArguments[0] = JsonConvert.SerializeObject(PublishConfigFileEntries);
}
finally
{
PublishDataSemaphore.Release();
}
Trace("GetListOfPublishedNodesMethod: Success!");
return ServiceResult.Good;