Skip to content

Commit 0f2920d

Browse files
committed
OPC UA browser improvements, session model cleanup and added ability to load, parse and download WoT files.
1 parent fa28d12 commit 0f2920d

File tree

5 files changed

+502
-84
lines changed

5 files changed

+502
-84
lines changed

Applications/Controllers/BrowserController.cs

+141-70
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
using Microsoft.AspNetCore.Http;
22
using Microsoft.AspNetCore.Mvc;
33
using Microsoft.AspNetCore.Mvc.Rendering;
4-
using Microsoft.AspNetCore.SignalR;
5-
using Microsoft.Extensions.Logging;
64
using Newtonsoft.Json;
75
using Opc.Ua;
86
using Opc.Ua.Client;
97
using Opc.Ua.Cloud.Library.Models;
108
using Opc.Ua.Configuration;
9+
using Opc.Ua.Edge.Translator.Models;
1110
using Opc.Ua.Export;
1211
using System;
1312
using System.Collections.Generic;
@@ -28,89 +27,111 @@ public class BrowserController : Controller
2827
public static List<string> _nodeSetFilenames = new List<string>();
2928

3029
private static HttpClient _client = new HttpClient();
31-
3230
private static Dictionary<string, string> _namespacesInCloudLibrary = new Dictionary<string, string>();
33-
3431
private static Dictionary<string, string> _namesInCloudLibrary = new Dictionary<string, string>();
32+
private static List<string> _wotProperties = new List<string>();
33+
private static ThingDescription _td;
34+
private static string _wotFileName = string.Empty;
3535

3636
private readonly OpcSessionHelper _helper;
3737
private readonly ApplicationInstance _application;
3838

39+
private OpcSessionModel _session;
40+
3941
public BrowserController(OpcSessionHelper helper, ApplicationInstance app)
4042
{
4143
_helper = helper;
4244
_application = app;
43-
}
4445

45-
public ActionResult Index()
46-
{
47-
OpcSessionModel sessionModel = new OpcSessionModel
46+
_session = new()
4847
{
49-
SessionId = HttpContext.Session.Id,
50-
NodesetIDs = new SelectList(new List<string>())
48+
NodesetIDs = new SelectList(_namesInCloudLibrary.Values),
49+
EndpointUrl = "opc.tcp://localhost",
50+
NodesetFile = string.Empty,
51+
WoTFile = _wotFileName,
52+
WoTProperties = new SelectList(_wotProperties)
5153
};
5254

55+
if (_nodeSetFilenames.Count > 0)
56+
{
57+
foreach (string filename in _nodeSetFilenames)
58+
{
59+
_session.NodesetFile += (filename + ", ");
60+
}
61+
}
62+
}
63+
64+
public ActionResult Index()
65+
{
5366
OpcSessionCacheData entry = null;
5467
if (_helper.OpcSessionCache.TryGetValue(HttpContext.Session.Id, out entry))
5568
{
56-
sessionModel.EndpointUrl = "opc.tcp://localhost";
57-
5869
HttpContext.Session.SetString("EndpointUrl", entry.EndpointURL);
5970

60-
return View("Browse", sessionModel);
71+
return View("Browse", _session);
6172
}
6273

63-
return View("Index", sessionModel);
74+
return View("Index", _session);
6475
}
6576

6677
[HttpPost]
6778
public ActionResult Login(string instanceUrl, string clientId, string secret)
6879
{
69-
OpcSessionModel sessionModel = new OpcSessionModel
80+
if (!string.IsNullOrEmpty(_client.BaseAddress?.ToString()))
7081
{
71-
SessionId = HttpContext.Session.Id,
72-
NodesetIDs = new SelectList(new List<string>())
73-
};
82+
_client.Dispose();
83+
_client = new HttpClient();
84+
}
7485

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

7890
if (!instanceUrl.EndsWith('/'))
7991
{
8092
instanceUrl += '/';
8193
}
82-
_client.BaseAddress = new Uri(instanceUrl);
8394

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

