Allen-Bradley Logix
Your Logix code lives in git as text. Every change is diffed and reviewed. CI verifies it. The controller is checked for drift against the repo. You watch live values and write setpoints from your editor.
Nothing about the runtime changes. Logix stays the controller and the program of record; nautilus supplies the software engineering around it. (The other direction, writing the logic in nautilus and generating the Logix project from it, is Authoring for Logix, which is experimental.)
From an .ACD to live rungs, in three steps
Section titled “From an .ACD to live rungs, in three steps”The whole path, start to finish. Each step names the release it needs;
install the latest naut and every
step works.
1. Convert the project to text: naut logix convert
Section titled “1. Convert the project to text: naut logix convert”Needs: naut v0.11.0 or later, and a logixd
agent on a Windows machine
with a licensed Studio 5000 and the Logix Designer SDK.
An .ACD is a binary database only Logix Designer can open. logixd asks
the SDK to save it as L5X: the file goes up, the L5X comes back, and
naut runs on any OS.
export NAUTILUS_LOGIXD_URL=http://plc-box:8188 # where logixd listensexport NAUTILUS_LOGIXD_TOKEN=... # %ProgramData%\logixd\logixd.token on that machine
naut logix convert DemoLine.ACD DemoLine.L5X
# A whole folder of projects:for f in projects/*.ACD; do naut logix convert "$f" "${f%.*}.L5X"; done--agent and --token set the same two things per command. With neither,
naut tries http://127.0.0.1:8188, so if logixd stays on loopback
(the installer’s default) reach it through an SSH tunnel instead of opening
a port:
ssh -N -L 8188:127.0.0.1:8188 you@plc-box & # Windows OpenSSH Servernaut logix convert DemoLine.ACD DemoLine.L5X # token still requiredlogixd installs with one command on that machine; see Install
it.
2. Import it as a nautilus project: naut logix import --project
Section titled “2. Import it as a nautilus project: naut logix import --project”Needs: naut v0.16.0 or later. No Rockwell software.
naut logix import --project demoline DemoLine.L5Xwrote demoline/MainProgram.ldwrote demoline/nautilus.yamlwrote demoline/tags/logix.yaml1 ladder routine, 1 complete; 2 rungs, 2 carriedEvery ladder routine becomes a nautilus PROGRAM in ladder source, with its
rung comments, its tags’ descriptions and their initial values
(HiLevelSP : REAL := 85.0). The manifest gets target: logix, so
naut check --target logix demoline passes on what came across. A rung
with no nautilus form is kept as a comment and counted under not
carried, never guessed at. What the import covers, and what you do with
the project afterwards, is in Authoring for Logix → Bringing an existing
program in.
3. Watch it run: naut logix serve and the editor
Section titled “3. Watch it run: naut logix serve and the editor”Needs: naut v0.16.1 or later, and the VS Code extension 0.14.0 or
later (stable channel; on the pre-release channel, 0.13.1 or later). An
older extension draws the rungs but shows no values on program-scope tags.
An older naut (from v0.14.0) serves values, but the first write after
half a minute without one is refused with EOF.
naut logix serve --host 10.0.0.20 --l5x DemoLine.L5XSet nautilus.runtimeUrl to http://localhost:8080 and open
DemoLine.L5X. The rungs light up with the controller’s values. Program
tags are served as <Program>_<Tag> (MainProgram_StartPB), and the
ladder view finds them from the rung’s own StartPB. To write a setpoint,
click the pencil on a tag in the Live Values panel (or right-click it,
Set Live Value…); the write goes to the controller. Live values from a running
controller has the details.
No controller to hand? naut logix emulate --l5x DemoLine.L5X serves the
export’s tags as a ControlLogix on 127.0.0.1:44818; point serve at it
with --host 127.0.0.1.
The rest of this page is the reference behind those steps. There are two halves, and the first needs nothing from Rockwell at all.
Half one: reading L5X — no Rockwell software
Section titled “Half one: reading L5X — no Rockwell software”An L5X is the XML Logix Designer writes on File → Export. naut
reads it directly, on any OS:
naut logix info DemoLine.L5X # what is in this export?naut logix import DemoLine.L5X # UDTs -> IEC types, tags -> a tag filenaut logix graph DemoLine.L5X # RLL routines -> the ladder modelnaut logix normalize DemoLine.L5X # pin the attributes that move on every exportTags arrive documented
Section titled “Tags arrive documented”naut logix import DemoLine.L5X --scope '*'# wrote logix_types.st (3 types) and tags/logix.yaml (8 tags)- { name: DwellTmr, role: input, type: TIMER, init: { PRE: 30000, ACC: 0 }, desc: "Dwell before the pump is allowed to restart" }- { name: StartPB, role: input, init: false, desc: "HS-101 start pushbutton" }That desc: is the point. Logix keeps tag documentation in the offline
project file, so a live CIP browse cannot see it — naut eip import
has to leave descriptions empty. Reading the L5X recovers them.
UDTs come across as real IEC types. A Logix UDT has no BOOL members — an
authored BOOL is exported as a hidden byte host plus BIT overlays — so
nautilus drops the hosts and declares the BOOLs you actually wrote:
Analog_Input : STRUCT Raw : INT; EU : REAL; Alarms : Limits; History : ARRAY [0..9] OF REAL; Fault : BOOL;END_STRUCT;Ladder, in your editor
Section titled “Ladder, in your editor”.L5X files open in the ladder view exactly like nautilus .ld source —
contacts, branches, in-rung blocks, coils — and diff as diagrams between
git revisions. Rendering only: nautilus draws the rung Logix exported and
claims nothing about how it executes, which is why an instruction it has
never seen still draws, as a box with its operands.
L5X is read-only in that view. It is a vendor export; edit it in Logix Designer.
Drift detection
Section titled “Drift detection”Every export stamps a new ExportDate, so a raw diff is never empty.
Normalizing pins that and the other volatile attributes, and then two
exports of unchanged code compare equal:
naut logix normalize --check controller.L5X repo.L5XLive values from a running controller
Section titled “Live values from a running controller”naut logix serve puts a running controller behind the same HTTP API a
nautilus controller serves, over EtherNet/IP, so the editor treats the PLC as
if it were one:
naut logix serve --host 10.0.0.20 --l5x repo.L5XThen set nautilus.runtimeUrl to http://localhost:8080. Live values,
hover, the Live Values panel and Set Live Value… now work against the
PLC, and so does the built-in dashboard at that address.
- Tags are discovered, not configured. The controller is browsed at
startup and every user tag it can decode is served, except module I/O
(
--tagsnarrows it with globs such as"Program:MainProgram.*"). Controller-scope tags keep their names. A program-scope tag is served as<Program>_<Tag>, because two programs may each have aSts. --l5xsupplies descriptions. A CIP browse can’t read tag documentation, so hover and the tag table show the descriptions from an export of the running project.- A write goes to the controller. What you see afterwards is the value
the next poll read back, not the value you sent. A write names one value,
so
P101.Runworks but a whole UDT doesn’t. A tag the controller refuses to write returns its refusal. - Program commands say what to use instead. A Logix controller holds no
nautilus source and has nothing to warm-swap. Diff with Controller and
Download explain that, and point at
naut logix driftandnaut logix push.
Run it where the controller is reachable. If only one machine can reach
the controller network, which is often the licensed Windows box, run
naut logix serve there and point the editor at that machine. naut is one
binary for Linux, macOS and Windows, and the path to the controller is still
direct EtherNet/IP.
Why not route values through logixd? The Logix Designer SDK reads and
writes one tag per call, at roughly half a second each by Rockwell’s own
figure. It only does that with the project open and online, while the SDK
holds its licence session. EtherNet/IP reads many tags per request, every
250 ms, and keeps working when the licence doesn’t. logixd stays the path
for project work: build, download, online edits.
This needs no Studio 5000, licence or Windows machine. Writes are open to
anything that can reach the port, just as with naut run. Before listening
anywhere but loopback, set NAUTILUS_TOKEN: a write here changes a running
PLC.
Half two: driving a project — the logixd agent
Section titled “Half two: driving a project — the logixd agent”Creating projects, compiling, downloading and online edits need the
Studio 5000 Logix Designer SDK, which is Windows-only, licensed, and
reachable only from a .NET client on the same machine — it is a gRPC
service bound to 127.0.0.1 with no remote protocol.
So nautilus does not talk to the SDK. It talks to logixd, a small
agent you run on the licensed Windows machine.
your laptop / CI ──HTTPS+token──▶ logixd (Windows) ──gRPC──▶ Studio 5000 SDK nautilus CLI tools/logixd 127.0.0.1:53204What you need on the Windows machine
Section titled “What you need on the Windows machine”| Studio 5000 Logix Designer | v31+; v37+ for build. Projects older than v31 are not supported. |
| Logix Designer SDK | 2.02.00 or later (installs LdSdkService). |
| .NET SDK | 10.x, to build logixd. The one-line install adds a private one if there is none. |
| FactoryTalk Linx | The SDK supports only FactoryTalk Linx — RSLinx is not supported. Your controller must be visible in the FT Linx Network Browser. |
| OS | 64-bit only. |
Install it
Section titled “Install it”In an elevated PowerShell on the licensed machine:
irm https://github.com/joyautomation/nautilus/releases/latest/download/install-logixd.ps1 | iexTo pass options (any of install.ps1’s; -Version 0.16.2 pins a release):
& ([scriptblock]::Create((irm https://github.com/joyautomation/nautilus/releases/latest/download/install-logixd.ps1))) -Listen 0.0.0.0 -Port 8188logixd is published as source, not as a binary. It links the SDK’s .NET
client, which is Rockwell’s and comes only with the SDK install, so it is
built on the machine that has it and nothing of Rockwell’s is ever
redistributed. The script downloads logixd.zip from the release (v0.16.2
and later carry it), installs a private .NET 10 SDK under
C:\Program Files\logixd-dotnet if the machine has none (not on PATH,
used only by logixd), and runs install.ps1 from the zip. From a clone,
run that directly:
cd tools\logixd.\install.ps1 -Listen 0.0.0.0 -Port 8188 # elevated PowerShellThat is the whole setup. It checks the prerequisites, builds logixd, mints
a bearer token, registers it to start at logon, adds a firewall rule, then
starts it and prints the licensing probe gate by gate — so you find out
whether the SDK is actually usable before you trust it, not later.
Re-running it is an upgrade; -Uninstall reverses it. The token lands in
%ProgramData%\logixd\logixd.token, readable by administrators only.
When something isn’t working
Section titled “When something isn’t working”Start with naut logix probe. Every gate that fails now prints what to
do about it, underneath the failure. If the probe itself cannot connect, or
the symptom is in a verb rather than a gate, find it here.
| What you see | What it is | What to do |
|---|---|---|
logixd: unreachable ... connection refused | The agent is not running. After a reboot this is almost always it. | Log in at the machine’s console, then Start-ScheduledTask -TaskName logixd. The agent is an interactive task, not a service — see above. |
logixd: unauthorized: http://plc-box:8188 refused the token; check --token or NAUTILUS_LOGIXD_TOKEN against %ProgramData%\logixd\logixd.token on the agent's machine | The agent answered HTTP 401: wrong bearer token. Re-installing mints a new one. With no token set at all the message reads ... wants a token and none was sent; set --token or NAUTILUS_LOGIXD_TOKEN from .... | Re-read %ProgramData%\logixd\logixd.token and pass it as --token or NAUTILUS_LOGIXD_TOKEN. |
A bare System.TimeoutException from any SDK call, nothing in any log | DOTNET_ROOT is set to an x64 .NET. | Unset it, set DOTNET_ROOT_X64 instead, restart the agent. The installer refuses to proceed while it is set. |
RxCMP_E_AUDIT_INVALIDOPTYPE - Invalid type. on build or download | An instruction the project uses does not exist under that name. The L5X importer does not validate instruction names, so a bad one imports cleanly and only fails at verify. | Check the mnemonic. Neutral text uses the short spellings: GE GT LE LT EQ NE MOVE LIMIT — not the GEQ/GRT/MOV/LIM captions the ladder editor shows. Opening the project in Logix Designer and verifying names the rung and the fix; the SDK does not pass that detail through. |
RxCL_E_CANNOT_UPLOAD_PHYS_ADDR going online | You passed a project file to an online edit. | Don’t — an online edit takes no project file. See Online edits. |
drift reports a difference of megabytes on a project you just downloaded | A detailed export compared against a basic one. | Fixed in current builds: drift matches the repo file’s export kind. If you see it, your CLI is older than your agent. |
build fails on a project Logix Designer opens fine | Usually a real verify error the SDK reports only as a code. | Open the project in Logix Designer and run Verify Controller. Its error list names the rung and the instruction; that is currently the fastest way to a diagnosis. |
If a gate fails and the remedy does not resolve it, naut logix probe --json gives the whole result, including which Logix revisions the agent
found installed.
Check it before you trust it
Section titled “Check it before you trust it”naut logix probe --agent http://plc-box:8188 --token $TOKENok sdk-service LdSdkServer runningok logix-designer installed: v38ok interactive-session a user is logged on at the consoleok live-sdk-call v38 offers 106 processor types, e.g. 1756-L71ok create-project created and closed a v38 1756-L71 projectThe SDK needs several independent things — a FactoryTalk token, a FlexNet
feature, a CodeMeter entitlement — and each fails differently and
uninformatively, with whichever fails first masking the rest. probe
reports them separately so you know which one to fix.
What you can then do
Section titled “What you can then do”# Format conversion, either direction — two SDK calls, no GUI.naut logix convert Plant.ACD Plant.L5Xnaut logix convert Plant.L5X Plant.ACD
# CI for control logic: compile it. No controller, no downtime, no risk.naut logix build Plant.ACD
# Does the controller still match the repo?naut logix drift repo.L5X --comm-path 'AB_ETH-1\10.0.0.5\Backplane\0'
# A warm rung edit on a RUNNING controller.naut logix push Plant.ACD rungs.L5X \ --program MainProgram --routine F07_Alarms \ --at 12 --replace 1 \ --comm-path 'AB_ETH-1\10.0.0.5\Backplane\0' --finalizeDon’t transcribe the comm path out of a GUI. Ask:
naut logix browse# AB_ETH-1\10.0.0.5\Backplane\0 PlantCtl (1756-L85E)browse reads what FactoryTalk Linx has already discovered — it does
not scan the network — and prints the controller sitting on the end of each
path, so you pick the one you meant rather than assembling a string and
hoping. If it finds nothing, browse to the controller once in the FT Linx
Network Browser and try again.
Online edits
Section titled “Online edits”--accept sends the change to the controller; --finalize also assembles
it if the controller is in Run. Both require --comm-path: offline the SDK
ignores the option, so without it the edit would land as pending edits
nobody accepted while the command reported success. nautilus refuses
instead.
An online edit takes no project file:
naut logix push --comm-path 'AB_ETH-1\10.0.0.5\Backplane\0' \ --program MainProgram --routine MainRoutine --at 1 --replace 1 \ --accept rungs.L5XIt uploads the program the controller is running, imports into that, and
sends it back. This is not a convenience — it is the only thing that works.
A project file on disk cannot go online even when its logic matches the
controller byte for byte, because downloading stamps match information into
the project and that copy stays on the machine that did the download. Push
your repo’s own .ACD and the SDK fails with:
RxCL_E_CANNOT_UPLOAD_PHYS_ADDR - Failed to upload physical address information.which is about as far from “wrong project file” as an error message gets.
Pass -o if you want to keep the resulting project locally.
To check the controller still matches your repo, use naut logix drift
— it exits 1 on a mismatch, so it works as a CI gate.
Security
Section titled “Security”logixd can stop a controller. Treat it accordingly.
- Loopback by default. Binding a routable address requires a token, and the agent refuses to start without one.
LOGIXD_COMM_ALLOWis agent-side configuration, never a request parameter: a pipeline cannot name a controller the operator has not blessed.- Every path the agent touches is confined to
LOGIXD_WORKDIR, compared after full resolution so traversal is rejected rather than string-matched. downloadnever changes the controller mode unless explicitly asked.
None of that makes it safe to expose to a plant network. It is an agent for a trusted host on a trusted link.
What nautilus will not pretend to do
Section titled “What nautilus will not pretend to do”A download stops the controller and resets tags to project values, and the SDK — unlike the GUI — neither changes the mode for you nor checks that it is right. Gate it: a reviewed pipeline, an agent-side allowlist, and, best of all, a permissive tag operators set from the HMI.
Byte-exact round-tripping of a hand-edited L5X is not offered, and nautilus does not claim a generated rung and a hand-written one compute the same thing. Where something cannot be done, the tooling says so rather than imitating it.