Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions build/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
156 changes: 49 additions & 107 deletions examples/CSharp DotNetCore/ExampleRaw.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> 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<TagInfo> 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<TagInfo>();
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<byte> res = stackalloc byte[cipService.GetSize()];
cipService.GetBuffer(res);

var tagInfos = new List<TagInfo>();
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<byte> bs, int offset) => BinaryPrimitives.ReadUInt16LittleEndian(bs[offset..]);
static uint U32(ReadOnlySpan<byte> bs, int offset) => BinaryPrimitives.ReadUInt32LittleEndian(bs[offset..]);
static string ASCII(ReadOnlySpan<byte> bs, int offset, int len) => Encoding.ASCII.GetString(bs[offset..(offset+len)]);
}

}
4 changes: 2 additions & 2 deletions src/libplctag.NativeImport/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
2 changes: 2 additions & 0 deletions src/libplctag.NativeImport/libplctag.NativeImport.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<DebugType>embedded</DebugType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
<PackageReference Include="System.Memory" Version="4.6.0" />
</ItemGroup>

<ItemGroup>
Expand Down
30 changes: 28 additions & 2 deletions src/libplctag.NativeImport/plctag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,38 @@ 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);
Span<byte> 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, Span<byte> buffer)
{
unsafe
{
fixed (byte* ptr = buffer)
{
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)
{
return NativeMethods.plc_tag_set_raw_bytes(tag_id, start_offset, buffer, buffer_length);
ReadOnlySpan<byte> 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, int start_offset, ReadOnlySpan<byte> buffer)
{
unsafe
{
fixed (byte* ptr = buffer)
{
return NativeMethods.plc_tag_set_raw_bytes(tag_id, start_offset, ptr, buffer.Length);
}
}
}


Expand Down
2 changes: 2 additions & 0 deletions src/libplctag/INative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> 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<byte> 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);
Expand Down
2 changes: 2 additions & 0 deletions src/libplctag/Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> 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<byte> 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);
Expand Down
24 changes: 24 additions & 0 deletions src/libplctag/Tag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,30 @@ public void GetBuffer(int offset, byte[] buffer, int length)
ThrowIfStatusNotOk(result);
}

public void GetBuffer(Span<byte> buffer)
{
GetBuffer(0, buffer);
}

public void GetBuffer(int offset, Span<byte> buffer)
{
ThrowIfAlreadyDisposed();
var result = (Status)_native.plc_tag_get_raw_bytes(nativeTagHandle, offset, buffer);
ThrowIfStatusNotOk(result);
}

public void SetBuffer(ReadOnlySpan<byte> buffer)
{
SetBuffer(0, buffer);
}

public void SetBuffer(int start_offset, ReadOnlySpan<byte> 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);
Expand Down
4 changes: 3 additions & 1 deletion src/libplctag/libplctag.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="System.Memory" Version="4.6.0" />
</ItemGroup>
</ItemGroup>

</Project>
Loading