diff --git a/NET Core/LibUA/LibUA.csproj b/NET Core/LibUA/LibUA.csproj index 802395d..f049755 100644 --- a/NET Core/LibUA/LibUA.csproj +++ b/NET Core/LibUA/LibUA.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net8.0 nauful-$(AssemblyName)-core True https://github.com/nauful/libua diff --git a/NET Core/LibUA/MemoryBufferExtensions.cs b/NET Core/LibUA/MemoryBufferExtensions.cs index 9be0f39..a77d1c7 100644 --- a/NET Core/LibUA/MemoryBufferExtensions.cs +++ b/NET Core/LibUA/MemoryBufferExtensions.cs @@ -1665,40 +1665,9 @@ public static bool Decode(this MemoryBuffer mem, out ExtensionObject obj) if (!mem.DecodeUAByteString(out byte[] str)) { return false; } obj.Body = str; - var tmp = new MemoryBuffer(str); + return obj.TryDecodeByteString(); - switch (obj.TypeId.NumericIdentifier) - { - case (uint)UAConst.ObjectAttributes_Encoding_DefaultBinary: - ObjectAttributes oa; - if (!tmp.Decode(out oa)) { return false; } - obj.Payload = oa; - break; - case (uint)UAConst.ObjectTypeAttributes_Encoding_DefaultBinary: - ObjectTypeAttributes ota; - if (!tmp.Decode(out ota)) { return false; } - obj.Payload = ota; - break; - case (uint)UAConst.VariableAttributes_Encoding_DefaultBinary: - VariableAttributes va; - if (!tmp.Decode(out va)) { return false; } - obj.Payload = va; - break; - case (uint)UAConst.VariableTypeAttributes_Encoding_DefaultBinary: - VariableTypeAttributes vta; - if (!tmp.Decode(out vta)) { return false; } - obj.Payload = vta; - break; - case (uint)UAConst.Argument_Encoding_DefaultBinary: - Argument arg; - if (!tmp.Decode(out arg)) { return false; } - obj.Payload = arg; - break; - default: - break; - } - - return true; + } return true; @@ -1706,49 +1675,12 @@ public static bool Decode(this MemoryBuffer mem, out ExtensionObject obj) public static bool Encode(this MemoryBuffer mem, ExtensionObject obj) { - if (obj == null) + if (obj == null || !obj.TryEncodeByteString(mem.Capacity)) { if (!mem.Encode(NodeId.Zero)) { return false; } return mem.Encode((byte)ExtensionObjectBodyType.None); } - if (obj.Payload != null) - { - var tmp = new MemoryBuffer(mem.Capacity); - UAConst payloadType = 0; - switch (obj.Payload) - { - case ObjectAttributes oa: - payloadType = UAConst.ObjectAttributes_Encoding_DefaultBinary; - if (!tmp.Encode(oa)) { return false; } - break; - case ObjectTypeAttributes ota: - payloadType = UAConst.ObjectTypeAttributes_Encoding_DefaultBinary; - if (!tmp.Encode(ota)) { return false; } - break; - case VariableAttributes va: - payloadType = UAConst.VariableAttributes_Encoding_DefaultBinary; - if (!tmp.Encode(va)) { return false; } - break; - case VariableTypeAttributes vta: - payloadType = UAConst.VariableTypeAttributes_Encoding_DefaultBinary; - if (!tmp.Encode(vta)) { return false; } - break; - case Argument arg: - payloadType = UAConst.Argument_Encoding_DefaultBinary; - if (!tmp.Encode(arg)) { return false; } - break; - default: - break; - } - if (payloadType != 0) - { - obj.TypeId = new NodeId(payloadType); - obj.Body = new byte[tmp.Position]; - Array.Copy(tmp.Buffer, obj.Body, obj.Body.Length); - } - } - if (!mem.Encode(obj.TypeId)) { return false; } if (obj.Body == null) diff --git a/NET Core/LibUA/Types.cs b/NET Core/LibUA/Types.cs index f67bd43..4a1b488 100644 --- a/NET Core/LibUA/Types.cs +++ b/NET Core/LibUA/Types.cs @@ -1,4 +1,5 @@ -using System; +using LibUA.ValueTypes; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -5800,10 +5801,162 @@ public override string ToString() public class ExtensionObject { + private static ConcurrentDictionary> _objectEncoders = new(); + private static ConcurrentDictionary> _objectDecoders = new(); + public static void RegisterEncoder(Func encoder) + { + _objectEncoders[typeof(TObject)] = encoder; + } + + public static void RegisterDecoder(NodeId TypeId, Func decoder) where TObject : class + { + _objectDecoders[TypeId] = decoder; + } + + + public NodeId TypeId { get; set; } public byte[] Body { get; set; } public object Payload { get; set; } + + public bool TryEncodeByteString(int BufferCapacity) + { + TypeId = null; + if (Payload != null) + { + var buffer = new MemoryBuffer(BufferCapacity); + UAConst payloadType = 0; + + if(_objectEncoders.TryGetValue(Payload.GetType(), out var encoder)) + { + TypeId = encoder(buffer); + if (TypeId == null) + return false; + } + else + { + switch (Payload) + { + case ObjectAttributes oa: + payloadType = UAConst.ObjectAttributes_Encoding_DefaultBinary; + if (!buffer.Encode(oa)) { return false; } + break; + case ObjectTypeAttributes ota: + payloadType = UAConst.ObjectTypeAttributes_Encoding_DefaultBinary; + if (!buffer.Encode(ota)) { return false; } + break; + case VariableAttributes va: + payloadType = UAConst.VariableAttributes_Encoding_DefaultBinary; + if (!buffer.Encode(va)) { return false; } + break; + case VariableTypeAttributes vta: + payloadType = UAConst.VariableTypeAttributes_Encoding_DefaultBinary; + if (!buffer.Encode(vta)) { return false; } + break; + case Argument arg: + payloadType = UAConst.Argument_Encoding_DefaultBinary; + if (!buffer.Encode(arg)) { return false; } + break; + case EUInformation eui: + payloadType = UAConst.EUInformation; + if(!buffer.Encode(eui)) { return false; } + break; + case OpcRange range: + payloadType = UAConst.Range; + if(!buffer.Encode(range)) { return false; } + break; + default: + break; + } + + if (payloadType != 0) + { + TypeId = new NodeId(payloadType); + } + } + + if(TypeId != null) + { + Body = new byte[buffer.Position]; + Array.Copy(buffer.Buffer, Body, Body.Length); + return true; + } + + return false; + } + + return false; + } + + public bool TryDecodeByteString() + { + var tmp = new MemoryBuffer(Body); + + if(_objectDecoders.TryGetValue(TypeId, out var decoder)) + { + Payload = decoder(tmp); + if (Payload != null) + return true; + } + + switch (TypeId.NumericIdentifier) + { + case (uint)UAConst.ObjectAttributes_Encoding_DefaultBinary: + ObjectAttributes oa; + if (!tmp.Decode(out oa)) { return false; } + Payload = oa; + break; + case (uint)UAConst.ObjectTypeAttributes_Encoding_DefaultBinary: + ObjectTypeAttributes ota; + if (!tmp.Decode(out ota)) { return false; } + Payload = ota; + break; + case (uint)UAConst.VariableAttributes_Encoding_DefaultBinary: + VariableAttributes va; + if (!tmp.Decode(out va)) { return false; } + Payload = va; + break; + case (uint)UAConst.VariableTypeAttributes_Encoding_DefaultBinary: + VariableTypeAttributes vta; + if (!tmp.Decode(out vta)) { return false; } + Payload = vta; + break; + case (uint)UAConst.Argument_Encoding_DefaultBinary: + Argument arg; + if (!tmp.Decode(out arg)) { return false; } + Payload = arg; + break; + case (uint)UAConst.EUInformation: + EUInformation eui; + if(!tmp.Decode(out eui)) { return false; } + Payload = eui; + break; + case (uint)UAConst.Range: + OpcRange range; + if(!tmp.Decode(out range)) { return false; } + Payload = range; + break; + default: + break; + } + + return Payload != null; + } + } + + public class ExtensionObject : ExtensionObject + { + public TPayload Value + { + get + { + if (Payload != null && Payload is TPayload tPayload) + return tPayload; + return default; + } + set => Payload = value; + } } public class DataValue diff --git a/NET Core/LibUA/ValueTypes/EUInformation.cs b/NET Core/LibUA/ValueTypes/EUInformation.cs new file mode 100644 index 0000000..2e10bb6 --- /dev/null +++ b/NET Core/LibUA/ValueTypes/EUInformation.cs @@ -0,0 +1,64 @@ +using LibUA.Core; +using System; + +namespace LibUA.ValueTypes; + +public class EUInformation +{ + public string NameSpaceUri { get; set; } = "http://www.opcfoundation.org/UA/units/un/cefact"; + public int UnitId { get; set; } = -1; + public LocalizedText DisplayName { get; set; } = new(""); + public LocalizedText Description { get; set; } = new(""); +} + +public static class EUInformationExtensions +{ + public static int CodingSize(this MemoryBuffer mem, EUInformation dv) + { + int sum = 0; + + sum += mem.CodingSizeUAString(dv.NameSpaceUri); + sum += mem.CodingSize(dv.UnitId); + sum += mem.CodingSize(dv.DisplayName); + sum += mem.CodingSize(dv.Description); + + return sum; + } + + public static bool Encode(this MemoryBuffer mem, EUInformation item) + { + if (!mem.EncodeUAString(item.NameSpaceUri)) { return false; } + if (!mem.Encode(item.UnitId)) { return false; } + if (!mem.Encode(item.DisplayName)) { return false; } + if (!mem.Encode(item.Description)) { return false; } + + return true; + } + + public static bool Decode(this MemoryBuffer mem, out EUInformation wv) + { + wv = null; + + if (!mem.DecodeUAString(out string namespaceUri)) { return false; } + if (!mem.Decode(out int unitId)) { return false; } + if (!mem.Decode(out LocalizedText displayName)) { return false; } + if (!mem.Decode(out LocalizedText description)) { return false; } + + try + { + wv = new EUInformation() + { + NameSpaceUri = namespaceUri, + UnitId = unitId, + DisplayName = displayName, + Description = description + }; + } + catch + { + return false; + } + + return true; + } +} diff --git a/NET Core/LibUA/ValueTypes/OpcRange.cs b/NET Core/LibUA/ValueTypes/OpcRange.cs new file mode 100644 index 0000000..f7150af --- /dev/null +++ b/NET Core/LibUA/ValueTypes/OpcRange.cs @@ -0,0 +1,53 @@ +using LibUA.Core; + +namespace LibUA.ValueTypes; + +public class OpcRange +{ + public double High { get; set; } + public double Low { get; set; } +} + +public static class RangeExtensions +{ + public static int CodingSize(this MemoryBuffer mem, OpcRange dv) + { + int sum = 0; + + sum += mem.CodingSize(dv.Low); + sum += mem.CodingSize(dv.High); + + return sum; + } + + public static bool Encode(this MemoryBuffer mem, OpcRange item) + { + if (!mem.Encode(item.Low)) { return false; } + if (!mem.Encode(item.High)) { return false; } + + return true; + } + + public static bool Decode(this MemoryBuffer mem, out OpcRange wv) + { + wv = null; + + if (!mem.Decode(out double low)) { return false; } + if (!mem.Decode(out double high)) { return false; } + + try + { + wv = new OpcRange() + { + High = high, + Low = low + }; + } + catch + { + return false; + } + + return true; + } +}