# ISC QtQuick App

Small PySide6 + Qt Quick application scaffold for the `ISenseCloud` desktop UI shell.

## Key App Features

The `ISenseCloud` application provides a real-time monitor and analysis dashboard for silicon wafer temperature profiles:

- **Real-Time Data Streaming & Coalescing**: Decodes binary frame streams from thermal sensors over serial/COM connections at ~20 Hz with low CPU overhead.
- **RBF Heatmap Interpolation**: A Radial Basis Function (RBF) interpolation engine paints a smooth 2D temperature gradient across the wafer surface based on physical sensor locations.
- **Interactive Review & Playback**: A `FramePlayer` component supports variable-speed playback (play, pause, step, seek) of recorded runs.
- **Interactive Trend Charts**: Renders multi-series temperature trends using a custom QtQuick painted item (`GraphQuickItem`) with automatic scaling, custom legends, and grid layouts.
- **Tabular Data Inspection**: Direct inspection of tabular dataset grids for frame-by-frame diagnostic checkups.
- **Custom Safety Limits & Thresholds**: Configurable alarm thresholds and set-points with automatic alerts when temperatures diverge from normal boundaries.

### App Showcase

![ISenseCloud UI displaying successful connection on COM6](image-1.png)
*Figure 1: Main Status Dashboard displaying successful connection, active serial communications, and automated logging terminal.*

> [!NOTE]
> **Take Screenshots for Visual Placeholders:**
>
> - **Wafer Heatmap Tab**: Take a screenshot of the Wafer Map tab showing the radial heatmap interpolation, save it to `docs/design/wafer_heatmap_tab.png` and add it here.
> - **Temperature Trend Graph**: Take a screenshot of the Graph tab plotting multi-sensor trend lines, save it to `docs/design/temp_trend_tab.png` and add it here.

## Key Architectural & Design Decisions

During the refactoring from the flat-layout prototype (`SettingTab` branch) to the unified package application (`main`), the following structural changes were made:

1. **Package Layout Restructuring**: Reorganized flat imports into a clean `src/` layout under `pygui.*` sub-packages (`controllers/`, `models/`, `data/`, `visualization/`, `wafer/`) for better module isolation.
2. **QML Component Decoupling**: Monolithic pages were refactored into reusable component blocks (e.g., `ReadoutPanel.qml`, `WaferMapView.qml`, `TransportBar.qml` under `src/pygui/ISC/Tabs/components/`), easing tab maintenance.
3. **Decoupled Controller Architecture**: Introduced QML-bound controllers (`DeviceController`, `SessionController`) acting as bridges between UI triggers and backend model states (`FramePlayer`, `SessionModel`), keeping UI files strictly visual.
4. **Robust Slot Error Boundaries**: Decorated QML-callable slots with a custom `@slot_error_boundary` wrapper to intercept runtime errors gracefully without terminating the Qt environment.
5. **Modernized Rendering Engine**: Replaced outdated contour algorithms with Radial Basis Function (RBF) interpolation (`rbf_heatmap.py`) to render high-fidelity, real-time thermal profiles.

## Requirements

- Python 3.11+ (tested with Python 3.14 in this workspace)
- macOS, Linux, or Windows with GUI support

## Setup

From the project root:

### Option A: Using `uv` (Recommended)

