164 lines
6.9 KiB
Markdown
164 lines
6.9 KiB
Markdown
# AB Legacy — RSLogix symbol & data-table import
|
|
|
|
ablegacy-11 / [#254](https://github.com/dohertj2/lmxopcua/issues/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](https://github.com/dohertj2/lmxopcua/issues/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`.
|
|
|
|
```powershell
|
|
# 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`
|
|
|
|
```powershell
|
|
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`.
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```csharp
|
|
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** — `AddRsLogixImport` concatenates onto the
|
|
existing `Tags` list 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 `AddRsLogixImport` twice 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
|
|
a `replace=true` switch.
|
|
- Description metadata is dropped on the floor — see the column reference
|
|
above. When [#248](https://github.com/dohertj2/lmxopcua/issues/248) lands a
|
|
`Description` field on `AbLegacyTagDefinition` the importer will start
|
|
populating it without further changes to the CSV contract.
|