Adds what the folder was missing for someone picking it up cold.
as-built/ the STruC++ output copied out of the live container - the C++
the PLC is actually executing, plus program.st and the Modbus
buffer config. The running image was made with docker commit
and exists in no registry, so this is the only other copy of
the compiled form.
Verified while copying: the deployed program.st matches
build/wrps.st generated from src/. Identical POU structure,
ZERO differences in non-declaration lines. src/ is genuinely
canonical and the running PLC agrees with it.
VERSIONS.md every version read from the running system, not from
documentation: runtime v4.1.10, STruC++ 0.6.2, Editor
4.2.11, Debian 12, g++ 12.2, pymodbus 3.11.2, CI
Server R1.03. Plus which plugins are enabled -
ethercat is on for no reason - and a v3-vs-v4 table,
since most OpenPLC guidance online is for v3 and the
%MW HR1024 change silently produces wrong data.
GETTING-STARTED.md three questions answered in a page: how to modify the
program, how to stand up a new PLC container from
scratch, how to move this one. Includes the compose
file, the Editor steps that are not generated and are
always missed, and a symptom-to-document table.
73 lines
3.4 KiB
Markdown
73 lines
3.4 KiB
Markdown
# Software and versions
|
||
|
||
Everything below was **read from the running system on 2026-09-02**, not taken
|
||
from documentation. Where a version matters — where a different one behaves
|
||
differently — that is said explicitly.
|
||
|
||
## The PLC
|
||
|
||
| Component | Version | Where it runs | How to check |
|
||
|---|---|---|---|
|
||
| **OpenPLC Runtime** | **v4.1.10** | `openplc-runtime` container on `yau-sls-poc-lin001` | `docker exec openplc-runtime cat /workdir/VERSION` |
|
||
| Docker image | `openplc-runtime-migrated:v4.1.10` | local to the host, **not in any registry** | `docker images` |
|
||
| Base OS in container | Debian 12 (bookworm) | | `docker exec openplc-runtime cat /etc/os-release` |
|
||
| Python (runtime + plugins) | 3.11.2 | | `docker exec openplc-runtime python3 --version` |
|
||
| g++ | 12.2.0 | used to build the PLC program | `docker exec openplc-runtime g++ --version` |
|
||
| GNU Make | 4.3 | drives `scripts/Makefile.strucpp` | |
|
||
| pymodbus (in the slave plugin) | 3.11.2 | | `docker exec openplc-runtime ./venvs/modbus_slave/bin/pip list` |
|
||
|
||
### Runtime plugins
|
||
|
||
`/workdir/plugins.conf` — the third field is enabled (1) / disabled (0):
|
||
|
||
| Plugin | Enabled | Note |
|
||
|---|---|---|
|
||
| `modbus_slave` | **1** | This is the one that matters. It is what makes the PLC a Modbus **server** on 502. |
|
||
| `modbus_master` | 0 | Correct — the PLC must not poll anyone. |
|
||
| `opcua` | 0 | |
|
||
| `s7comm` | 0 | |
|
||
| `ethercat` | **1** | ⚠️ Enabled for no reason. It came from an `ethercat.json` the Editor project emits by default (the file is 0 bytes). Harmless so far, but it is a plugin running with nothing to do. |
|
||
|
||
## The toolchain
|
||
|
||
| Component | Version | Notes |
|
||
|---|---|---|
|
||
| **OpenPLC Editor** | **v4.2.11** (AppImage) | Needs `--no-sandbox` (Electron) and `libfuse2t64`. ⚠️ **Not currently installed anywhere** — see `DEPLOY.md` §0. |
|
||
| **STruC++** | **v0.6.2** | The ST → C++ compiler. **Ships only inside the Editor AppImage**; there is no standalone binary. This is the single reason the Editor is unavoidable. |
|
||
|
||
## The SCADA
|
||
|
||
| Component | Version | Where |
|
||
|---|---|---|
|
||
| **Yokogawa CI Server** | R1.03 | `yau-poc-cicore1` (`10.0.0.21`) |
|
||
|
||
## Versions that actually matter
|
||
|
||
**OpenPLC Runtime v3 → v4 is a breaking change, and most guidance you'll find
|
||
online is for v3.**
|
||
|
||
| | v3 | **v4 (what we run)** |
|
||
|---|---|---|
|
||
| ST compiler | MatIEC | **STruC++** |
|
||
| Accepts a flat `.st` upload | yes | **no** — `scripts/compile.sh` explicitly rejects MatIEC files (`Config0.c`, `glueVars.c`) |
|
||
| Web UI on the runtime | yes | **no** — REST API only, port 8443, JWT |
|
||
| Editor project format | one file | **a folder** — `project.json` + one file per POU |
|
||
| `%MW` Modbus mapping | starts at HR 0 | **starts at HR 1024** (`%QW` occupies 0–1023) |
|
||
|
||
That last row is the one that silently produces wrong data rather than an error.
|
||
See `register-map.csv`, which carries resolved addresses.
|
||
|
||
**The Modbus buffer layout is configuration, not a constant.** From
|
||
`core/generated/conf/modbus_slave.json` in the running container:
|
||
|
||
```json
|
||
"holding_registers": { "qw_count": 1024, "mw_count": 1024, ... }
|
||
```
|
||
|
||
`qw_count: 1024` is *why* `%MW0` is holding register 1024. Change that file and
|
||
every SCADA address moves.
|
||
|
||
**CI Server R1.03 vs R1.05.** One R1.03 behaviour is load-bearing here: an item
|
||
import is rejected if two items share an `IO_ADDRESS` (`EQP-E-DUP_ITEM`). The SCADA
|
||
design works around it by publishing one unit per register. If you move to R1.05,
|
||
re-check that before assuming the workaround is still needed.
|