# Running & Building the CIMTechniques Service Suite Modern rebuild in Python 3 + PySide6 of the legacy VB6 service tools, unified into one app. Three modules are built: **DA-12** (`da12`), **DA-07** (`da07`), and **IOModbus** (`iomodbus`). ## Prerequisites - Windows 10/11 - Python 3.11+ (developed on 3.13) - For building the installer: [Inno Setup 6](https://jrsoftware.org/isdl.php) ## Developer setup ```powershell # from the repo root py -m venv .venv .venv\Scripts\python -m pip install -e ".[dev]" ``` ## Run it ```powershell # Against a simulator (no hardware needed) — best for a first look. Swap the module # id for da07 or iomodbus to launch the other tools: .venv\Scripts\python -m cim_suite.shell.app --module da12 --simulate .venv\Scripts\python -m cim_suite.shell.app --module da07 --simulate .venv\Scripts\python -m cim_suite.shell.app --module iomodbus --simulate # Against real hardware on a serial port: .venv\Scripts\python -m cim_suite.shell.app --module da12 --port COM3 # With no arguments it opens the suite launcher landing page (a card per module): .venv\Scripts\python -m cim_suite.shell.app ``` Inside the app you can also pick the port via **Connect…** in the toolbar; the choice is remembered in a small JSON config under your user app-data folder. ## Tests & linting ```powershell .venv\Scripts\python -m pytest -q .venv\Scripts\python -m ruff check cim_suite tests ``` The whole suite runs without hardware (each module ships an in-memory simulator that speaks its real wire protocol — DA-12/DA-07 stations and a Modbus slave bank for IOModbus). UI tests run headless via `pytest-qt`. ## Build the standalone app ```powershell .venv\Scripts\pyinstaller --noconfirm --distpath packaging\dist --workpath packaging\build packaging\suite.spec ``` Produces `packaging\dist\CIM-Service-Suite\CIM-Service-Suite.exe` — a self-contained folder with no Python/VB6 runtime or OCX dependencies. The bundle's entry point is `packaging\suite_launcher.py` (it imports the package absolutely so `app.py`'s relative imports resolve — running `app.py` directly as the entry causes "attempted relative import with no known parent package"). Verify it — **don't just eyeball that a window appears.** A bundled windowed app shows a modal error dialog and *stays alive* on an unhandled exception, so "the process is still running" does NOT mean it booted. Use the self-test hook, which boots then auto-exits, and check the exit code: ```powershell $env:SUITE_SELFTEST="1" .\packaging\dist\CIM-Service-Suite\CIM-Service-Suite.exe --module da12 --simulate echo "exit=$LASTEXITCODE" # 0 = booted cleanly; non-zero = failed to start Remove-Item Env:\SUITE_SELFTEST # Then a normal interactive run to click around: .\packaging\dist\CIM-Service-Suite\CIM-Service-Suite.exe --module da12 --simulate ``` `tests/test_packaging_entry.py` runs this same boot check against the launcher in CI. ## Build the installer After the PyInstaller build, compile the Inno Setup script: ```powershell & "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" packaging\installer.iss ``` Produces `packaging\Output\DA12-Service-Setup.exe`. For the cleanest customer experience (and to avoid SmartScreen/AV warnings), sign both `CIM-Service-Suite.exe` and the installer with an Authenticode certificate — see the commented `SignTool` line in `installer.iss`. ## Where data lives Each module scopes its own files under the user app-data folder (never next to the exe), via `cim_suite/modules//config.py`: - **Config** (last COM port, logging toggle): `_config.json` - **Calibration history**: `DACal.csv` - **Measurement logs**: `DA-Logs\` (DA-12/DA-07) or `IO-Logs\` (IOModbus) - **IOModbus** additionally reads its device catalog from the bundled `resources/IOModbus.txt`, overridable by dropping an `IOModbus.txt` in the data folder. ## Architecture (quick map) Five layers per module under `cim_suite/modules//` (where `` is `da12`, `da07`, or `iomodbus`), on a shared `cim_suite/core` + `cim_suite/shell`: | Layer | Package | Responsibility | |---|---|---| | Protocol (pure) | `modules//protocol` | framing/codecs/decode/encode — no I/O, fully tested | | Transport | `modules//transport` | in-memory simulator (real serial is shared in `core`) | | Domain | `modules//domain` | models, calibration, logging, the controller hub | | UI | `modules//ui` | PySide6 window, tabs/grids, dialogs | See `CLAUDE.md` for the per-module protocol differences, and `docs/superpowers/specs/` and `docs/superpowers/plans/` for the designs and plans.