L5Sharp.Gateway 1.0.0-beta.1

This is a prerelease version of L5Sharp.Gateway.
dotnet add package L5Sharp.Gateway --version 1.0.0-beta.1
                    
NuGet\Install-Package L5Sharp.Gateway -Version 1.0.0-beta.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="L5Sharp.Gateway" Version="1.0.0-beta.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="L5Sharp.Gateway" Version="1.0.0-beta.1" />
                    
Directory.Packages.props
<PackageReference Include="L5Sharp.Gateway" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add L5Sharp.Gateway --version 1.0.0-beta.1
                    
#r "nuget: L5Sharp.Gateway, 1.0.0-beta.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package L5Sharp.Gateway@1.0.0-beta.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=L5Sharp.Gateway&version=1.0.0-beta.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=L5Sharp.Gateway&version=1.0.0-beta.1&prerelease
                    
Install as a Cake Tool

L5Sharp.Gateway

A .NET library providing PLC communication capabilities for Rockwell Automation Logix controllers through tag read/write operations, integrating L5Sharp with libplctag.

Overview

L5Sharp.Gateway enables real-time communication with Allen-Bradley PLCs by bridging the high-performance libplctag library with the rich type system of L5Sharp. While other wrappers exist, L5Sharp.Gateway is uniquely designed to map PLC data directly to L5Sharp tag objects—whether they are extracted from an L5X project file or defined dynamically in memory. This integration simplifies the API and provides out-of-the-box data mapping for complex Logix structures.

Motivation

The primary driver for L5Sharp.Gateway was to address the "data mapping gap" found in existing PLC libraries. Most libraries excel at reading individual atomic tags (DINTs, REALs) but offer little support for complex structures. To work with a UDT or a Predefined type (like a TIMER), developers are typically forced to manually map raw byte arrays onto C# classes. This process is error-prone because Logix data packing rules such as member alignment and padding differ significantly between UDTs and Predefined structures. Unpacking these types correctly often requires knowledge of internal Logix memory layouts or tedious reverse engineering.

L5Sharp.Gateway eliminates this burden. Instead of manual byte-shuffling, developers work with strongly typed Tag objects that handle serialization and deserialization automatically. By leveraging source-generated types for standard Logix structures (TIMER, COUNTER, etc.) and providing tools to generate types for custom UDTs, the library ensures that PLC communication is IntelliSense-friendly, type-safe, and free of boilerplate mapping code.

This provides the best of both worlds: the proven reliability of libplctag combined with the sophisticated modeling of L5Sharp, resulting in cleaner, more maintainable code and significantly reduced development time.

Features

  • Real PLC Communication: Read and write tag data from/to physical controllers via libplctag
  • L5Sharp Integration: Leverage L5Sharp.Core tag definitions and type system
  • Protocol Support: Compatible with ab_eip protocol for ControlLogix, CompactLogix, and Micro800 PLCs
  • Virtual Tag Service: In-memory service for testing without hardware
  • Tag Operations: Single and bulk tag read/write capabilities
  • Tag Watch: Monitor updates for specific tags as changes occur in the PLC
  • Async API: All operations use modern Task-based methods
  • Result Pattern: Get aggregated tag response object, similar to an HTTP client.
  • Type Safety: Strongly typed tag data with automatic serialization

Installation

L5Sharp.Gateway is available as a NuGet package and can be installed using your preferred package manager.

Install-Package L5Sharp.Gateway

Usage

The primary interface for the library is the PlcClient object. Create a new client by providing the IP and slot of the PLC to connect to.

using var client = new PlcClient("10.10.10.10", 1);

Tag Reading

Use the client object to read a known tag by name and type.

var response = await client.ReadTag<DINT>("MyDintTag");

To read program tags, use the "Program:" prefix. Example: Program:SomeProgram.MyDintTag

Or get the tag from an existing L5X and pass it to the client. Note that in the following example, we don't need to know the type of the tag since it is embedded in the L5X.

var content = L5X.Load(@"C:\Path\To\MyFile.L5X");
var tag = content.Tags.Get("SomeTag");
var response = await client.ReadTag(tag);

Inspect the TagRespone object of the completed operation. The Tag object that was provided or created will now have the updated data if the operation succeeded.

Console.WriteLine($"Success: {response.Success}");
Console.WriteLine($"Result: {response.Result}");
Console.WriteLine($"Timestamp: {response.Timestamp}");
Console.WriteLine($"Duration: {response.Duration}");
Console.WriteLine($"Value: {response.Tags.First().Value}");

Another spot where this library shines is reading collections of tags. Since we can query an L5X using LINQ and pass that collection to the client, we never even need to know the tag name or type at all. For example, we can read all tags in a certain program:

var tags = content.Query<Tag>(t => t.Scope.Container == "MyProgram");
var response = await client.ReadTags(tags);

