Skip to content

Commit

Permalink
OPC UA browser improvements, session model cleanup and added ability …
Browse files Browse the repository at this point in the history
…to load, parse and download WoT files.
  • Loading branch information
barnstee committed Aug 14, 2024
1 parent fa28d12 commit 0f2920d
Show file tree
Hide file tree
Showing 5 changed files with 502 additions and 84 deletions.
211 changes: 141 additions & 70 deletions Applications/Controllers/BrowserController.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Cloud.Library.Models;
using Opc.Ua.Configuration;
using Opc.Ua.Edge.Translator.Models;
using Opc.Ua.Export;
using System;
using System.Collections.Generic;
Expand All @@ -28,89 +27,111 @@ public class BrowserController : Controller
public static List<string> _nodeSetFilenames = new List<string>();

private static HttpClient _client = new HttpClient();

private static Dictionary<string, string> _namespacesInCloudLibrary = new Dictionary<string, string>();

private static Dictionary<string, string> _namesInCloudLibrary = new Dictionary<string, string>();
private static List<string> _wotProperties = new List<string>();
private static ThingDescription _td;
private static string _wotFileName = string.Empty;

private readonly OpcSessionHelper _helper;
private readonly ApplicationInstance _application;

private OpcSessionModel _session;

public BrowserController(OpcSessionHelper helper, ApplicationInstance app)
{
_helper = helper;
_application = app;
}

public ActionResult Index()
{
OpcSessionModel sessionModel = new OpcSessionModel
_session = new()
{
SessionId = HttpContext.Session.Id,
NodesetIDs = new SelectList(new List<string>())
NodesetIDs = new SelectList(_namesInCloudLibrary.Values),
EndpointUrl = "opc.tcp://localhost",
NodesetFile = string.Empty,
WoTFile = _wotFileName,
WoTProperties = new SelectList(_wotProperties)
};

if (_nodeSetFilenames.Count > 0)
{
foreach (string filename in _nodeSetFilenames)
{
_session.NodesetFile += (filename + ", ");
}
}
}

public ActionResult Index()
{
OpcSessionCacheData entry = null;
if (_helper.OpcSessionCache.TryGetValue(HttpContext.Session.Id, out entry))
{
sessionModel.EndpointUrl = "opc.tcp://localhost";

HttpContext.Session.SetString("EndpointUrl", entry.EndpointURL);

return View("Browse", sessionModel);
return View("Browse", _session);
}

return View("Index", sessionModel);
return View("Index", _session);
}

[HttpPost]
public ActionResult Login(string instanceUrl, string clientId, string secret)
{
OpcSessionModel sessionModel = new OpcSessionModel
if (!string.IsNullOrEmpty(_client.BaseAddress?.ToString()))
{
SessionId = HttpContext.Session.Id,
NodesetIDs = new SelectList(new List<string>())
};
_client.Dispose();
_client = new HttpClient();
}

_client.BaseAddress = new Uri(instanceUrl);
_client.DefaultRequestHeaders.Remove("Authorization");
_client.DefaultRequestHeaders.Add("Authorization", "basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(clientId + ":" + secret)));

if (!instanceUrl.EndsWith('/'))
{
instanceUrl += '/';
}
_client.BaseAddress = new Uri(instanceUrl);

// get namespaces
string address = instanceUrl + "infomodel/namespaces";
HttpResponseMessage response = _client.Send(new HttpRequestMessage(HttpMethod.Get, address));
string[] identifiers = JsonConvert.DeserializeObject<string[]>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());

_namespacesInCloudLibrary.Clear();
foreach (string nodeset in identifiers)
if (identifiers != null)
{
string[] tuple = nodeset.Split(",");
_namespacesInCloudLibrary.Add(tuple[1], tuple[0]);
foreach (string nodeset in identifiers)
{
string[] tuple = nodeset.Split(",");
_namespacesInCloudLibrary.Add(tuple[1], tuple[0]);
}
}

// get names
address = instanceUrl + "infomodel/names";
response = _client.Send(new HttpRequestMessage(HttpMethod.Get, address));
string[] names = JsonConvert.DeserializeObject<string[]>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());

List<string> sortedNames = new List<string>(names);
sortedNames.Sort();

_namesInCloudLibrary.Clear();
foreach (string name in sortedNames)
if (names != null)
{
string[] tuple = name.Split(",");
_namesInCloudLibrary.Add(tuple[1], tuple[0]);
List<string> sortedNames = new List<string>(names);
sortedNames.Sort();


_namesInCloudLibrary.Clear();
if (sortedNames != null)
{
foreach (string name in sortedNames)
{
string[] tuple = name.Split(",");
_namesInCloudLibrary.Add(tuple[1], tuple[0]);
}
}
}

sessionModel.NodesetIDs = new SelectList(_namesInCloudLibrary.Values);
_session.NodesetIDs = new SelectList(_namesInCloudLibrary.Values);

return View("Index", sessionModel);
return View("Index", _session);
}

public ActionResult Privacy()
Expand Down Expand Up @@ -149,12 +170,11 @@ public ActionResult GenerateAAS()
}
catch (Exception ex)
{
OpcSessionModel sessionModel = new OpcSessionModel
{
StatusMessage = HttpUtility.HtmlDecode(ex.Message)
};
Trace.TraceError(ex.Message);

_session.StatusMessage = ex.Message;

return View("Error", sessionModel);
return View("Error", _session);
}
}

