# Ignition Project Structure — CLAUDE.md Parent: [../CLAUDE.md](../CLAUDE.md) ## Overview Ignition 8.3 with Git-native project storage. The project directory is mounted into the Ignition Docker container and is the single source of truth. All resources are JSON files on disk. ## Git-Native Project Storage Ignition 8.3 stores projects as files and directories: ``` ignition/project/ ├── project.json ← project metadata ├── com.inductiveautomation.perspective/ │ ├── views/ │ │ └── / │ │ ├── view.json ← view definition │ │ └── resource.json ← resource metadata │ └── page-config/ ├── com.inductiveautomation.webdev/ │ └── / │ ├── code.py ← Jython 2.7 handler │ └── resource.json └── ignition/ ├── named-query/ │ └── / │ ├── query.sql │ └── resource.json ├── script-python/ │ └── / │ ├── code.py ← project library scripts │ └── resource.json └── global-props/ └── props.json ``` ## JSON File Rules All JSON files in the project directory: 1. **Must be valid JSON** — validate before writing (use `json.loads()` then write). 2. **Must use sorted keys** — `json.dumps(data, sort_keys=True, indent=2)` 3. **Must be backed up** before overwriting — `cp file.json file.json.bak` 4. **Must use 2-space indentation** — matches Ignition's native format. ### resource.json Template Every resource directory contains a `resource.json`: ```json { "props": { "resource.name": "" }, "scope": "G", "version": 1 } ``` - `scope`: `"G"` = gateway, `"C"` = client, `"A"` = all - `version`: increment on changes for cache-busting ## UDT / Linking Pattern ### Structure ``` [Device Type UDT] ├── Config/ │ ├── DeviceType (String) │ ├── DeviceId (String) │ └── Description (String) ├── Status/ │ ├── Running (Boolean) │ ├── Faulted (Boolean) │ └── ... ├── Command/ │ ├── Start (Boolean) │ ├── Stop (Boolean) │ └── ... └── Linking/ └── Link/ ├── LinkPath (String) ← path to PLC tag └── LinkData (Dataset/Document) ← signal mapping ``` ### How LinkPath Works `LinkPath` contains the OPC tag path prefix for the PLC signals. Example: `[ControlLogix]Program:MainProgram.P_101` `LinkData` maps UDT members to PLC tag suffixes: - `Status/Running` → `LinkPath + ".Sts_Running"` - `Command/Start` → `LinkPath + ".Cmd_Start"` This decouples the test/simulation layer from specific PLC I/O structures. Changing the PLC program only requires updating `LinkPath` and `LinkData`. ### Rules - Never modify `LinkPath` or `LinkData` at runtime from external tools. - Set linking configuration through Ignition Designer or startup scripts. - Test scenarios reference device UDT paths, not raw PLC tag paths. ## Scripting ### Jython 2.7 Constraints All scripts in `ignition/project/` run Jython 2.7. Violating these causes runtime errors: - NO f-strings → use `"text {}".format(val)` or `"text %s" % val` - NO walrus operator `:=` - NO `pathlib` → use `os.path` - NO type hints → no `def foo(x: int) -> str:` - NO `dataclasses`, `enum.auto()`, `asyncio` - NO dictionary unpacking `{**d1, **d2}` → use `d1.update(d2)` - NO `yield from` → use explicit loops - Imports: `system.*` for Ignition API, standard Java/Jython libs only ### Project Library Scripts (`script-python/`) Jython 2.7 — see root CLAUDE.md for language constraints. Naming convention: ``` script-python/ ├── testing/ │ ├── code.py ← testing.runScenario(), testing.validateResults() │ └── resource.json ├── devices/ │ ├── code.py ← devices.getStatus(), devices.sendCommand() │ └── resource.json └── util/ ├── code.py ← util.formatTag(), util.validateJson() └── resource.json ``` Access in Ignition: `project.testing.runScenario()` ### Gateway Event Scripts Located in project config, not in `script-python/`. Used for startup initialization, tag change events, scheduled tasks. ### Common Ignition API Calls ```python # Tag operations system.tag.readBlocking(["path/to/tag"]) system.tag.writeBlocking(["path/to/tag"], [value]) system.tag.browseConfiguration("path", {}) # Named queries system.db.runNamedQuery("queryName", {"param": value}) # WebDev (inside endpoint handlers) # request object has: data, params, headers, remoteAddr # return dict or string — Ignition serializes to JSON # Logging logger = system.util.getLogger("testing") logger.info("message") ``` ## Tag Provider Structure ``` [default] ├── _types_/ ← UDT definitions │ ├── Devices/ │ │ ├── Motor/ │ │ ├── Valve/ │ │ └── Pump/ │ └── Linking/ │ └── Link/ ├── Devices/ ← UDT instances │ ├── Pumps/ │ │ ├── P-101/ │ │ └── P-102/ │ ├── Valves/ │ │ └── XV-1001/ │ └── Motors/ │ └── M-201/ └── System/ ← system/status tags ├── Health/ └── Config/ ``` ## Perspective Views Views are JSON definitions in `com.inductiveautomation.perspective/views/`. Each view folder contains `view.json` (component tree) and `resource.json`. Rules: - Do not hand-edit `view.json` unless you understand the component schema. - Prefer creating views through Ignition Designer when possible. - If editing programmatically, validate the full JSON structure before writing.