Tag Writing

Use the client object to write a known tag by name and type, providing an update action.

var response = await client.WriteTag<TIMER>("SomeTimer", d =>
{
    d.PRE = 12345;
    d.ACC = 123;
    d.EN = true;
});

Or pass in your own Tag object to write. We can either create an in-memory tag instance or get an existing one from an L5X file.

// Create tag in memory using builder API. 
// Use the "Program" prefix to specify this as a program tag.
var tag = Tag.Create("Program:SomeProgram.SomeTag")
    .WithValue(123)
    .Build();

var response = await client.WriteTag(tag);

Tag Response

All read/write operations will return a single aggregate TagResponse object providing details on the result of the operation. The response object contains the following data:

  • Success: A bool flag indicating whether the operation completed without errors.
  • Result: A TagStatus enumeration value indicating the first error encountered if any occurred, or an "Ok" status otherwise.
  • Timestamp: A DateTime value indicating the time at which the response was generated.
  • Duration: A TimeSpan value indicating how long the operation took to process.
  • Tags: The collection of resulting Tag instances with updated data. Note that these are the same tag instances passed to the client. The client is not creating new instances to return.
  • Errors: A collection of TagError objects detailing which error was encountered for a specific tag name (member of the provided tag structure). This helps identify which tags have issues.

Tag Life Cycle & Mutability

L5Sharp.Gateway uses a stateful API design. The Tag instances you pass to the client are the same instances that get updated when an operation completes. The client does not create new Tag objects for results; it updates the internal LogixData buffer of your existing objects. This ensures that if you have a Tag bound to a UI element or a logic engine, it stays synchronized with the PLC automatically after a read. While the PlcClient manages its internal handles safely, you should ensure that you aren't modifying a Tag's value in one thread while the PlcClient is performing a Write operation on it in another.

Tag Monitoring

The libplctag library lets you configure tags for periodic read operations to stream data from the controller. This library leverages that feature using a tag watch API. To monitor a tag for changes, call WatchTag and provide a callback delegate.

var tag = Tag.New<DINT>("MyDintTag");

using var writeToConsole = await client.WatchTag(tag, t =>
    Console.WriteLine($"Tag: {t.Name} | Value: {t.Value}")
);

This will stream updated tag data into the provided tag instance while the watch is in memory. The callback is invoked with the updated tag instance. The watch methods return an IDisposable which will stop polling when disposed.

Client Configuration

Users can configure the client using the PlcOptions object. Along with IP and slot, you can configure various other settings, such as timeout and read interval.

var options = new PlcOptions
{
    IP = "10.10.10.10",
    Slot = 2,
    // Duration in milliseconds before timeout error occurs. Default is 5000
    Timeout = 30000,
    // Rate in milliseconds at which to poll for tag updates (only applies to Watch API). Default is 1000.
    ReadInterval = 100,
    // Which result statuses to throw exceptions for. Default is none.
    ThrowOn = { TagStatus.BadData, TagStatus.BadConnection, TagStatus.Timeout }
};

// Create the client with the configured options.
using var client = new PlcClient(options);

Tag Service

This library contains an interface wrapper called ITagService that mimics the native library API (but is reduced to only the methods that are used by PlcClient). This lets us inject a mock service implementation to the client in cases where we don't have PLC hardware available. This library also includes a default VirtualTagService implementation that mimics the native service, but uses a provided L5X as the backing store for reading/writing tag data. When using the virtual tag service, all operations will effectively read from or write to the underlying L5X, which would in theory mimic whatever PLC you would be communicating with.

// This creates a new virtual tag service by loading the specified L5X and confiuring a fake latency duration.
var tagService = VirtualTagService.Upload(@"C:\Path\To\MyFile.L5X", TimeSpan.FromMilliseconds(10));

// You can inject this or any other mock service into the client constructor.
using var client = new PlcClient(options, tagService);

Feedback & Support

We welcome your feedback, bug reports, and feature requests to help improve L5Sharp.Gateway.

Reporting Issues

If you encounter a bug or have a feature request, please open an issue on our GitHub repository:

When reporting issues, please include:

  • A clear description of the problem or feature request
  • Steps to reproduce (for bugs)
  • Expected vs actual behavior
  • Your environment details (PLC model, .NET version, library version)
  • Sample code or L5X files if applicable

Contributing

We appreciate contributions from the community! If you'd like to contribute:

  • Fork the repository and create a feature branch
  • Submit pull requests with clear descriptions of changes
  • Follow the existing code style and conventions
  • Include tests for new functionality

License

L5Sharp.Gateway is licensed under the MIT License – see the LICENSE file for details.

Third-Party Licenses

The library uses libplctag for PLC communication, which is licensed under the Mozilla Public License 2.0 (MPL 2.0).

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-beta.1 41 1/19/2026