Expand All @@ -172,22 +192,13 @@ private void CopyStream(Stream source, Stream target)
[HttpPost]
public ActionResult Error(string errorMessage)
{
OpcSessionModel sessionModel = new OpcSessionModel
{
StatusMessage = HttpUtility.HtmlDecode(errorMessage),
NodesetIDs = new SelectList(new List<string>())
};
_session.StatusMessage = HttpUtility.HtmlDecode(errorMessage);

return View("Error", sessionModel);
return View("Error", _session);
}

public async Task<ActionResult> CloudLibrayFileOpen(string nodesetfile)
{
OpcSessionModel sessionModel = new OpcSessionModel
{
EndpointUrl = "opc.tcp://localhost"
};

string address = _client.BaseAddress + "infomodel/download/";
foreach (KeyValuePair<string, string> ns in _namesInCloudLibrary)
{
Expand All @@ -209,23 +220,18 @@ public async Task<ActionResult> CloudLibrayFileOpen(string nodesetfile)
string error = ValidateNamespacesAndModels(true);
if (!string.IsNullOrEmpty(error))
{
sessionModel.StatusMessage = error;
return View("Error", sessionModel);
_session.StatusMessage = error;
return View("Error", _session);
}

await StartClientAndServer(sessionModel).ConfigureAwait(false);
await StartClientAndServer().ConfigureAwait(false);

return View("Browse", sessionModel);
return View("Browse", _session);
}

[HttpPost]
public async Task<ActionResult> LocalFileOpen(IFormFile[] files, bool autodownloadreferences)
{
OpcSessionModel sessionModel = new OpcSessionModel
{
EndpointUrl = "opc.tcp://localhost"
};

try
{
if ((files == null) || (files.Length == 0))
Expand Down Expand Up @@ -257,25 +263,96 @@ public async Task<ActionResult> LocalFileOpen(IFormFile[] files, bool autodownlo
string error = ValidateNamespacesAndModels(autodownloadreferences);
if (!string.IsNullOrEmpty(error))
{
sessionModel.StatusMessage = error;
return View("Error", sessionModel);
_session.StatusMessage = error;
return View("Error", _session);
}

await StartClientAndServer().ConfigureAwait(false);

return View("Browse", _session);
}
catch (Exception ex)
{
Trace.TraceError(ex.Message);

_session.StatusMessage = ex.Message;

return View("Error", _session);
}
}

[HttpPost]
public async Task<ActionResult> WoTFileOpen(IFormFile file)
{
try
{
if ((file == null) || (file.Length == 0))
{
throw new ArgumentException("No file specified!");
}

await StartClientAndServer(sessionModel).ConfigureAwait(false);
// file name validation
new FileInfo(file.FileName);
_wotFileName = file.FileName;
_session.WoTFile = _wotFileName;

using (MemoryStream stream = new())
{
await file.CopyToAsync(stream).ConfigureAwait(false);

string contents = Encoding.UTF8.GetString(stream.ToArray());

// parse WoT TD file contents
_td = JsonConvert.DeserializeObject<ThingDescription>(contents);

return View("Browse", sessionModel);
_wotProperties = new List<string>();
foreach (string propertyName in _td.Properties.Keys)
{
_wotProperties.Add(propertyName);
}
_session.WoTProperties = new SelectList(_wotProperties);
}

return View("Browse", _session);
}
catch (Exception ex)
{
Trace.TraceError(ex.Message);

sessionModel.StatusMessage = ex.Message;
_session.StatusMessage = ex.Message;

return View("Error", sessionModel);
return View("Error", _session);
}
}

private async Task StartClientAndServer(OpcSessionModel sessionModel)
public IActionResult MapWoTProperty(string wotproperty)
{
return View("Browse", _session);
}

[HttpPost]
public IActionResult DownloadWoT()
{
try
{
string content = JsonConvert.SerializeObject(_td, Formatting.Indented);

using (MemoryStream stream = new())
{
return File(Encoding.UTF8.GetBytes(content), "application/json", _wotFileName);
}
}
catch (Exception ex)
{
Trace.TraceError(ex.Message);

_session.StatusMessage = ex.Message;

return View("Error", _session);
}
}

private async Task StartClientAndServer()
{
// (re-)start the UA server
if (_application.Server != null)
Expand Down Expand Up @@ -464,13 +541,7 @@ public ActionResult Disconnect()
_application.Stop();
}

OpcSessionModel sessionModel = new OpcSessionModel
{
SessionId = HttpContext.Session.Id,
NodesetIDs = new SelectList(new List<string>())
};

return View("Index", sessionModel);
return View("Index", _session);
}
}
}
10 changes: 5 additions & 5 deletions Applications/Models/OpcSessionModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ namespace UANodesetWebViewer.Models
{
public class OpcSessionModel
{
public string SessionId { get; set; }

public SelectList NodesetIDs { get; set; }

public string EndpointUrl { get; set; }

public string UserName { get; set; }
public string StatusMessage { get; set; }

public string NodesetFile { get; set; }

public string Password { get; set; }
public string WoTFile { get; set; }

public string StatusMessage { get; set; }
public SelectList WoTProperties { get; set; }
}
}
Loading

0 comments on commit 0f2920d

Please sign in to comment.