89100
_namespacesInCloudLibrary.Clear();
90-
foreach (string nodeset in identifiers)
101+
if (identifiers != null)
91102
{
92-
string[] tuple = nodeset.Split(",");
93-
_namespacesInCloudLibrary.Add(tuple[1], tuple[0]);
103+
foreach (string nodeset in identifiers)
104+
{
105+
string[] tuple = nodeset.Split(",");
106+
_namespacesInCloudLibrary.Add(tuple[1], tuple[0]);
107+
}
94108
}
95109

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

101-
List<string> sortedNames = new List<string>(names);
102-
sortedNames.Sort();
103-
104-
_namesInCloudLibrary.Clear();
105-
foreach (string name in sortedNames)
115+
if (names != null)
106116
{
107-
string[] tuple = name.Split(",");
108-
_namesInCloudLibrary.Add(tuple[1], tuple[0]);
117+
List<string> sortedNames = new List<string>(names);
118+
sortedNames.Sort();
119+
120+
121+
_namesInCloudLibrary.Clear();
122+
if (sortedNames != null)
123+
{
124+
foreach (string name in sortedNames)
125+
{
126+
string[] tuple = name.Split(",");
127+
_namesInCloudLibrary.Add(tuple[1], tuple[0]);
128+
}
129+
}
109130
}
110131

111-
sessionModel.NodesetIDs = new SelectList(_namesInCloudLibrary.Values);
132+
_session.NodesetIDs = new SelectList(_namesInCloudLibrary.Values);
112133

113-
return View("Index", sessionModel);
134+
return View("Index", _session);
114135
}
115136

116137
public ActionResult Privacy()
@@ -149,12 +170,11 @@ public ActionResult GenerateAAS()
149170
}
150171
catch (Exception ex)
151172
{
152-
OpcSessionModel sessionModel = new OpcSessionModel
153-
{
154-
StatusMessage = HttpUtility.HtmlDecode(ex.Message)
155-
};
173+
Trace.TraceError(ex.Message);
174+
175+
_session.StatusMessage = ex.Message;
156176

157-
return View("Error", sessionModel);
177+
return View("Error", _session);
158178
}
159179
}
160180

@@ -172,22 +192,13 @@ private void CopyStream(Stream source, Stream target)
172192
[HttpPost]
173193
public ActionResult Error(string errorMessage)
174194
{
175-
OpcSessionModel sessionModel = new OpcSessionModel
176-
{
177-
StatusMessage = HttpUtility.HtmlDecode(errorMessage),
178-
NodesetIDs = new SelectList(new List<string>())
179-
};
195+
_session.StatusMessage = HttpUtility.HtmlDecode(errorMessage);
180196

181-
return View("Error", sessionModel);
197+
return View("Error", _session);
182198
}
183199