If you have [uv](https://docs.astral.sh/uv/) installed:

```bash
uv sync
```

### Option B: Using `pip`

```bash
python3 -m venv .venv
source .venv/bin/activate   # or .venv\Scripts\activate on Windows
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -e .            # install the `pygui` package (src/ layout) in editable mode
```

## Run

### Launch PySide6 QML App

```bash
make run
```

This launches the Qt window and loads the `ISC` QML module from `src/pygui/ISC/Main.qml`.

## Development

The project uses `uv` for dependency management and tooling, `Ruff` for linting and formatting, `Mypy` for static type checking, and `pytest` for unit tests.

A `Makefile` is provided to simplify common development and verification tasks:

| Command | Description |
| --------- | ------------- |
| `make install` | Sync dependencies and set up the local `.venv` using `uv` |
| `make run` | Launch the ISenseCloud application |
| `make test` | Run the `pytest` test suite |
| `make lint` | Check code style and quality with the `Ruff` linter |
| `make fix` | Automatically fix fixable `Ruff` style violations |
| `make typecheck` | Run the `Mypy` static type checker |
| `make clean` | Clean build and tool caches (`.pytest_cache`, `.ruff_cache`, `.mypy_cache`, `__pycache__`) |

### Ruff Configuration

Ruff is configured in `pyproject.toml` to enforce:

- **E/W**: Pycodestyle errors and warnings
- **F**: Pyflakes linter rules
- **I**: Import sorting (isort parity)
- **N**: PEP 8 naming conventions

## Project Structure

The application lives under a `src/` layout as the `pygui` package:

- `src/pygui/__main__.py`: PySide6 bootstrap; creates the Qt app and loads QML module `ISC/Main`. Entry point for `python -m pygui`.
- `src/pygui/backend/`: Qt-facing models, controllers, and visualizers organized into subdirectories:
  - `controllers/`: QML-facing controllers (Session & Device controllers).
  - `data/`: local settings, file browser, and CSV recorder utilities.
  - `models/`: data models (Session, Thresholds, Frame Player, Frame representation).
  - `visualization/`: QML wafer map item integration.
  - `wafer/`: layout definitions, coordinate mappings, and files parser.
- `src/pygui/serialcomm/`: serial port transport, device service scanning, and protocol data-parser layer.
- `src/pygui/ISC/`: the `ISC` QML module (UI).
  - `Main.qml`: top-level window definition.
  - `HomePage.qml`: main UI layout (left action rail, workspace panel, footer tabs).
  - `Theme.qml`: shared theme constants and dark/light mode tokens.
  - `qmldir`: QML module registration.
- `tests/`: pytest suite.
- `packaging/`: PyInstaller spec (`isc.spec`) and app icons.

## Architecture

```
┌────────────────────────────────────────────────────────────┐
│                      QML Layer (src/pygui/ISC/)            │
│  HomePage.qml  ─  WaferMapTab.qml  ─  DataTab.qml         │
│  StatusTab.qml  ─  SettingsTab.qml  ─  components/*        │
└─────────────────────────┬──────────────────────────────────┘
                          │  Q_PROPERTY / @Slot / Signal
┌─────────────────────────▼──────────────────────────────────┐
│                Controller Layer (backend/controllers/)      │
│  DeviceController  ─  SessionController                    │
│  (serial ops, status)  (streaming, playback)                │
└─────────────────────────┬──────────────────────────────────┘
                          │
┌─────────────────────────▼──────────────────────────────────┐
│                  Model Layer (backend/models/)              │
│  FramePlayer  ─  SessionModel  ─  ReplayStatsTracker       │
│  ThresholdClassifier  ─  SensorEditor  ─  ClusterAverage   │
│  TemperatureTableModel                                     │
└─────────────────────────┬──────────────────────────────────┘
                          │
┌─────────────────────────▼──────────────────────────────────┐
│              Serial / Data Layer (serialcomm/ + data/)      │
│  SerialPort ─ DeviceService ─ StreamReader ─ data_parser   │
│  LocalSettings ─ FileBrowser ─ CsvRecorder                 │
└─────────────────────────┬──────────────────────────────────┘
                          │
┌─────────────────────────▼──────────────────────────────────┐
│              Visualization (backend/visualization/)         │
│  WaferMapItem  ─  GraphView  ─  TrendChartItem             │
│  RBFHeatmap  ─  WaferMapView (QML wrapper)                 │
└────────────────────────────────────────────────────────────┘
```

## Window Configuration

Window dimensions and constraints are defined in `src/pygui/ISC/Main.qml`:

- **Default size**: 1400 × 820 pixels
- **Minimum size**: 1100 × 700 pixels
- **Title bar**: "ISenseCloud"

To adjust the window, edit the `Window` block in `src/pygui/ISC/Main.qml`:

```qml
Window {
    width: 1400
    height: 820
    minimumWidth: 1100
    minimumHeight: 700
    visible: true
    title: qsTr("ISenseCloud")
}
```

## Customization

- Toggle dark/light mode in `src/pygui/ISC/Theme.qml` via `isDarkMode`.
- Update sidebar and footer labels in `src/pygui/ISC/HomePage.qml` through `sideActions` and `bottomTabs`.

## Troubleshooting

- If the app does not start, verify the virtual environment is active and dependencies are installed:

  *Using `uv`:*

  ```bash
  uv sync
  ```

  *Using `pip`:*

  ```bash
  source .venv/bin/activate   # or .venv\Scripts\activate on Windows
  pip install -r requirements.txt
  ```

- If no window appears, ensure you are running in a desktop session with GUI access.

- **Windows COM Port Connection Issues:**
  - Standard Windows serial drivers don't support custom baud rates (e.g. `888888`), throwing an expected `OSError(22)` warning before falling back to `115200` baud.

### Step-by-Step Windows Simulator Connection Guide

#### Step 1: Create the Virtual Serial Port Bridge

Open **HHD Virtual Serial Port Tools**. Under the **Local Bridges** panel on the left, click the green **`+`** (Add) button to create a new port pair. Configure it to bridge **`COM5 ↔ COM6`**.

![HHD Virtual Serial Port Tools showing COM5 ↔ COM6 Local Bridge](image.png)

#### Step 2: Configure and Start the Wafer Simulator

Launch the **Wafer Simulator Control Panel** (`wafer_sim_gui.py`):

1. Set the **Serial Port** to **`COM5`**.
2. **Uncheck** the `Auto-create Virtual Port (com0com)` checkbox.
3. Choose your desired **Wafer Type** (e.g., `aepwafer`) and **Family Code** (e.g., `A`).
4. Click **Start Simulator**. Once the client connects, you will see `Streaming (D2)` status and active data transfer logs.

![Wafer Simulator Control Panel configured on COM5 and streaming](image-2.png)

#### Step 3: Run the UI and Connect

Launch the `pygui` client application. On the left navigation rail, click **DETECT WAFER**. The application will scan all active COM ports, automatically detect the virtual wafer simulator on **`COM6`**, and update the status indicator to **Connected** (green).

![ISenseCloud UI displaying successful connection on COM6](image-1.png)
