6.9 KiB
AB Legacy — RSLogix symbol & data-table import
ablegacy-11 / #254 — bulk-import
RSLogix 500 / 5 symbol exports into the AB Legacy driver. Saves operators from
hand-typing every N7:0 / F8:12 / B3:0/5 row of a several-hundred-tag PLC
into appsettings.json.
Supported formats — v1
| Format | Status | Notes |
|---|---|---|
.CSV "Database Export" |
supported | Header columns Symbol,Address,Description,DataType,Scope; quoted fields, doubled-quote escapes, comment lines (; / #) all honoured |
.SLC text export |
supported | RSLogix 500's "Save As Text" emits the same column shape — point the importer at the file directly |
.RSS (RSLogix 500 binary project) |
out of scope | Proprietary; no parser ships in libplctag or any community project. Export to CSV first |
.RSP (RSLogix 5 binary project) |
out of scope | Same as .RSS |
The binary .RSS / .RSP non-goal isn't a "we don't have time" decision —
Rockwell's binary format is undocumented + tied to RSLogix's internal page
layout, and the only known parsers are commercial IDE plugins. v1 ships with
text/CSV only and a clean abstraction (IRsLogixImporter) so a binary parser
can slot in later without reshaping the call sites.
CSV column reference
| Column | Required | Notes |
|---|---|---|
Symbol |
yes | OPC UA tag name. RSLogix symbols are already stable; the importer uses them verbatim |
Address |
yes | PCCC address. File letter implies DataType (see below); the importer's resolution wins over the CSV's DataType column |
Description |
no | Parsed but currently unused — AbLegacyTagDefinition has no Description field at the v2 schema layer (see #248). Held in the column contract for future schema bumps |
DataType |
no | RSLogix-supplied (INT / REAL / BOOL / TIMER / …). Ignored at import time; the importer derives the type from the file letter |
Scope |
no | Global (default when blank) or Local:N for ladder-file-N-scoped tags. Acts as a filter when --scope is set on the CLI |
File-letter → AbLegacyDataType mapping
| Letter | Example | Maps to | Notes |
|---|---|---|---|
N |
N7:0 |
Int (signed 16-bit) |
|
F |
F8:0 |
Float (32-bit IEEE-754) |
|
B |
B3:0/0 |
Bit |
Bit-within-word also forces Bit when BitIndex is set |
L |
L9:0 |
Long (signed 32-bit) |
SLC 5/05+ only |
ST |
ST10:0 |
String |
82-byte fixed-length + length word |
T |
T4:0.ACC |
TimerElement |
Sub-element implied by .ACC / .PRE / .EN / .DN |
C |
C5:0.ACC |
CounterElement |
|
R |
R6:0.LEN |
ControlElement |
|
A |
A14:0 |
AnalogInt |
Older hardware |
I / O / S |
I:0/0 |
Int (or Bit with bit suffix) |
I/O + status files |
PD / MG / PLS / BT |
PD9:0 |
PidElement etc. |
Family-gated; PD/MG common on SLC500 + PLC-5; PLS/BT PLC-5 only |
RTC / HSC / DLS / … |
RTC:0.YR |
MicroLogixFunctionFile |
MicroLogix 1100 / 1400 only |
A bit suffix (/N) on any file letter forces Bit, regardless of the file
letter's normal classification — N7:0/3 parses as Bit, not Int.
Scope filter
The Scope column distinguishes program-scoped tags (Local:1, Local:2, …)
from globals. RSLogix exports usually mix both. The CLI's --scope flag (and
ImportOptions.ScopeFilter at the API level) keeps only the rows whose
Scope value matches case-insensitively; rows with no Scope column count as
Global.
# Import only the Global symbols
otopcua-ablegacy-cli import-rslogix `
--file plc-export.csv `
--device ab://192.168.1.20/1,0 `
--scope Global
# Import only the file-2 program-scope tags
otopcua-ablegacy-cli import-rslogix `
--file plc-export.csv `
--device ab://192.168.1.20/1,0 `
--scope Local:2
CLI subcommand — import-rslogix
otopcua-ablegacy-cli import-rslogix --help
| Flag | Default | Purpose |
|---|---|---|
-f / --file |
required | Path to the CSV export |
-d / --device |
required | Canonical AB Legacy gateway URI every imported tag binds to |
--emit |
appsettings-fragment |
appsettings-fragment (JSON) or summary (one-line counter) |
-o / --output |
stdout | Optional path; when set the JSON fragment is written there + summary line goes to stdout |
--scope |
none | Optional Scope filter (case-insensitive) |
--max-rows |
unlimited | Defensive cap on rows imported |
--strict |
off | Fail-fast on the first malformed row (default permissive: skip + log) |
appsettings-fragment output shape
The default --emit appsettings-fragment mode writes a JSON object whose
Tags array is shaped like the AbLegacyDriverConfigDto.Tags array — paste
straight into the driver-instance config under
Drivers/<instance>/Config/Tags.
{
"Tags": [
{
"Name": "MotorSpeed",
"DeviceHostAddress": "ab://192.168.1.20/1,0",
"Address": "N7:0",
"DataType": "Int",
"Writable": true
},
…
]
}
Summary line
--emit summary writes a single line:
Imported 142 tag(s), skipped 3, errors 0.
Skipped covers Scope-filter rejections + missing-required-field rows; errors
covers rows whose Address failed to parse as a PCCC address.
API surface — IRsLogixImporter + AddRsLogixImport
For server-side / bootstrap use-cases the importer is also reachable via:
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Import;
var options = new AbLegacyDriverOptions
{
Devices = [new AbLegacyDeviceOptions("ab://192.168.1.20/1,0")],
};
// Append imported tags onto an existing options object.
var updated = options.AddRsLogixImport(
path: @"C:\plc\plc-export.csv",
deviceHostAddress: "ab://192.168.1.20/1,0",
out var result);
// result.ParsedCount / SkippedCount / ErrorCount surface the import telemetry.
Console.WriteLine($"Imported {result.ParsedCount} tags");
For a hand-managed importer instance (e.g. supplying a custom ILogger) call
new RsLogixSymbolImport(logger).Parse(stream, deviceHostAddress, opts)
directly.
Operational notes
- The importer is additive —
AddRsLogixImportconcatenates onto the existingTagslist rather than replacing it. Hand-rolled tags (system-status variables, computed fields the operator added by hand) survive a re-import. - Re-imports are not idempotent today — calling
AddRsLogixImporttwice will produce duplicate tag rows. Operators are expected to either start from a clean options object or de-duplicate themselves; a future schema rev may add areplace=trueswitch. - Description metadata is dropped on the floor — see the column reference
above. When #248 lands a
Descriptionfield onAbLegacyTagDefinitionthe importer will start populating it without further changes to the CSV contract.