From acc6df9e73e04b05c360661c03731c86e270443c Mon Sep 17 00:00:00 2001 From: barnstee Date: Thu, 29 Aug 2024 23:55:11 +0100 Subject: [PATCH] Removed duplicate session cache. --- Controllers/BrowserController.cs | 75 ++------------ Interfaces/IUAClient.cs | 11 +- Models/SessionModel.cs | 2 - OpcSessionHelper.cs | 168 ------------------------------- Pages/UABrowser.razor | 68 ++++--------- Startup.cs | 1 - UAClient.cs | 82 +++++++++++++-- Views/Browser/Browse.cshtml | 1 - 8 files changed, 115 insertions(+), 293 deletions(-) delete mode 100644 OpcSessionHelper.cs diff --git a/Controllers/BrowserController.cs b/Controllers/BrowserController.cs index 7dda183..d1d5f0f 100644 --- a/Controllers/BrowserController.cs +++ b/Controllers/BrowserController.cs @@ -5,7 +5,6 @@ namespace Opc.Ua.Cloud.Publisher.Controllers using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Newtonsoft.Json; - using Opc.Ua.Cloud.Publisher; using Opc.Ua.Cloud.Publisher.Interfaces; using Opc.Ua.Cloud.Publisher.Models; using System; @@ -18,16 +17,14 @@ public class BrowserController : Controller { private readonly IUAApplication _app; private readonly IUAClient _client; - private readonly OpcSessionHelper _helper; private readonly ILogger _logger; private SessionModel _session; - public BrowserController(OpcSessionHelper helper, IUAApplication app, IUAClient client, ILoggerFactory loggerFactory) + public BrowserController(IUAApplication app, IUAClient client, ILoggerFactory loggerFactory) { _app = app; _client = client; - _helper = helper; _logger = loggerFactory.CreateLogger("BrowserController"); _session = new(); } @@ -35,7 +32,6 @@ public BrowserController(OpcSessionHelper helper, IUAApplication app, IUAClient [HttpPost] public IActionResult DownloadUACert() { - _session.SessionId = HttpContext.Session.Id; _session.EndpointUrl = HttpContext.Session.GetString("EndpointUrl"); try @@ -52,13 +48,10 @@ public IActionResult DownloadUACert() [HttpGet] public ActionResult Index() { - _session.SessionId = HttpContext.Session.Id; _session.EndpointUrl = HttpContext.Session.GetString("EndpointUrl"); - OpcSessionCacheData entry = null; - if (_helper.OpcSessionCache.TryGetValue(_session.SessionId, out entry)) + if (!string.IsNullOrEmpty(_session.EndpointUrl)) { - _session.EndpointUrl = entry.EndpointURL; return View("Browse", _session); } @@ -68,7 +61,6 @@ public ActionResult Index() [HttpPost] public ActionResult UserPassword(string endpointUrl) { - _session.SessionId = HttpContext.Session.Id; _session.EndpointUrl = endpointUrl; HttpContext.Session.SetString("EndpointUrl", endpointUrl); @@ -77,70 +69,36 @@ public ActionResult UserPassword(string endpointUrl) } [HttpPost] - public async Task ConnectAsync(string username, string password) + public ActionResult ConnectAsync(string username, string password) { - _session.SessionId = HttpContext.Session.Id; _session.EndpointUrl = HttpContext.Session.GetString("EndpointUrl"); - _session.UserName = username; _session.Password = password; - - Client.Session session = null; - try - { - session = await _helper.GetSessionAsync(_session.SessionId, _session.EndpointUrl, _session.UserName, _session.Password).ConfigureAwait(false); - } - catch (Exception ex) - { - _session.StatusMessage = ex.Message; - return View("Index", _session); - } - - if (session == null) - { - _session.StatusMessage = "Unable to create session!"; - return View("Index", _session); - } - else - { - _session.StatusMessage = "Connected to: " + _session.EndpointUrl; - return View("Browse", _session); - } + _session.StatusMessage = "Connected to: " + _session.EndpointUrl; + + return View("Browse", _session); } [HttpPost] public ActionResult Disconnect() { - _session.SessionId = HttpContext.Session.Id; _session.EndpointUrl = HttpContext.Session.GetString("EndpointUrl"); - try - { - _helper.Disconnect(_session.SessionId); - } - catch (Exception) - { - // do nothing - } - return View("Index", _session); } [HttpPost] public async Task GeneratePN() { - _session.SessionId = HttpContext.Session.Id; _session.EndpointUrl = HttpContext.Session.GetString("EndpointUrl"); try { - Client.Session session = await _helper.GetSessionAsync(_session.SessionId, _session.EndpointUrl, _session.UserName, _session.Password).ConfigureAwait(false); - - List results = await _client.BrowseVariableNodesResursivelyAsync(session, null).ConfigureAwait(false); + List results = await _client.BrowseVariableNodesResursivelyAsync(_session.EndpointUrl, _session.UserName, _session.Password, null).ConfigureAwait(false); PublishNodesInterfaceModel model = new() { - EndpointUrl = session.Endpoint.EndpointUrl, + EndpointUrl = _session.EndpointUrl, OpcNodes = new List() }; @@ -175,14 +133,11 @@ public async Task GeneratePN() [HttpPost] public async Task GenerateCSV() { - _session.SessionId = HttpContext.Session.Id; _session.EndpointUrl = HttpContext.Session.GetString("EndpointUrl"); try { - Client.Session session = await _helper.GetSessionAsync(_session.SessionId, _session.EndpointUrl, _session.UserName, _session.Password).ConfigureAwait(false); - - List results = await _client.BrowseVariableNodesResursivelyAsync(session, null).ConfigureAwait(false); + List results = await _client.BrowseVariableNodesResursivelyAsync(_session.EndpointUrl, _session.UserName, _session.Password, null).ConfigureAwait(false); string content = "Endpoint,ApplicationUri,ExpandedNodeId,DisplayName,Type,VariableCurrentValue,VariableType,Parent,References\r\n"; foreach (UANodeInformation nodeInfo in results) @@ -221,20 +176,12 @@ public async Task GenerateCSV() [HttpPost] public async Task PushCert() { - _session.SessionId = HttpContext.Session.Id; _session.EndpointUrl = HttpContext.Session.GetString("EndpointUrl"); try { - OpcSessionCacheData entry; - if (_helper.OpcSessionCache.TryGetValue(_session.SessionId, out entry)) - { - if (entry.OPCSession != null) - { - await _client.GDSServerPush(_session.EndpointUrl, entry.Username, entry.Password).ConfigureAwait(false); - } - } - + await _client.GDSServerPush(_session.EndpointUrl, _session.UserName, _session.Password).ConfigureAwait(false); + _session.StatusMessage = "New certificate and trust list pushed successfully to server!"; } catch (Exception ex) diff --git a/Interfaces/IUAClient.cs b/Interfaces/IUAClient.cs index 046b2ed..ce50d96 100644 --- a/Interfaces/IUAClient.cs +++ b/Interfaces/IUAClient.cs @@ -1,7 +1,6 @@  namespace Opc.Ua.Cloud.Publisher.Interfaces { - using Opc.Ua.Client; using Opc.Ua.Cloud.Publisher.Models; using System; using System.Collections.Generic; @@ -10,6 +9,12 @@ namespace Opc.Ua.Cloud.Publisher.Interfaces public interface IUAClient : IDisposable { + Task Browse(string endpointUrl, string username, string password, BrowseDescription nodeToBrowse, bool throwOnError); + + Task> BrowseVariableNodesResursivelyAsync(string endpointUrl, string username, string password, NodeId nodeId); + + string ReadNode(string endpointUrl, string username, string password, ref string nodeId); + Task PublishNodeAsync(NodePublishingModel nodeToPublish, CancellationToken cancellationToken = default); void UnpublishNode(NodePublishingModel nodeToUnpublish); @@ -19,9 +24,7 @@ public interface IUAClient : IDisposable IEnumerable GetPublishedNodes(); Task GDSServerPush(string endpointURL, string adminUsername, string adminPassword); - - Task> BrowseVariableNodesResursivelyAsync(Session session, NodeId nodeId); - + Task WoTConUpload(string endpoint, string username, string password, byte[] bytes, string assetName); } } \ No newline at end of file diff --git a/Models/SessionModel.cs b/Models/SessionModel.cs index ecf9120..f58ba19 100644 --- a/Models/SessionModel.cs +++ b/Models/SessionModel.cs @@ -3,8 +3,6 @@ namespace Opc.Ua.Cloud.Publisher.Models { public class SessionModel { - public string SessionId { get; set; } - public string EndpointUrl { get; set; } public string UserName { get; set; } diff --git a/OpcSessionHelper.cs b/OpcSessionHelper.cs deleted file mode 100644 index fd556a9..0000000 --- a/OpcSessionHelper.cs +++ /dev/null @@ -1,168 +0,0 @@ - -namespace Opc.Ua.Cloud.Publisher -{ - using Opc.Ua; - using Opc.Ua.Client; - using Opc.Ua.Cloud.Publisher.Interfaces; - using System; - using System.Collections.Concurrent; - using System.Threading; - using System.Threading.Tasks; - - public class OpcSessionCacheData - { - public Session OPCSession { get; set; } - - public string EndpointURL { get; set; } - - public string Username { get; set; } - - public string Password { get; set; } - - public OpcSessionCacheData() - { - OPCSession = null; - EndpointURL = string.Empty; - Username = string.Empty; - Password = string.Empty; - } - } - - public class OpcSessionHelper - { - public ConcurrentDictionary OpcSessionCache = new ConcurrentDictionary(); - - private readonly ApplicationConfiguration _configuration = null; - - private readonly IUAApplication _app; - - public OpcSessionHelper(IUAApplication app) - { - _app = app; - _configuration = app.UAApplicationInstance.ApplicationConfiguration; - } - - public void Disconnect(string sessionID) - { - OpcSessionCacheData entry; - if (OpcSessionCache.TryRemove(sessionID, out entry)) - { - try - { - if (entry.OPCSession != null) - { - entry.OPCSession.Close(); - } - } - catch - { - // do nothing - } - } - } - - public async Task GetSessionAsync(string sessionID, string endpointURL, string username = null, string password = null) - { - if (string.IsNullOrEmpty(sessionID) || string.IsNullOrEmpty(endpointURL)) - { - return null; - } - - OpcSessionCacheData entry = null; - if (OpcSessionCache.TryGetValue(sessionID, out entry)) - { - if ((entry != null) && (entry.OPCSession != null)) - { - if (entry.OPCSession.Connected) - { - return entry.OPCSession; - } - - try - { - entry.OPCSession.Close(500); - } - catch (Exception) - { - // do nothing - } - - entry.OPCSession = null; - } - } - else - { - // create a new entry - OpcSessionCacheData newEntry = new OpcSessionCacheData { EndpointURL = endpointURL }; - OpcSessionCache.TryAdd(sessionID, newEntry); - } - - EndpointDescription selectedEndpoint = null; - ITransportWaitingConnection connection = null; - if (Settings.Instance.UseReverseConnect) - { - connection = await _app.ReverseConnectManager.WaitForConnection(new Uri(endpointURL), null, new CancellationTokenSource(30_000).Token).ConfigureAwait(false); - if (connection == null) - { - throw new ServiceResultException(StatusCodes.BadTimeout, "Waiting for a reverse connection timed out after 30 seconds."); - } - - selectedEndpoint = CoreClientUtils.SelectEndpoint(_configuration, connection, true); - } - else - { - selectedEndpoint = CoreClientUtils.SelectEndpoint(endpointURL, true); - } - - ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(_configuration)); - - - uint timeout = (uint)_configuration.ClientConfiguration.DefaultSessionTimeout; - - UserIdentity userIdentity = null; - if (username == null) - { - userIdentity = new UserIdentity(new AnonymousIdentityToken()); - } - else - { - userIdentity = new UserIdentity(username, password); - } - - Session session = await Session.Create( - _configuration, - configuredEndpoint, - true, - false, - _configuration.ApplicationName, - (uint)_configuration.ClientConfiguration.DefaultSessionTimeout, - userIdentity, - null - ).ConfigureAwait(false); - - if (session != null) - { - // enable diagnostics - session.ReturnDiagnostics = DiagnosticsMasks.All; - - // Update our cache data - if (OpcSessionCache.TryGetValue(sessionID, out entry)) - { - if (string.Equals(entry.EndpointURL, endpointURL, StringComparison.InvariantCultureIgnoreCase)) - { - OpcSessionCacheData newValue = new OpcSessionCacheData - { - EndpointURL = entry.EndpointURL, - OPCSession = session, - Username = username, - Password = password - }; - OpcSessionCache.TryUpdate(sessionID, newValue, entry); - } - } - } - - return session; - } - } -} \ No newline at end of file diff --git a/Pages/UABrowser.razor b/Pages/UABrowser.razor index 2adf965..7eb6498 100644 --- a/Pages/UABrowser.razor +++ b/Pages/UABrowser.razor @@ -4,7 +4,6 @@ @using Opc.Ua @using Opc.Ua.Client; -@inject OpcSessionHelper _helper @inject IUAClient _client
@@ -39,20 +38,20 @@ @code { - [Parameter] - public string EndpointUrl { get; set; } = string.Empty; + [Parameter] + public string EndpointUrl { get; set; } = string.Empty; - [Parameter] - public string Username { get; set; } = string.Empty; + [Parameter] + public string Username { get; set; } = string.Empty; - [Parameter] - public string Password { get; set; } = string.Empty; + [Parameter] + public string Password { get; set; } = string.Empty; - [Parameter] - public string StatusMessage { get; set; } = string.Empty; + [Parameter] + public string StatusMessage { get; set; } = string.Empty; - [Parameter] - public string SessionId { get; set; } = string.Empty; + [Parameter] + public string SessionId { get; set; } = string.Empty; private class UANode { @@ -86,9 +85,9 @@ } } - private async void SelectedNodeChanged(UANode node) + private void SelectedNodeChanged(UANode node) { - await VariableReadAsync(node).ConfigureAwait(false); + VariableRead(node); } private bool HasChildNodes(UANode node) @@ -120,9 +119,7 @@ try { - Session session = await _helper.GetSessionAsync(SessionId, EndpointUrl, Username, Password).ConfigureAwait(false); - - ReferenceDescriptionCollection references = UAClient.Browse(session, nodeToBrowse, true); + ReferenceDescriptionCollection references = await _client.Browse(EndpointUrl, Username, Password, nodeToBrowse, true).ConfigureAwait(false); Dictionary processedReferences = new(); foreach (ReferenceDescription nodeReference in references) @@ -146,49 +143,30 @@ { StatusMessage = ex.Message; - _helper.Disconnect(SessionId); - return new List(); } } - private async Task VariableReadAsync(UANode node) + private void VariableRead(UANode node) { try { - // set default values NodeId = node.NodeId; NodeDisplayName = node.Text; - NodeValue = string.Empty; NodeNotPublishable = true; - DataValueCollection values = null; - DiagnosticInfoCollection diagnosticInfos = null; - ReadValueIdCollection nodesToRead = new ReadValueIdCollection(); - - ReadValueId valueId = new() { - NodeId = new NodeId(node.NodeId), - AttributeId = Attributes.Value, - IndexRange = null, - DataEncoding = null - }; - nodesToRead.Add(valueId); - - Session session = await _helper.GetSessionAsync(SessionId, EndpointUrl, Username, Password).ConfigureAwait(false); - ResponseHeader responseHeader = session.Read(null, 0, TimestampsToReturn.Both, nodesToRead, out values, out diagnosticInfos); - if (values.Count > 0 && values[0].Value != null) + string nodeId = NodeId; + NodeValue = _client.ReadNode(EndpointUrl, Username, Password, ref nodeId); + + if (!string.IsNullOrEmpty(NodeValue)) { - NodeId = new ExpandedNodeId(valueId.NodeId, session.NamespaceUris.ToArray()[valueId.NodeId.NamespaceIndex]).ToString(); - NodeDisplayName = node.Text; - NodeValue = values[0].WrappedValue.ToString(); ; + NodeId = nodeId; NodeNotPublishable = false; } } catch (Exception ex) { StatusMessage = ex.Message; - - _helper.Disconnect(SessionId); } } @@ -196,15 +174,13 @@ { try { - Session session = await _helper.GetSessionAsync(SessionId, EndpointUrl, Username, Password).ConfigureAwait(false); - NodePublishingModel node = new NodePublishingModel { ExpandedNodeId = new ExpandedNodeId(NodeId), EndpointUrl = EndpointUrl, SkipFirst = false, - Username = null, - Password = null, + Username = Username, + Password = Password, OpcAuthenticationMode = UserAuthModeEnum.Anonymous }; @@ -220,8 +196,6 @@ catch (Exception ex) { StatusMessage = ex.Message; - - _helper.Disconnect(SessionId); } } } diff --git a/Startup.cs b/Startup.cs index 564f0f1..5aa7b6e 100644 --- a/Startup.cs +++ b/Startup.cs @@ -68,7 +68,6 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); // add our message processing engine services.AddSingleton(); diff --git a/UAClient.cs b/UAClient.cs index 3346847..ae0f395 100644 --- a/UAClient.cs +++ b/UAClient.cs @@ -444,6 +444,44 @@ private void ReconnectCompleteHandler(object sender, EventArgs e) _logger.LogInformation($"RECONNECTED session {session.SessionId}!"); } + public string ReadNode(string endpointUrl, string username, string password, ref string nodeId) + { + // find or create the session we need to monitor the node + Session session = ConnectSessionAsync( + endpointUrl, + username, + password + ).GetAwaiter().GetResult(); + + if (session == null) + { + // couldn't create the session + throw new Exception($"Could not create session for endpoint {endpointUrl}!"); + } + + DataValueCollection values = null; + DiagnosticInfoCollection diagnosticInfos = null; + ReadValueIdCollection nodesToRead = new ReadValueIdCollection(); + + ReadValueId valueId = new() + { + NodeId = new NodeId(nodeId), + AttributeId = Attributes.Value, + IndexRange = null, + DataEncoding = null + }; + nodesToRead.Add(valueId); + + session.Read(null, 0, TimestampsToReturn.Both, nodesToRead, out values, out diagnosticInfos); + if (values.Count > 0 && values[0].Value != null) + { + nodeId = new ExpandedNodeId(valueId.NodeId, session.NamespaceUris.ToArray()[valueId.NodeId.NamespaceIndex]).ToString(); + return values[0].WrappedValue.ToString(); + } + + return string.Empty; + } + public async Task PublishNodeAsync(NodePublishingModel nodeToPublish, CancellationToken cancellationToken = default) { // find or create the session we need to monitor the node @@ -827,10 +865,29 @@ private async Task PersistPublishedNodesAsync(CancellationToken cancellationToke } } - public static ReferenceDescriptionCollection Browse(Session session, BrowseDescription nodeToBrowse, bool throwOnError) + public async Task Browse(string endpointUrl, string username, string password, BrowseDescription nodeToBrowse, bool throwOnError) { - try + // find or create the session + Session session = await ConnectSessionAsync( + endpointUrl, + username, + password + ).ConfigureAwait(false); + + if (session == null) { + // couldn't create the session + throw new Exception($"Could not create session for endpoint {endpointUrl}!"); + } + + return Browse(session, nodeToBrowse, throwOnError); + } + + + private static ReferenceDescriptionCollection Browse(Session session, BrowseDescription nodeToBrowse, bool throwOnError) + { + try + { ReferenceDescriptionCollection references = new ReferenceDescriptionCollection(); // construct browse request. @@ -903,7 +960,7 @@ public static ReferenceDescriptionCollection Browse(Session session, BrowseDescr } } - public async Task> BrowseVariableNodesResursivelyAsync(Session session, NodeId nodeId) + public async Task> BrowseVariableNodesResursivelyAsync(string endpointUrl, string username, string password, NodeId nodeId) { List results = new(); @@ -922,7 +979,7 @@ public async Task> BrowseVariableNodesResursivelyAsync(S ResultMask = (uint)BrowseResultMask.All }; - ReferenceDescriptionCollection references = Browse(session, nodeToBrowse, true); + ReferenceDescriptionCollection references = await Browse(endpointUrl, username, password, nodeToBrowse, true).ConfigureAwait(false); List processedReferences = new(); foreach (ReferenceDescription nodeReference in references) @@ -935,6 +992,19 @@ public async Task> BrowseVariableNodesResursivelyAsync(S try { + // find or create the session + Session session = await ConnectSessionAsync( + endpointUrl, + username, + password + ).ConfigureAwait(false); + + if (session == null) + { + // couldn't create the session + throw new Exception($"Could not create session for endpoint {endpointUrl}!"); + } + nodeInfo.ApplicationUri = session.ServerUris.ToArray()[0]; nodeInfo.Endpoint = session.Endpoint.EndpointUrl; @@ -973,7 +1043,7 @@ public async Task> BrowseVariableNodesResursivelyAsync(S } } - List childReferences = await BrowseVariableNodesResursivelyAsync(session, ExpandedNodeId.ToNodeId(nodeReference.NodeId, session.NamespaceUris)).ConfigureAwait(false); + List childReferences = await BrowseVariableNodesResursivelyAsync(endpointUrl, username, password, ExpandedNodeId.ToNodeId(nodeReference.NodeId, session.NamespaceUris)).ConfigureAwait(false); nodeInfo.References = new string[childReferences.Count]; for (int i = 0; i < childReferences.Count; i++) @@ -1210,7 +1280,7 @@ public async Task WoTConUpload(string endpoint, string username, string password ResultMask = (uint)BrowseResultMask.All }; - ReferenceDescriptionCollection references = Browse(session, nodeToBrowse, true); + ReferenceDescriptionCollection references = await Browse(endpoint, username, password, nodeToBrowse, true).ConfigureAwait(false); fileId = (NodeId)references[0].NodeId; fileHandle = ExecuteCommand(session, MethodIds.FileType_Open, fileId, (byte)6, null, out status); diff --git a/Views/Browser/Browse.cshtml b/Views/Browser/Browse.cshtml index c908c4c..8431339 100644 --- a/Views/Browser/Browse.cshtml +++ b/Views/Browser/Browse.cshtml @@ -16,7 +16,6 @@ EndpointUrl = Model.EndpointUrl, Username = Model.UserName, Password = Model.Password, - SessionId = Model.SessionId, StatusMessage = Model.StatusMessage }).ConfigureAwait(false))