184200
public async Task<ActionResult> CloudLibrayFileOpen(string nodesetfile)
185201
{
186-
OpcSessionModel sessionModel = new OpcSessionModel
187-
{
188-
EndpointUrl = "opc.tcp://localhost"
189-
};
190-
191202
string address = _client.BaseAddress + "infomodel/download/";
192203
foreach (KeyValuePair<string, string> ns in _namesInCloudLibrary)
193204
{
@@ -209,23 +220,18 @@ public async Task<ActionResult> CloudLibrayFileOpen(string nodesetfile)
209220
string error = ValidateNamespacesAndModels(true);
210221
if (!string.IsNullOrEmpty(error))
211222
{
212-
sessionModel.StatusMessage = error;
213-
return View("Error", sessionModel);
223+
_session.StatusMessage = error;
224+
return View("Error", _session);
214225
}
215226

216-
await StartClientAndServer(sessionModel).ConfigureAwait(false);
227+
await StartClientAndServer().ConfigureAwait(false);
217228

218-
return View("Browse", sessionModel);
229+
return View("Browse", _session);
219230
}
220231

221232
[HttpPost]
222233
public async Task<ActionResult> LocalFileOpen(IFormFile[] files, bool autodownloadreferences)
223234
{
224-
OpcSessionModel sessionModel = new OpcSessionModel
225-
{
226-
EndpointUrl = "opc.tcp://localhost"
227-
};
228-
229235
try
230236
{
231237
if ((files == null) || (files.Length == 0))
@@ -257,25 +263,96 @@ public async Task<ActionResult> LocalFileOpen(IFormFile[] files, bool autodownlo
257263
string error = ValidateNamespacesAndModels(autodownloadreferences);
258264
if (!string.IsNullOrEmpty(error))
259265
{
260-
sessionModel.StatusMessage = error;
261-
return View("Error", sessionModel);
266+
_session.StatusMessage = error;
267+
return View("Error", _session);
268+
}
269+
270+
await StartClientAndServer().ConfigureAwait(false);
271+
272+
return View("Browse", _session);
273+
}
274+
catch (Exception ex)
275+
{
276+
Trace.TraceError(ex.Message);
277+
278+
_session.StatusMessage = ex.Message;
279+
280+
return View("Error", _session);
281+
}
282+
}
283+
284+
[HttpPost]
285+
public async Task<ActionResult> WoTFileOpen(IFormFile file)
286+
{
287+
try
288+
{
289+
if ((file == null) || (file.Length == 0))
290+
{
291+
throw new ArgumentException("No file specified!");
262292
}
263293

264-
await StartClientAndServer(sessionModel).ConfigureAwait(false);
294+
// file name validation
295+
new FileInfo(file.FileName);
296+
_wotFileName = file.FileName;
297+
_session.WoTFile = _wotFileName;
298+
299+
using (MemoryStream stream = new())
300+
{
301+
await file.CopyToAsync(stream).ConfigureAwait(false);
302+
303+
string contents = Encoding.UTF8.GetString(stream.ToArray());
304+
305+
// parse WoT TD file contents
306+
_td = JsonConvert.DeserializeObject<ThingDescription>(contents);
265307

266-
return View("Browse", sessionModel);
308+
_wotProperties = new List<string>();
309+
foreach (string propertyName in _td.Properties.Keys)
310+
{
311+
_wotProperties.Add(propertyName);
312+
}
313+
_session.WoTProperties = new SelectList(_wotProperties);
314+
}
315+
316+
return View("Browse", _session);
267317
}
268318
catch (Exception ex)
269319
{
270320
Trace.TraceError(ex.Message);
271321

272-
sessionModel.StatusMessage = ex.Message;
322+
_session.StatusMessage = ex.Message;
273323

274-
return View("Error", sessionModel);
324+
return View("Error", _session);
275325
}
276326
}
277327

278-
private async Task StartClientAndServer(OpcSessionModel sessionModel)
328+
public IActionResult MapWoTProperty(string wotproperty)
329+
{
330+
return View("Browse", _session);
331+
}
332+
333+
[HttpPost]
334+
public IActionResult DownloadWoT()
335+
{
336+
try
337+
{
338+
string content = JsonConvert.SerializeObject(_td, Formatting.Indented);
339+
340+
using (MemoryStream stream = new())
341+
{
342+
return File(Encoding.UTF8.GetBytes(content), "application/json", _wotFileName);
343+
}
344+
}
345+
catch (Exception ex)
346+
{
347+
Trace.TraceError(ex.Message);
348+
349+
_session.StatusMessage = ex.Message;
350+
351+
return View("Error", _session);
352+
}
353+
}
354+
355+
private async Task StartClientAndServer()
279356
{
280357
// (re-)start the UA server
281358
if (_application.Server != null)
@@ -464,13 +541,7 @@ public ActionResult Disconnect()
464541
_application.Stop();
465542
}
466543

467-
OpcSessionModel sessionModel = new OpcSessionModel
468-
{
469-
SessionId = HttpContext.Session.Id,
470-
NodesetIDs = new SelectList(new List<string>())
471-
};
472-
473-
return View("Index", sessionModel);
544+
return View("Index", _session);
474545
}
475546
}
476547
}

Applications/Models/OpcSessionModel.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ namespace UANodesetWebViewer.Models
55
{
66
public class OpcSessionModel
77
{
8-
public string SessionId { get; set; }
9-
108
public SelectList NodesetIDs { get; set; }
119

1210
public string EndpointUrl { get; set; }
1311

14-
public string UserName { get; set; }
12+
public string StatusMessage { get; set; }
13+
14+
public string NodesetFile { get; set; }
1515

16-
public string Password { get; set; }
16+
public string WoTFile { get; set; }
1717

18-
public string StatusMessage { get; set; }
18+
public SelectList WoTProperties { get; set; }
1919
}
2020
}

0 commit comments

Comments
 (0)