ProjectWriter¶
File: tckit/ports/writer.py
Purpose: Structural writes that go through the IDE's authoring interface, not raw file edits.
| Method | Returns |
|---|---|
open_project(solution_path) |
Result |
create_project(name, path) |
Result |
add_plc_project(sln_path, plc_name, *, project_type='standard') |
Result |
save_plc_as_library(plc_name, output_path, *, install=True, repository='System', overwrite=False) |
Result |
add_library_reference(consumer_plc_name, library_name, *, version='*', distributor='Tc3 Project') |
Result |
add_library_placeholder(consumer_plc_name, placeholder_name, default_library, *, version='*', distributor='', parameters=None) |
Result |
add_pou(name, pou_type, code, *, plc_name=None) |
Result |
add_gvl(name, code, *, plc_name=None) |
Result |
add_dut(name, code, *, dut_kind=DUTKind.STRUCT, plc_name=None) |
Result |
add_method(pou_name, method_name, code, *, plc_name=None) |
Result |
add_property(pou_name, property_name, return_type, *, getter_code=None, setter_code=None, plc_name=None) |
Result |
update_pou_declaration(pou_name, code, *, plc_name=None) |
Result |
update_pou_implementation(pou_name, code, *, plc_name=None) |
Result |
update_method_body(pou_name, method_name, code, *, plc_name=None) |
Result |
update_pou_declaration_patch(pou_name, old, new, *, plc_name=None) |
Result |
update_pou_implementation_patch(pou_name, old, new, *, plc_name=None) |
Result |
update_method_body_patch(pou_name, method_name, old, new, *, plc_name=None) |
Result |
add_variable(pou_name, scope, declaration, item_name=None, *, plc_name=None) |
Result |
POU updates¶
A POU has its own declaration block and (for FBs / programs) its own
cyclic body, plus zero or more methods / actions / properties hanging
underneath. The three update_pou_* calls target the POU itself; the
three update_method_body* calls target a named child item.
The patch variants mirror Claude Code's Edit semantics — exactly one
unique anchor, fail otherwise.
Multi-project solutions¶
PLC-scoped writes take an optional plc_name. The bridge already enforces
the same fallback policy on the PowerShell side (Resolve-TcPlcName):
per-call name → PLC_PROJECT_NAME env var → auto-resolve on a
single-project sln → throw with the candidate list. open_project and
create_project stay solution-scoped.
To author a multi-PLC sln from scratch (e.g. a Library + Tests split), use
create_project for the sln + first PLC project, then add_plc_project for
each additional PLC. save_plc_as_library and add_library_reference author
a compiled library reference between in-sln PLC projects. See
multi-plc for the full workflow.
Why this shape¶
A TwinCAT project is more than a folder of .TcPOU files — there are GUIDs, cross-references in .plcproj, and tree indexes that have to stay consistent. If the agent edits XML directly it ends up reasoning over two parallel realities: the files on disk, and what the IDE thinks the project is. ProjectWriter routes every structural change through the IDE so there is one source of truth and the agent never has to invent GUIDs or reconcile drift.