Correct drift that predated IOModbus and persisted after the merge: - RUNNING.md: retitled to the suite; run commands for all 3 modules; per-module data locations; generalized architecture map and simulator note. - SUITE-ARCHITECTURE.md: 'one real module' -> three; sequencing steps 4/5 done. - DESIGN-SYSTEM.md: theme path corrected to cim_suite/core/ui/theme (it moved in the monorepo reshape); import examples fully qualified. - REBUILD-STATUS.md: hover help now spans all modules (IOModbus included). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.2 KiB
Unified Service Suite — Architecture
Status: Reshape DONE. DA-12 is module #1 running inside the cim_suite
monorepo. The shared core package and the shell launcher exist. Two future apps
get cim_suite/modules/<app>/legacy/ drop folders when their VB6 source arrives.
The decision
The plan is to fold CIMTechniques' other VB6 service utilities into a single unified desktop app where each tool is a module, built on a shared core. This was chosen deliberately based on how the tools are actually used:
- Same users, same machines — the same technicians/operators run several of these tools on the same PCs, so one app they open is better than several.
- No access boundaries — everyone can have every tool, so bundling them raises no factory-only / customer-specific / licensing concerns.
- Partial technical overlap — some tools share device family/protocol style, some don't. That's fine: modules share the shell and plumbing; unrelated modules simply don't import device code they don't need.
Net effect for customers: one signed installer, updated once — which is the original reason for the whole modernization effort.
Actual structure (monorepo)
The import root is cim_suite (not bare core). The repo currently looks like:
cim_suite/
core/ # shared, generic plumbing
protocol/
codecs.py # hex/scale primitives + FieldReader
transport/
base.py # BaseTransport callback interface
serial_transport.py # pyserial reader thread
export/
sheet.py # Sheet/Cell data types (Qt-free)
xlsx.py # write_xlsx — render Sheets to .xlsx (openpyxl)
ui/
theme/ # design system: tokens → fonts → stylesheet → apply_theme
# (bundled Lato fonts included here)
table_tab.py # shared TableTab base widget (+ export_sheet snapshot)
export_action.py # add_export_actions — reusable Export toolbar actions
config.py # generic app-name-keyed config (Qt GenericConfigLocation)
module.py # Module Protocol + ComingSoonModule
modules/
da12/ # DA-12 Monitoring Station — module #1
legacy/ # VB6 baseline (Main.frm, Calib.frm, Support.bas, …)
# read-only reference; never modify
protocol/ # DA-12 framing, decoder, encoder, messages dataclasses
domain/ # models, StationController, calibration, logger
transport/
simulator.py # in-memory fake DA-12 (speaks full wire protocol)
ui/ # MainWindow + 5 tabs + dialogs
config.py # thin wrapper over core/config.py (DA-12-scoped)
module.py # Da12Module (implements Module contract)
# <tool2>/ <tool3>/ … drop VB6 legacy/ here when source arrives
shell/
app.py # entry point: python -m cim_suite.shell.app
registry.py # static list of registered modules
launcher.py # card landing page (grid of module cards)
window.py # SuiteWindow: hosts launcher or active module
# with a "☰ Suite" back action
packaging/
suite_launcher.py # PyInstaller frozen entry (absolute imports)
suite.spec # builds CIM-Service-Suite.exe (one-folder)
installer.iss # Inno Setup 6 — product "CIMTechniques Service Suite"
The module contract (kept deliberately thin)
Each module is self-contained and owns its own device connection (the tools talk to different devices/ports). The contract as built:
class Module(Protocol):
id: str
title: str
summary: str # one-line description shown on the launcher card
available: bool # False → card shown as "coming soon", greyed out
icon: QIcon | None
def create_widget(self, parent) -> QWidget: ... # the module's tabbed UI
def shutdown(self) -> None: ... # close ports / cleanup
The shell shows a card landing page (a grid of module cards) on startup. Clicking
a card swaps SuiteWindow to that module's create_widget output. A "☰ Suite"
toolbar action returns to the launcher. ComingSoonModule is a placeholder
implementation for modules not yet built.
The DA-12's MainWindow tab set is the body of its create_widget. StationController
remains the DA-12 hub — it owns the models and transport, decodes inbound frames,
emits Qt signals the UI subscribes to, and exposes one method per outbound command.
Guiding principle: don't over-build from one example
Three modules now exist (DA-12, DA-07, IOModbus), which is exactly the rule-of-three
point where genuinely-shared abstractions become knowable. The principle held: core
carries only the obviously generic pieces (transport base + serial, TableTab,
config, packaging, the codec primitives, the theme, the Module contract, export); each
module keeps its own protocol/ because the three wire formats diverge (DA-12 big-
endian fixed-point stream, DA-07 little-endian float handshake, IOModbus standard
Modbus RTU request/response). Keep extending core only when a third consumer proves
the need — don't build a speculative plugin framework off one data point.
Sequencing / next steps
- ✅ (Andy) Provide source for the two other main apps — VB6 source or
.vbpdeps plus a sentence on what each tool does. (Prerequisite satisfied before the reshape started.) - ✅ Inventory & compare the three apps' dependency/protocol surfaces; decide
what truly belongs in
core. - ✅ Reshape DA-12 into
cim_suite/modules/da12+cim_suite/core+ a minimal shell — contained refactor of already-working, fully-tested code (88 tests guard the move). - ✅ Bring in app #2 as module #2 (DA-07) — rebuilt 2026-06-02 with its own
protocol/;corereused as-is. - ✅ Bring in app #3 as module #3 (IOModbus) — rebuilt 2026-06-03, a config-
driven Modbus RTU master;
corereused as-is.
Each step is its own spec → plan → implement cycle (see docs/superpowers/), and the
VB6 migration playbook covers the per-app reverse-
engineering. All three rebuilds are sim-complete; the outstanding milestone is
hardware verification (HARDWARE-VERIFICATION.md).
What does NOT change
- The layered architecture, the pure-tested-protocol-core discipline, and the simulator pattern carry straight over to every module.
- The visual design system is the binding standard for all modules. It lives at
cim_suite/core/ui/theme/— every module imports the sameapply_theme/tokens so the suite looks like one product. The rules and a per-module adoption checklist are inDESIGN-SYSTEM.md. - The packaging/AV/code-signing/clean-VM concerns are now solved once in
corefor the whole suite. - The current DA-12 hardware-verification work (
docs/HARDWARE-VERIFICATION.md) still stands and is unaffected by the suite structure.