From ac5f2ed015e35a3c4fb93e0b74c44d14801cf016 Mon Sep 17 00:00:00 2001 From: timyhac Date: Fri, 17 Jan 2025 07:01:08 +1100 Subject: [PATCH 1/3] Added Span-based overloads for raw_bytes set/get - first draft --- src/libplctag.NativeImport/NativeMethods.cs | 4 +-- .../libplctag.NativeImport.csproj | 2 ++ src/libplctag.NativeImport/plctag.cs | 29 +++++++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/libplctag.NativeImport/NativeMethods.cs b/src/libplctag.NativeImport/NativeMethods.cs index 5a494d6..bcdbad2 100644 --- a/src/libplctag.NativeImport/NativeMethods.cs +++ b/src/libplctag.NativeImport/NativeMethods.cs @@ -245,9 +245,9 @@ static NativeMethods() [DllImport(DLL_NAME, EntryPoint = nameof(plc_tag_get_raw_bytes), CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern int plc_tag_get_raw_bytes(Int32 tag_id, int start_offset, [Out] byte[] buffer, int buffer_length); + public unsafe static extern int plc_tag_get_raw_bytes(Int32 tag_id, int start_offset, byte* buffer, int buffer_length); [DllImport(DLL_NAME, EntryPoint = nameof(plc_tag_set_raw_bytes), CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern int plc_tag_set_raw_bytes(Int32 tag_id, int start_offset, [In] byte[] buffer, int buffer_length); + public unsafe static extern int plc_tag_set_raw_bytes(Int32 tag_id, int start_offset, byte* buffer, int buffer_length); } } diff --git a/src/libplctag.NativeImport/libplctag.NativeImport.csproj b/src/libplctag.NativeImport/libplctag.NativeImport.csproj index 467d4ce..4d3a795 100644 --- a/src/libplctag.NativeImport/libplctag.NativeImport.csproj +++ b/src/libplctag.NativeImport/libplctag.NativeImport.csproj @@ -32,10 +32,12 @@ true true embedded + true + diff --git a/src/libplctag.NativeImport/plctag.cs b/src/libplctag.NativeImport/plctag.cs index 86a912b..4c6068a 100644 --- a/src/libplctag.NativeImport/plctag.cs +++ b/src/libplctag.NativeImport/plctag.cs @@ -281,12 +281,37 @@ public static int plc_tag_get_string_total_length(Int32 tag_id, int string_start public static int plc_tag_get_raw_bytes(Int32 tag_id, int start_offset, byte[] buffer, int buffer_length) { - return NativeMethods.plc_tag_get_raw_bytes(tag_id, start_offset, buffer, buffer_length); + int returnValue = plc_tag_get_raw_bytes(tag_id, start_offset, out Span span); + span.CopyTo(buffer); + return returnValue; + } + + public static int plc_tag_get_raw_bytes(Int32 tag_id, int start_offset, out Span buffer) + { + unsafe + { + fixed (byte* ptr = buffer) + { + return NativeMethods.plc_tag_get_raw_bytes(tag_id, 0, ptr, buffer.Length); + } + } } public static int plc_tag_set_raw_bytes(Int32 tag_id, int start_offset, byte[] buffer, int buffer_length) { - return NativeMethods.plc_tag_set_raw_bytes(tag_id, start_offset, buffer, buffer_length); + ReadOnlySpan span = buffer.AsSpan().Slice(start_offset, buffer_length); + return plc_tag_set_raw_bytes(tag_id, span); + } + + public static int plc_tag_set_raw_bytes(Int32 tag_id, ReadOnlySpan buffer) + { + unsafe + { + fixed (byte* ptr = buffer) + { + return NativeMethods.plc_tag_set_raw_bytes(tag_id, 0, ptr, buffer.Length); + } + } } From b28ec4dcc868bfd4fdbb2292a3b94389efcf3b6e Mon Sep 17 00:00:00 2001 From: timyhac Date: Fri, 17 Jan 2025 18:09:16 +1100 Subject: [PATCH 2/3] Fixes to build --- build/Build.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/build/Build.cs b/build/Build.cs index f88aa25..4ad3877 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -132,19 +132,20 @@ class Build : NukeBuild void NetCoreInstallRestoreBuildTest(Project proj, bool isTransitive, string nugetConfigPath) { - DotNetRestore(s => s - .SetProjectFile(proj) - .SetPackageDirectory(PackageRestoreDirectory) - .SetConfigFile(nugetConfigPath) - ); var libplctagVersion = libplctag.GetProperty("version"); var libplctagNativeImportVersion = libplctag_NativeImport.GetProperty("version"); - DotNet($"add {proj.Path} package {libplctag_NativeImport.Name} -s {ArtifactsDirectory} --version {libplctagNativeImportVersion} --package-directory {PackageRestoreDirectory}"); + DotNet($"add {proj.Path} package {libplctag_NativeImport.Name} -s {ArtifactsDirectory} --version {libplctagNativeImportVersion} --package-directory {PackageRestoreDirectory} --no-restore"); if (isTransitive) - DotNet($"add {proj.Path} package {libplctag.Name} -s {ArtifactsDirectory} --version {libplctagVersion} --package-directory {PackageRestoreDirectory}"); + DotNet($"add {proj.Path} package {libplctag.Name} -s {ArtifactsDirectory} --version {libplctagVersion} --package-directory {PackageRestoreDirectory} --no-restore"); + + DotNetRestore(s => s + .SetProjectFile(proj) + .SetPackageDirectory(PackageRestoreDirectory) + .SetConfigFile(nugetConfigPath) + ); DotNetBuild(s => s .SetProjectFile(proj) From 70d5afc4d2b3a10e45750bb07a57392eec425877 Mon Sep 17 00:00:00 2001 From: timyhac Date: Sat, 18 Jan 2025 14:17:07 +1100 Subject: [PATCH 3/3] Changes required for working `ExampleRaw.cs` --- examples/CSharp DotNetCore/ExampleRaw.cs | 156 +++++++---------------- src/libplctag.NativeImport/plctag.cs | 15 ++- src/libplctag/INative.cs | 2 + src/libplctag/Native.cs | 2 + src/libplctag/Tag.cs | 24 ++++ src/libplctag/libplctag.csproj | 4 +- 6 files changed, 88 insertions(+), 115 deletions(-) diff --git a/examples/CSharp DotNetCore/ExampleRaw.cs b/examples/CSharp DotNetCore/ExampleRaw.cs index 656fea5..a7bedb8 100644 --- a/examples/CSharp DotNetCore/ExampleRaw.cs +++ b/examples/CSharp DotNetCore/ExampleRaw.cs @@ -7,132 +7,74 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; +using System.Buffers.Binary; using libplctag; namespace CSharp_DotNetCore { class ExampleRaw { - public static void Run() - { - var lister = new LogixTagListing() - { - Gateway = "192.168.0.1", - Path = "1,0", - Timeout = TimeSpan.FromMilliseconds(1000), - }; - - var tags = lister.ListTags(); - - foreach (var tag in tags) - Console.WriteLine($"Id={tag.Id} Name={tag.Name} Type={tag.Type} Length={tag.Length}"); - } - class LogixTagListing + public static void Run() { - readonly Tag _rawCip = new Tag() + // This payload is taken from https://github.com/libplctag/libplctag/blob/release/src/examples/test_raw_cip.c + // but others can be found by analysing the Rockwell or other manufacturer's documentation + // https://literature.rockwellautomation.com/idc/groups/literature/documents/pm/1756-pm020_-en-p.pdf pg 39 + + ReadOnlySpan raw_payload = [ + 0x55, + 0x03, + 0x20, 0x6b, 0x25, 0x00, 0x00, 0x00, + 0x04, 0x00, + 0x02, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x01, 0x00 + ]; + + var cipService = new Tag() { PlcType = PlcType.ControlLogix, Protocol = Protocol.ab_eip, + Gateway = "10.10.10.10", + Path = "1,0", Name = "@raw", }; - - public string Gateway { get => _rawCip.Gateway; set => _rawCip.Gateway = value; } - public string Path { get => _rawCip.Path; set => _rawCip.Path = value; } - public TimeSpan Timeout { get => _rawCip.Timeout; set => _rawCip.Timeout = value; } - - public List ListTags() - { - // This payload is taken from https://github.com/libplctag/libplctag/blob/release/src/examples/test_raw_cip.c - // but others can be found by analysing the Rockwell or other manufacturer's documentation - // https://literature.rockwellautomation.com/idc/groups/literature/documents/pm/1756-pm020_-en-p.pdf pg 39 - - var raw_payload = new byte[] { - 0x55, - 0x03, - 0x20, - 0x6b, - 0x25, - 0x00, - 0x00, - 0x00, - 0x04, - 0x00, - 0x02, - 0x00, - 0x07, - 0x00, - 0x08, - 0x00, - 0x01, - 0x00 - }; - - _rawCip.Initialize(); - _rawCip.SetSize(raw_payload.Length); - _rawCip.SetBuffer(raw_payload); - _rawCip.Write(); - - var responseSize = _rawCip.GetSize(); - - var tagInfos = new List(); - int offset = 0; - while (offset < responseSize) - tagInfos.Add(DecodeOneTagInfo(ref offset)); - - return tagInfos; - } - - public class TagInfo - { - public uint Id { get; set; } - public ushort Type { get; set; } - public string Name { get; set; } - public ushort Length { get; set; } - public uint[] Dimensions { get; set; } - } - - TagInfo DecodeOneTagInfo(ref int offset) + cipService.Initialize(); + cipService.SetSize(raw_payload.Length); + cipService.SetBuffer(raw_payload); + cipService.Write(); + Span res = stackalloc byte[cipService.GetSize()]; + cipService.GetBuffer(res); + + var tagInfos = new List(); + for (int cursor = 0; cursor < res.Length;) { - - var tagInstanceId = _rawCip.GetUInt32(offset); - var tagType = _rawCip.GetUInt16(offset + 4); - var tagLength = _rawCip.GetUInt16(offset + 6); - var tagArrayDims = new uint[] - { - _rawCip.GetUInt32(offset + 8), - _rawCip.GetUInt32(offset + 12), - _rawCip.GetUInt32(offset + 16) - }; - - var apparentTagNameLength = (int)_rawCip.GetUInt16(offset + 20); - const int TAG_STRING_SIZE = 200; - var actualTagNameLength = Math.Min(apparentTagNameLength, TAG_STRING_SIZE * 2 - 1); - - var tagNameBytes = Enumerable.Range(offset + 22, actualTagNameLength) - .Select(o => _rawCip.GetUInt8(o)) - .Select(Convert.ToByte) - .ToArray(); - - var tagName = Encoding.ASCII.GetString(tagNameBytes); - - offset = 22 + actualTagNameLength; - - return new TagInfo() - { - Id = tagInstanceId, - Type = tagType, - Name = tagName, - Length = tagLength, - Dimensions = tagArrayDims - }; - + var tagNameLength = U16(res, cursor + 20); + tagInfos.Add(new ( + Id : U32(res, cursor + 0), + Type : U16(res, cursor + 4), + Length : U16(res, cursor + 6), + Dimensions : [ + U32(res, cursor + 8), + U32(res, cursor + 12), + U32(res, cursor + 16) + ], + Name : ASCII(res, cursor + 22, tagNameLength) + )); + + cursor += 22 + tagNameLength; } + foreach (var tag in tagInfos) + Console.WriteLine($"Id={tag.Id} Name={tag.Name} Type={tag.Type} Length={tag.Length}"); } + record TagInfo(uint Id, ushort Type, ushort Length, uint[] Dimensions, string Name); + static ushort U16(ReadOnlySpan bs, int offset) => BinaryPrimitives.ReadUInt16LittleEndian(bs[offset..]); + static uint U32(ReadOnlySpan bs, int offset) => BinaryPrimitives.ReadUInt32LittleEndian(bs[offset..]); + static string ASCII(ReadOnlySpan bs, int offset, int len) => Encoding.ASCII.GetString(bs[offset..(offset+len)]); } } diff --git a/src/libplctag.NativeImport/plctag.cs b/src/libplctag.NativeImport/plctag.cs index 4c6068a..047372f 100644 --- a/src/libplctag.NativeImport/plctag.cs +++ b/src/libplctag.NativeImport/plctag.cs @@ -281,35 +281,36 @@ public static int plc_tag_get_string_total_length(Int32 tag_id, int string_start public static int plc_tag_get_raw_bytes(Int32 tag_id, int start_offset, byte[] buffer, int buffer_length) { - int returnValue = plc_tag_get_raw_bytes(tag_id, start_offset, out Span span); + Span span = buffer.AsSpan().Slice(0, buffer_length); + int returnValue = plc_tag_get_raw_bytes(tag_id, start_offset, span); span.CopyTo(buffer); return returnValue; } - public static int plc_tag_get_raw_bytes(Int32 tag_id, int start_offset, out Span buffer) + public static int plc_tag_get_raw_bytes(Int32 tag_id, int start_offset, Span buffer) { unsafe { fixed (byte* ptr = buffer) { - return NativeMethods.plc_tag_get_raw_bytes(tag_id, 0, ptr, buffer.Length); + return NativeMethods.plc_tag_get_raw_bytes(tag_id, start_offset, ptr, buffer.Length); } } } public static int plc_tag_set_raw_bytes(Int32 tag_id, int start_offset, byte[] buffer, int buffer_length) { - ReadOnlySpan span = buffer.AsSpan().Slice(start_offset, buffer_length); - return plc_tag_set_raw_bytes(tag_id, span); + ReadOnlySpan span = buffer.AsSpan(); + return plc_tag_set_raw_bytes(tag_id, start_offset, span); } - public static int plc_tag_set_raw_bytes(Int32 tag_id, ReadOnlySpan buffer) + public static int plc_tag_set_raw_bytes(Int32 tag_id, int start_offset, ReadOnlySpan buffer) { unsafe { fixed (byte* ptr = buffer) { - return NativeMethods.plc_tag_set_raw_bytes(tag_id, 0, ptr, buffer.Length); + return NativeMethods.plc_tag_set_raw_bytes(tag_id, start_offset, ptr, buffer.Length); } } } diff --git a/src/libplctag/INative.cs b/src/libplctag/INative.cs index b2d50d1..d226598 100644 --- a/src/libplctag/INative.cs +++ b/src/libplctag/INative.cs @@ -63,7 +63,9 @@ interface INative int plc_tag_unregister_logger(int tag_id); int plc_tag_write(int tag, int timeout); int plc_tag_get_raw_bytes(int tag, int start_offset, byte[] buffer, int buffer_length); + int plc_tag_get_raw_bytes(int tag, int start_offset, Span buffer); int plc_tag_set_raw_bytes(int tag, int start_offset, byte[] buffer, int buffer_length); + int plc_tag_set_raw_bytes(int tag, int start_offset, ReadOnlySpan buffer); int plc_tag_get_string_length(int tag, int string_start_offset); int plc_tag_get_string(int tag, int string_start_offset, StringBuilder buffer, int buffer_length); int plc_tag_get_string_total_length(int tag, int string_start_offset); diff --git a/src/libplctag/Native.cs b/src/libplctag/Native.cs index a5a4fb4..44c67d7 100644 --- a/src/libplctag/Native.cs +++ b/src/libplctag/Native.cs @@ -59,7 +59,9 @@ class Native : INative public int plc_tag_set_bit(Int32 tag, int offset_bit, int val) => plctag.plc_tag_set_bit(tag, offset_bit, val); public void plc_tag_set_debug_level(int debug_level) => plctag.plc_tag_set_debug_level(debug_level); public int plc_tag_get_raw_bytes(int tag, int start_offset, byte[] buffer, int buffer_length) => plctag.plc_tag_get_raw_bytes(tag, start_offset, buffer, buffer_length); + public int plc_tag_get_raw_bytes(int tag, int start_offset, Span buffer) => plctag.plc_tag_get_raw_bytes(tag, start_offset, buffer); public int plc_tag_set_raw_bytes(int tag, int start_offset, byte[] buffer, int buffer_length) => plctag.plc_tag_set_raw_bytes(tag, start_offset, buffer, buffer_length); + public int plc_tag_set_raw_bytes(int tag, int start_offset, ReadOnlySpan buffer) => plctag.plc_tag_set_raw_bytes(tag, start_offset, buffer); public int plc_tag_get_string_length(int tag, int string_start_offset) => plctag.plc_tag_get_string_length(tag, string_start_offset); public int plc_tag_get_string(int tag, int string_start_offset, StringBuilder buffer, int buffer_length) => plctag.plc_tag_get_string(tag, string_start_offset, buffer, buffer_length); public int plc_tag_get_string_total_length(int tag, int string_start_offset) => plctag.plc_tag_get_string_total_length(tag, string_start_offset); diff --git a/src/libplctag/Tag.cs b/src/libplctag/Tag.cs index b31e7b0..7740837 100644 --- a/src/libplctag/Tag.cs +++ b/src/libplctag/Tag.cs @@ -948,6 +948,30 @@ public void GetBuffer(int offset, byte[] buffer, int length) ThrowIfStatusNotOk(result); } + public void GetBuffer(Span buffer) + { + GetBuffer(0, buffer); + } + + public void GetBuffer(int offset, Span buffer) + { + ThrowIfAlreadyDisposed(); + var result = (Status)_native.plc_tag_get_raw_bytes(nativeTagHandle, offset, buffer); + ThrowIfStatusNotOk(result); + } + + public void SetBuffer(ReadOnlySpan buffer) + { + SetBuffer(0, buffer); + } + + public void SetBuffer(int start_offset, ReadOnlySpan buffer) + { + ThrowIfAlreadyDisposed(); + var result = (Status)_native.plc_tag_set_raw_bytes(nativeTagHandle, start_offset, buffer); + ThrowIfStatusNotOk(result); + } + public void SetBuffer(byte[] buffer) { SetBuffer(0, buffer, buffer.Length); diff --git a/src/libplctag/libplctag.csproj b/src/libplctag/libplctag.csproj index a80ea41..0a4ab4e 100644 --- a/src/libplctag/libplctag.csproj +++ b/src/libplctag/libplctag.csproj @@ -38,7 +38,9 @@ - + + +