From 5e79c983d8d11a51ba84f88e6b4b9c7c6d1c51bd Mon Sep 17 00:00:00 2001 From: ssweber <57631333+ssweber@users.noreply.github.com> Date: Mon, 2 Mar 2026 13:27:10 -0500 Subject: [PATCH 001/179] feat(ladder): Add ladder module for Click PLC clipboard encode/decode Extract working prototype from clickplc-tools/exploring/runggrid_construct.py into self-contained clicknick.ladder module (model/codec/clipboard layers). Template-based encoding confirmed working for 4-char operands (X###, Y###). Includes HANDOFF.md (v7) with stream-based format discovery: operand strings are variable-length UTF-16LE and all downstream offsets shift accordingly. Fixed coil func code offset (+0x2E not +0x30). Co-Authored-By: Claude Opus 4.6 --- HANDOFF.md | 436 ++++++++++++++++++ pyproject.toml | 5 +- src/clicknick/ladder/__init__.py | 15 + src/clicknick/ladder/clipboard.py | 73 +++ src/clicknick/ladder/codec.py | 155 +++++++ src/clicknick/ladder/model.py | 115 +++++ .../ladder/resources/NO_X002_coil.AF.bin | Bin 0 -> 8192 bytes tests/ladder/__init__.py | 0 tests/ladder/test_codec.py | 92 ++++ tests/ladder/test_model.py | 94 ++++ 10 files changed, 984 insertions(+), 1 deletion(-) create mode 100644 HANDOFF.md create mode 100644 src/clicknick/ladder/__init__.py create mode 100644 src/clicknick/ladder/clipboard.py create mode 100644 src/clicknick/ladder/codec.py create mode 100644 src/clicknick/ladder/model.py create mode 100644 src/clicknick/ladder/resources/NO_X002_coil.AF.bin create mode 100644 tests/ladder/__init__.py create mode 100644 tests/ladder/test_codec.py create mode 100644 tests/ladder/test_model.py diff --git a/HANDOFF.md b/HANDOFF.md new file mode 100644 index 0000000..07eff4e --- /dev/null +++ b/HANDOFF.md @@ -0,0 +1,436 @@ +# Click PLC Clipboard Reverse Engineering — Handoff v7 + +## Goal + +Reverse engineer Click Programming Software's clipboard format so pyrung (a Python ladder logic framework) can generate clipboard-ready bytes for paste-into-Click. The intermediate representation between pyrung and clipboard bytes is called **RungGrid**. + +## Status + +**WORKING MODULE in ClickNick. Template-based paste confirmed working for 4-char operands.** + +The `clicknick.ladder` module encodes/decodes clipboard bytes and can paste rungs +into Click Programming Software. Tested with NO/NC contacts and Out coils using +4-char operands (X###, Y###). Read/write round-trip confirmed working. + +**Key discovery:** The clipboard format uses a **variable-length stream**, not fixed +cells. Operand strings are stored as raw UTF-16LE and all downstream offsets shift +based on operand length. Current codec only handles 4-char operands. See finding 17. + +## Module Location + +`src/clicknick/ladder/` — self-contained, no imports from ClickNick's models/services/data/views. + +```python +from clicknick.ladder import ClickCodec, RungGrid, read_from_clipboard, copy_to_clipboard + +# Read from Click clipboard +raw = read_from_clipboard() +grid = ClickCodec().decode(raw) +print(grid.to_csv()) # "X006,->,:out(Y006)" + +# Encode and paste +grid = RungGrid.from_csv("X001,->,:out(Y001)") +copy_to_clipboard(ClickCodec().encode(grid)) +``` + +Tests: `tests/ladder/test_model.py`, `tests/ladder/test_codec.py` + +## Exploration Tools (in clickplc-tools repo) + +### dump_click_clipboard.py +Main capture/diff/report tool. +``` +uv run dump_click_clipboard.py capture