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
26 changes: 26 additions & 0 deletions docs/libplctag.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ myTag.Write();
Console.WriteLine($"Updated value: {updatedValue}");
```

To force separate channels to the same PLC from one process, set different `ConnectionGroupId` values:

```csharp
var channel1Tag = new Tag()
{
Name = "SomeDINT",
Gateway = "10.10.10.10",
Path = "1,0",
PlcType = PlcType.ControlLogix,
Protocol = Protocol.ab_eip,
ConnectionGroupId = 1
};

var channel2Tag = new Tag()
{
Name = "SomeOtherDINT",
Gateway = "10.10.10.10",
Path = "1,0",
PlcType = PlcType.ControlLogix,
Protocol = Protocol.ab_eip,
ConnectionGroupId = 2
};
```

If `ConnectionGroupId` is not set, the channel is shared.

See the examples projects for further detail and usage:

* [C# (.NET)](../examples/CSharp%20DotNetCore/)
Expand Down
2 changes: 1 addition & 1 deletion examples/CSharp NativeImport/CallbackEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static void Run()
{

// Create the tag handle
var tagHandle = plctag.plc_tag_create("protocol=ab_eip&gateway=127.0.0.1&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MyTag[0]", 1000);
var tagHandle = plctag.plc_tag_create("protocol=ab_eip&gateway=127.0.0.1&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MyTag[0]&connection_group_id=1", 1000);
var statusBeforeRead = plctag.plc_tag_status(tagHandle);
if (statusBeforeRead != 0)
{
Expand Down
3 changes: 2 additions & 1 deletion examples/CSharp NativeImport/Multithread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ namespace NativeImport_Examples
{
class Multithread
{
const string TAG_PATH = "protocol=ab_eip&gateway=192.168.0.10&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MY_DINT";
// Optional: set connection_group_id to isolate this tag's channel from other tags in the same process.
const string TAG_PATH = "protocol=ab_eip&gateway=192.168.0.10&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MY_DINT&connection_group_id=1";
const int ELEM_COUNT = 1;
const int ELEM_SIZE = 4;
const int DATA_TIMEOUT = 2000;
Expand Down
7 changes: 4 additions & 3 deletions examples/CSharp NativeImport/NativeImportExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public static void Run()
//Please reference the libplctag documentation for API and usage


var tagHandle = plctag.plc_tag_create("protocol=ab_eip&gateway=192.168.0.10&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MY_DINT", 1000);
// Optional: set connection_group_id to create a separate channel to the same PLC when needed.
var tagHandle = plctag.plc_tag_create("protocol=ab_eip&gateway=192.168.0.10&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MY_DINT&connection_group_id=1", 1000);

while (plctag.plc_tag_status(tagHandle) == 1)
{
Expand Down Expand Up @@ -54,7 +55,7 @@ public static void Run()
public static void RunCallbackExample()
{

var tagHandle = plctag.plc_tag_create("protocol=ab_eip&gateway=192.168.0.10&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MY_DINT", 1000);
var tagHandle = plctag.plc_tag_create("protocol=ab_eip&gateway=192.168.0.10&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MY_DINT&connection_group_id=1", 1000);

while (plctag.plc_tag_status(tagHandle) == 1)
{
Expand Down Expand Up @@ -105,7 +106,7 @@ public static void RunLoggerExample()
Console.WriteLine($"Something went wrong {statusAfterRegistration}");
}

var tagHandle = plctag.plc_tag_create("protocol=ab_eip&gateway=127.0.0.1&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MyTag&debug=4", 1000);
var tagHandle = plctag.plc_tag_create("protocol=ab_eip&gateway=127.0.0.1&path=1,0&plc=LGX&elem_size=4&elem_count=1&name=MyTag&debug=4&connection_group_id=1", 1000);

while (plctag.plc_tag_status(tagHandle) == 1)
{
Expand Down
18 changes: 18 additions & 0 deletions src/libplctag.Tests/OtherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,23 @@ public void Attribute_string_does_not_contain_unset_properties()
nativeTag.Verify(m => m.plc_tag_create_ex(expectedAttributeString, It.IsAny<NativeImport.plctag.callback_func_ex>(), It.IsAny<IntPtr>(), It.IsAny<int>()), Times.Once);

}

[Fact]
public void Attribute_string_contains_connection_group_id_when_set()
{
// Arrange
var nativeTag = new Mock<INative>();
var tag = new Tag(nativeTag.Object)
{
ConnectionGroupId = 7,
};

// Act
tag.Initialize();

// Assert
var expectedAttributeString = "connection_group_id=7";
nativeTag.Verify(m => m.plc_tag_create_ex(expectedAttributeString, It.IsAny<NativeImport.plctag.callback_func_ex>(), It.IsAny<IntPtr>(), It.IsAny<int>()), Times.Once);
}
}
}
23 changes: 23 additions & 0 deletions src/libplctag/Tag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public sealed class Tag : IDisposable
private bool? _allowFieldResize;
private int? _readCacheMillisecondDuration;
private uint? _maxRequestsInFlight;
private int? _connectionGroupId;
private TimeSpan _timeout = defaultTimeout;
private TimeSpan? _autoSyncReadInterval;
private TimeSpan? _autoSyncWriteInterval;
Expand Down Expand Up @@ -630,6 +631,27 @@ public uint? MaxRequestsInFlight
set => SetField(ref _maxRequestsInFlight, value);
}

/// <summary>
/// [OPTIONAL]
/// A user-defined identifier, in the form of an integer, used to separate PLC connections into distinct groups.
/// </summary>
///
/// <remarks>
/// By default, tags that share the same <see cref="Gateway"/> and <see cref="Path"/> are
/// multiplexed over a single shared connection/session to the PLC.
/// Setting <see cref="ConnectionGroupId"/> to a distinct value forces those tags onto their
/// own, independent connection, even when the gateway and path are otherwise identical.
/// This allows a single process to maintain multiple simultaneous connections to the same PLC,
/// for example to isolate latency-sensitive polling from bulk/background requests.
/// Like the other connection attributes, this cannot be changed after the tag is initialized.
/// Note: Leave unset to preserve existing connection sharing behavior.
/// </remarks>
public int? ConnectionGroupId
{
get => GetField(ref _connectionGroupId);
set => SetField(ref _connectionGroupId, value);
}


public void Dispose()
{
Expand Down Expand Up @@ -1207,6 +1229,7 @@ string FormatTimeSpan(TimeSpan? timespan)
{ "str_pad_bytes", StringPadBytes?.ToString() },
{ "str_total_length", StringTotalLength?.ToString() },
{ "max_requests_in_flight", MaxRequestsInFlight?.ToString() },
{ "connection_group_id", ConnectionGroupId?.ToString() },
{ "allow_field_resize", FormatNullableBoolean(AllowFieldResize) },
};

Expand Down