Skip to content

Commit

Permalink
Converted implementation to OPC UA File API to be WoT-Connectivity sp…
Browse files Browse the repository at this point in the history
…ec compliant again. Also now loading WoT-Connectivity nodeset at startup instead of generating these WoT-Connectivity nodes in code.
  • Loading branch information
barnstee committed May 6, 2024
1 parent 59b1e72 commit 9dbfd60
Show file tree
Hide file tree
Showing 13 changed files with 3,509 additions and 3,201 deletions.
291 changes: 291 additions & 0 deletions FileManager.cs
@@ -0,0 +1,291 @@

namespace Opc.Ua.Edge.Translator
{
using Opc.Ua;
using Opc.Ua.WotCon;
using Serilog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

public class FileManager : IDisposable
{
private readonly UANodeManager _nodeManager;
private readonly WoTAssetFileState _file;
private readonly Dictionary<uint, Handle> _handles = new();
private bool _writing = false;
private uint _nextHandle = 1;

private class Handle
{
public NodeId SessionId;
public MemoryStream Stream;
public uint Position;
public bool Writing;
}

public FileManager(UANodeManager nodeManager, WoTAssetFileState file)
{
_nodeManager = nodeManager;
_file = file;
_file.Size.Value = 0;
if (_file.MaxByteStringLength != null) _file.LastModifiedTime.Value = DateTime.MinValue;
_file.Writable.Value = false;
_file.UserWritable.Value = false;
_file.OpenCount.Value = 0;
if (_file.MaxByteStringLength != null) _file.MaxByteStringLength.Value = UInt16.MaxValue;
_file.Open.OnCall = new OpenMethodStateMethodCallHandler(OnOpen);
_file.Close.OnCall = new CloseMethodStateMethodCallHandler(OnClose);
_file.Read.OnCall = new ReadMethodStateMethodCallHandler(OnRead);
_file.Write.OnCall = new WriteMethodStateMethodCallHandler(OnWrite);
_file.GetPosition.OnCall = new GetPositionMethodStateMethodCallHandler(OnGetPosition);
_file.SetPosition.OnCall = new SetPositionMethodStateMethodCallHandler(OnSetPosition);
_file.CloseAndUpdate.OnCall = new WotCon.CloseAndUpdateMethodStateMethodCallHandler(OnCloseAndUpdate);
}

public void Dispose()
{
lock (_handles)
{
foreach (var handle in _handles.Values)
{
handle.Stream.Close();
handle.Stream.Dispose();
}

_handles.Clear();
}
}

private Handle Find(ISystemContext _context, uint fileHandle)
{
Handle handle;

lock (_handles)
{
if (!_handles.TryGetValue(fileHandle, out handle))
{
throw new ServiceResultException(StatusCodes.BadInvalidArgument);
}

if (handle.SessionId != _context.SessionId)
{
throw new ServiceResultException(StatusCodes.BadUserAccessDenied);
}
}

return handle;
}

private ServiceResult OnOpen(
ISystemContext _context,
MethodState _method,
NodeId _objectId,
byte mode,
ref uint fileHandle)
{
if (mode != 1 && mode != 6)
{
return StatusCodes.BadNotSupported;
}

lock (_handles)
{
if (_handles.Count >= 10)
{
return StatusCodes.BadTooManyOperations;
}

if (_writing && mode != 1)
{
return StatusCodes.BadInvalidState;
}

var handle = new Handle
{
SessionId = _context.SessionId,
Stream = new MemoryStream(),
Position = 0
};

if (mode == 6)
{
_writing = handle.Writing = true;
}

lock (_handles)
{
fileHandle = ++_nextHandle;
_handles.Add(fileHandle, handle);
_file.OpenCount.Value = (ushort)_handles.Count;
}
}

return ServiceResult.Good;
}

private ServiceResult OnGetPosition(
ISystemContext _context,
MethodState _method,
NodeId _objectId,
uint fileHandle,
ref ulong position)
{
Handle handle = Find(_context, fileHandle);
position = (ulong)handle.Stream.Position;
return StatusCodes.Good;
}

private ServiceResult OnSetPosition(
ISystemContext _context,
MethodState _method,
NodeId _objectId,
uint fileHandle,
ulong position)
{
Handle handle = Find(_context, fileHandle);
handle.Stream.Seek((long)position, SeekOrigin.Begin);
return StatusCodes.Good;
}

private ServiceResult OnRead(
ISystemContext _context,
MethodState _method,
NodeId _objectId,
uint fileHandle,
int length,
ref byte[] data)
{
lock (_handles)
{
var handle = Find(_context, fileHandle);

if (handle.Writing)
{
return StatusCodes.BadInvalidState;
}

if (data != null && data.Length > 0)
{
byte[] buffer = new byte[data.Length];
handle.Stream.Read(data, 0, data.Length);
data = buffer;
}
else
{
data = new byte[0];
}
}

return StatusCodes.Good;
}

private ServiceResult OnWrite(
ISystemContext _context,
MethodState _method,
NodeId _objectId,
uint fileHandle,
byte[] data)
{
lock (_handles)
{
var handle = Find(_context, fileHandle);

if (!handle.Writing)
{
return StatusCodes.BadInvalidState;
}

if (data != null && data.Length > 0)
{
handle.Stream.Write(data, 0, data.Length);
}
}

return StatusCodes.Good;
}

private ServiceResult OnClose(
ISystemContext _context,
MethodState _method,
NodeId _objectId,
uint fileHandle)
{
Handle handle;

lock (_handles)
{
if (!_handles.TryGetValue(fileHandle, out handle))
{
return StatusCodes.BadInvalidArgument;
}

if (handle.SessionId != _context.SessionId)
{
return StatusCodes.BadUserAccessDenied;
}

_writing = false;
_handles.Remove(fileHandle);
_file.OpenCount.Value = (ushort)_handles.Count;
}

handle.Stream.Close();
handle.Stream.Dispose();

return ServiceResult.Good;
}

private ServiceResult OnCloseAndUpdate(
ISystemContext _context,
MethodState _method,
NodeId _objectId,
uint fileHandle)
{
Handle handle;

lock (_handles)
{
if (!_handles.TryGetValue(fileHandle, out handle))
{
return StatusCodes.BadInvalidArgument;
}

if (handle.SessionId != _context.SessionId)
{
return StatusCodes.BadUserAccessDenied;
}

_writing = false;
_handles.Remove(fileHandle);
_file.OpenCount.Value = (ushort)_handles.Count;
}

try
{
handle.Stream.Close();

string contents = Encoding.UTF8.GetString(handle.Stream.ToArray());

_nodeManager.AddNodesForWoTProperties(_file.Parent, contents);

File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "settings", _file.Parent.DisplayName.Text + ".jsonld"), contents);

_ = Task.Run(() => _nodeManager.HandleServerRestart());

return ServiceResult.Good;
}
catch (Exception ex)
{
Log.Logger.Error(ex.Message, ex);
return StatusCodes.BadDecodingError;
}
finally
{
handle.Stream.Dispose();
}
}
}
}
51 changes: 0 additions & 51 deletions Models/DTDL.cs

This file was deleted.

3 changes: 0 additions & 3 deletions Models/ThingDescription.cs
Expand Up @@ -34,9 +34,6 @@ public class ThingDescription

[JsonProperty("properties")]
public Dictionary<string, Property> Properties { get; set; }

[JsonProperty("opcua:type")]
public string OpcUaType { get; set; }
}

public class OpcUaNamespaces
Expand Down

0 comments on commit 9dbfd60

Please sign in to comment.