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
- 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)
Getting started Installation
- Install the NuGet package:
- Package Manager: Install-Package ABPlcRx
- .NET CLI: dotnet add package ABPlcRx
- 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
glx.Observe<int>("Counter").Subscribe(v => Console.WriteLine($"Counter={v}"));
// Increment and write
glx.Value("Counter", lgx.Value<int>("Counter") + 1);Reactive API highlights
- Observe(variable, bit = -1): stream values on change, supports late‑added tags
- ObserveMany(params string[] variables): latest values as a dictionary
- ObserveGroup(groupName): emits tag objects in a group when they change
- ObserveSampled(variable, sampleInterval, bit, scheduler): sampled stream for rate limiting
- ObserveErrors(): only tag operations that returned an error
- CreateWriter(variable, bit): returns an IObserver that writes on OnNext
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 commitsConfiguration 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(variable, tagName, tagGroup = "Default")
- Observe(variable, bit = -1)
- ObserveMany(params string[] variables)
- ObserveGroup(groupName)
- ObserveSampled(variable, sampleInterval, bit = -1, scheduler = null)
- ObserveErrors()
- CreateWriter(variable, bit = -1)
- Value(variable, bit = -1) / Value(variable, value, bit = -1)
- Read()/Read(variable) and Write()/Write(variable)
- Ping(bool echo = false), PingAsync(...), ObservePing(interval,...)
Data types and bit access
- To treat a single bit 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 ⚡🏭