Files
cimtechniques-service-suite/docs/SUITE-ARCHITECTURE.md
andy 9ae558d8f9 docs: refresh living docs for the 3-module suite
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>
2026-06-04 07:52:20 -04:00

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

  1. (Andy) Provide source for the two other main apps — VB6 source or .vbp deps plus a sentence on what each tool does. (Prerequisite satisfied before the reshape started.)
  2. Inventory & compare the three apps' dependency/protocol surfaces; decide what truly belongs in core.
  3. 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).
  4. Bring in app #2 as module #2 (DA-07) — rebuilt 2026-06-02 with its own protocol/; core reused as-is.
  5. Bring in app #3 as module #3 (IOModbus) — rebuilt 2026-06-03, a config- driven Modbus RTU master; core reused 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 same apply_theme/tokens so the suite looks like one product. The rules and a per-module adoption checklist are in DESIGN-SYSTEM.md.
  • The packaging/AV/code-signing/clean-VM concerns are now solved once in core for the whole suite.
  • The current DA-12 hardware-verification work (docs/HARDWARE-VERIFICATION.md) still stands and is unaffected by the suite structure.