ABPlcRx 2.0.54
dotnet add package ABPlcRx --version 2.0.54
NuGet\Install-Package ABPlcRx -Version 2.0.54
<PackageReference Include="ABPlcRx" Version="2.0.54" />
<PackageVersion Include="ABPlcRx" Version="2.0.54" />
<PackageReference Include="ABPlcRx" />
paket add ABPlcRx --version 2.0.54
#r "nuget: ABPlcRx, 2.0.54"
#:package ABPlcRx@2.0.54
#addin nuget:?package=ABPlcRx&version=2.0.54
#tool nuget:?package=ABPlcRx&version=2.0.54
<p align="left"> <a href="https://github.com/ChrisPulman/ABPlcRx"> <img alt="ABPlcRx" src="Images/logo.png" width="200"/> </a> </p>
ABPlcRx
A reactive Allen‑Bradley PLC client built on top of libplctag. It provides a simple, high‑performance, reactive API for reading/writing tags from Rockwell/Allen‑Bradley controllers.
Warning – Disclaimer PLCs control equipment. Mistakes can cause loss of property, production, or life. Use extreme caution. No warranty of suitability is provided.
Supported PLC families (via libplctag)
- ControlLogix/CompactLogix (LGX) over CIP EtherNet/IP
- Micro800 family where supported by libplctag
- PLC‑5, SLC 500, MicroLogix (Ethernet/ENI/DH+ bridging where supported)
- Additional families supported by libplctag may be usable
Core features
- Create tags and group them for bulk operations
- Reactive APIs (IObservable) for on‑change updates
- Async reactive APIs (IObservableAsync) through ReactiveUI.Extensions on .NET 8+
- Read/write primitives: 8/16/32/64‑bit signed/unsigned, 32/64‑bit float
- Bit addressing helpers for coil/word bits
- String and structure support (libplctag style)
- Bulk read/write across groups
- Health monitoring (Ping/ObservePing)
- Source generator attributes for typed PLC stream models
- TUnit tests running on Microsoft Testing Platform
Getting started Installation
- Install the NuGet package:
- Package Manager: Install-Package ABPlcRx
- .NET CLI: dotnet add package ABPlcRx
- The ABPlcRx package includes its source generator analyzer; no separate generator package is required for normal NuGet consumption.
- ABPlcRx depends on libplctag; the NuGet dependency brings required bindings.
Basic concepts
- Variable: your app’s key for a tag (free‑form string)
- TagName: the PLC’s address/name for the tag (e.g., B3:3, N7:0, MyTag)
- TagGroup: logical group to batch operations (e.g., “Default”, “Motion”)
- Types and bits: to read/write a bit, create a tag as short (Int16) and use bit index 0‑15
Quick start
using ABPlcRx;
using System;
using System.Reactive.Disposables;
var disposables = new CompositeDisposable();
// SLC/PLC5/MicroLogix example (500ms scan)
var slc = new ABPlcRx(PlcType.SLC, "192.168.1.50", TimeSpan.FromMilliseconds(500));
disposables.Add(slc);
// Create a word tag and use bit addressing (B3:3/0)
slc.AddUpdateTagItem<short>("LightOn", "B3:3", "Default");
// Observe changes (bool via bit 0)
disposables.Add(
slc.Observe<bool>("LightOn", bit: 0)
.Subscribe(v => Console.WriteLine($"LightOn = {v}"))
);
// Toggle the bit and write
var current = !slc.Value<bool>("LightOn", bit: 0);
slc.Value("LightOn", current, bit: 0); // AutoWriteValue=true writes immediately
Console.WriteLine($"Wrote {current} -> B3:3/0");
ControlLogix/CompactLogix (LGX) example
// For LGX you must provide a path (default "1,0" = backplane, slot 0)
var lgx = new ABPlcRx(PlcType.LGX, "192.168.1.60", TimeSpan.FromMilliseconds(200),
timeOut: TimeSpan.FromSeconds(2), path: "1,0");
// Controller tag named MyDINT
lgx.AddUpdateTagItem<int>("Counter", "MyDINT", "Default");
// Observe numeric values
lgx.Observe<int>("Counter").Subscribe(v => Console.WriteLine($"Counter={v}"));
// Increment and write
lgx.Value("Counter", lgx.Value<int>("Counter") + 1);
// Standard Logix BOOL tag named MachineReady
lgx.AddUpdateTagItem<bool>("Ready", "MachineReady", "Default");
lgx.Observe<bool>("Ready").Subscribe(v => Console.WriteLine($"Ready={v}"));
var ready = !lgx.Value<bool>("Ready");
lgx.Value("Ready", ready);
Reactive API highlights
- Observe<T>(variable, bit = -1): stream values on change, supports late‑added tags
- ObserveAsync<T>(variable, bit = -1): async-native stream using ReactiveUI.Extensions.Async on .NET 8+
- ObserveMany(params string[] variables): latest values as a dictionary
- ObserveManyAsync(params string[] variables): async-native latest value dictionaries
- ObserveGroup(groupName): emits tag objects in a group when they change
- ObserveGroupAsync(groupName): async-native group stream
- ObserveSampled<T>(variable, sampleInterval, bit, scheduler): sampled stream for rate limiting
- ObserveSampledAsync<T>(variable, sampleInterval, bit, scheduler): async-native sampled stream
- ObserveErrors(): only tag operations that returned an error
- ObserveErrorsAsync(): async-native error stream
- CreateWriter<T>(variable, bit): returns an IObserver<T> that writes on OnNext
Async observables
using ReactiveUI.Extensions.Async;
var counter = lgx.ObserveAsync<int>("Counter");
// IObservableAsync<T> can use ReactiveUI.Extensions.Async operators,
// including Select, Where, Merge, CombineLatest, Retry, Timeout, Publish,
// ReplayLatest, ToObservable, and ToObservableAsync.
var activeCounter =
counter
.Where(value => value > 0)
.Select(value => $"Counter={value}");
Source generated stream models
using ABPlcRx.SourceGeneration;
[PlcModel]
[PlcTag(typeof(int), "Counter", "MyDINT")]
[PlcTag(typeof(bool), "LightOn", "B3:3", Bit = 0)]
public partial class MachineTags
{
}
var tags = new MachineTags();
using var binding = tags.AttachPlcStreams(slc);
tags.CounterObservable.Subscribe(value => Console.WriteLine($"Counter={value}"));
tags.LightOnObservable.Subscribe(value => Console.WriteLine($"LightOn={value}"));
// On .NET 8+ the generator also emits IObservableAsync<T> streams.
var asyncLightOn = tags.LightOnObservableAsync;
The generator creates a property for each class-level PlcTag attribute, registers tags in AttachPlcStreams, keeps the generated property updated from the observable subscription, and exposes both PropertyNameObservable and PropertyNameObservableAsync on .NET 8+. Boolean tags without a Bit value are registered as native bool tags. Boolean bit tags with Bit set are registered as short tags and exposed as bool values.
Examples Observe multiple variables
// Emits { "LightOn": true, "Counter": 42 }
slc.ObserveMany("LightOn", "Counter")
.Subscribe(dict => Console.WriteLine(string.Join(", ", dict.Select(kv => $"{kv.Key}={kv.Value}"))));
Group operations and bulk I/O
// Group creation is implicit via AddUpdateTagItem
slc.AddUpdateTagItem<short>("Alarm", "B3:10", "Safety");
slc.AddUpdateTagItem<short>("Guard", "B3:11", "Safety");
// Bulk read/write across all groups
var results = slc.Read();
var wrote = slc.Write();
Health monitoring
// One‑off ping
var ok = slc.Ping();
// Observe ping results every 2 seconds
slc.ObservePing(TimeSpan.FromSeconds(2))
.Subscribe(alive => Console.WriteLine($"PLC reachable: {alive}"));
Advanced: writing with an observer
var writer = slc.CreateWriter<bool>("LightOn", bit: 0);
writer.OnNext(true); // writes and commits
Configuration and options
- ScanEnabled: enable/disable background scanning by group
- AutoWriteValue: when true (default), setting Value(tag) writes immediately
- Timeout: communications timeout (ms) via constructor timeOut
- Groups: use the tagGroup parameter to logically separate tags
API surface (high level)
- ABPlcRx (implements IABPlcRx)
- AddUpdateTagItem<T>(variable, tagName, tagGroup = "Default")
- Observe<T>(variable, bit = -1)
- ObserveAsync<T>(variable, bit = -1) on .NET 8+
- ObserveMany(params string[] variables)
- ObserveManyAsync(params string[] variables) on .NET 8+
- ObserveGroup(groupName)
- ObserveGroupAsync(groupName) on .NET 8+
- ObserveSampled<T>(variable, sampleInterval, bit = -1, scheduler = null)
- ObserveSampledAsync<T>(variable, sampleInterval, bit = -1, scheduler = null) on .NET 8+
- ObserveErrors()
- ObserveErrorsAsync() on .NET 8+
- CreateWriter<T>(variable, bit = -1)
- Value<T>(variable, bit = -1) / Value<T>(variable, value, bit = -1)
- Read()/Read(variable) and Write()/Write(variable)
- Ping(bool echo = false), PingAsync(...), ObservePing(interval,...)
- ObservePingAsync(interval,...) on .NET 8+
Testing
- Tests are in
src/ABPlcRx.Tests. - The suite uses TUnit with Microsoft Testing Platform;
global.jsonsets"test": { "runner": "Microsoft.Testing.Platform" }. - Run all tests with:
dotnet test src/ABPlcRx.sln
Data types and bit access
- Logix/CompactLogix/Micro800 standard
BOOLtags can be created directly withAddUpdateTagItem<bool>("Ready", "MachineReady")and read or written withValue<bool>("Ready"). - To treat a single bit in an SLC/PLC word as a boolean, create the tag as
shortand use thebitparameter (0‑15). - For numeric tags use C# primitive types: sbyte/byte/short/ushort/int/uint/long/ulong/float/double.
- Strings and structure types are supported where the PLC and libplctag support them.
Error handling
- Each read/write yields a PlcTagResult with StatusCode (see PlcTagStatus).
- If
FailOperationRaiseExceptionis set true on the underlying controller, failed operations will throwPlcTagException.
Performance notes
- Tags are grouped internally; bulk
Read()/Write()iterate groups for fewer round trips. - Tag lookups are cached; prefer consistent
variablekeys. - Use
ObserveSampledto reduce update rates to UI or logs.
Troubleshooting
- LGX controllers require a valid
path(e.g., "1,0" for backplane/slot0). - Ensure your PLC networking, firewall, and CIP routes are reachable from your app host.
- Use
Ping()/ObservePing()to monitor reachability.
License MIT. See LICENSE.
ABPlcRx - Empowering Industrial Automation with Reactive Technology ⚡🏭
| Product | Versions 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 is compatible. 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 is compatible. 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 is compatible. 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. |
-
.NETStandard 2.0
- libplctag.NativeImport (>= 1.0.41)
- System.Reactive (>= 6.1.0)
-
net10.0
- libplctag.NativeImport (>= 2.0.0-alpha.8)
- ReactiveUI.Extensions (>= 4.0.0)
- System.Reactive (>= 6.1.0)
-
net8.0
- libplctag.NativeImport (>= 2.0.0-alpha.8)
- ReactiveUI.Extensions (>= 4.0.0)
- System.Reactive (>= 6.1.0)
-
net9.0
- libplctag.NativeImport (>= 2.0.0-alpha.8)
- ReactiveUI.Extensions (>= 4.0.0)
- System.Reactive (>= 6.1.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on ABPlcRx:
| Package | Downloads |
|---|---|
|
MQTTnet.Rx.ABPlc
Reactive extensions for MQTTnet Broker |
GitHub repositories
This package is not used by any popular GitHub repositories.
Compatability with Net 8/ 9/ 10 and netstandard2.0