Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 74 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,85 @@ on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: Tests (${{ matrix.os }}, Python ${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python-version: "3.10"
- os: ubuntu-latest
python-version: "3.13"
- os: macos-latest
python-version: "3.11"
- os: windows-latest
python-version: "3.11"
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: pip
- run: python -m pip install --upgrade pip
- run: python -m pip install -e .
- run: python -m unittest discover -s tests
env:
PYTHONPATH: src

lint:
name: Ruff
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.11"
cache: pip
- run: python -m pip install --upgrade pip
- run: python -m pip install -r requirements.txt
- run: PYTHONPATH=src python -m unittest discover -s tests
- run: python -m pip install -e ".[dev]"
- run: python -m ruff check src/app tests scripts

windows-package:
name: Windows one-folder package smoke
runs-on: windows-latest
needs: [test, lint]
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.11"
cache: pip
- name: Build and run no-hardware package smoke
shell: powershell
run: >-
.\scripts\build_windows_onefolder.ps1
-RuntimeRoot "$env:RUNNER_TEMP\AutoLoadOffTestRuntime"
- name: Upload smoke receipt
if: always()
uses: actions/upload-artifact@v7
with:
name: windows-package-smoke-receipt
path: ${{ runner.temp }}/AutoLoadOffTestRuntime/__data__/package_smoke_receipt.json
if-no-files-found: warn
retention-days: 14
- name: Upload one-folder package
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v7
with:
name: AutoLoadOffTest-windows-onefolder
path: dist/AutoLoadOffTest
if-no-files-found: error
retention-days: 14
46 changes: 30 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,31 @@ It turns a repetitive manual lab workflow into a layered application:

## Evidence Status

This table is intentionally conservative. Update it when draft work merges or live bench validation becomes available.
This table is intentionally conservative. See the detailed [validation matrix](docs/validation_matrix.md).

| Surface | Public status | Safe claim |
| Surface | Inspectable evidence | Boundary |
| --- | --- | --- |
| `main` branch | Layered Tkinter desktop app with hardware-free tests, deterministic fixture demo, measurement export, operator guide, architecture notes, and safety notes. | Software engineering maturity for a lab-automation workflow; hardware-free validation only. |
| [Draft PR #6](https://github.com/lishehao/auto-load-off-test/pull/6) | Draft capability/discovery/packaging sprint with public CI success on the PR head. Not merged into `main` yet. | In-review follow-up work for model capability profiles, mockable discovery/test-connect, and Windows packaging notes. |
| [Draft PR #7](https://github.com/lishehao/auto-load-off-test/pull/7) | Draft calibration/export workflow receipt sprint stacked on PR #6. Main-target CI may not run until it is retargeted. Not merged into `main` yet. | In-review UI polish for reference coverage receipts, export artifact receipts, and non-modal workflow warnings. |
| Live instruments | No current public evidence of live AWG/oscilloscope validation. | Do not claim live instrument validation; use the project as hardware-free software and workflow evidence. |
| Hardware-free core | Cross-platform unit tests, fake instrument ports, capability-aware preflight, strict measurement/reference validation, and a deterministic fixture/reference/correction/export/reload workflow. | Validates software behavior and data contracts without instruments. |
| Operator console | Real Tkinter window capture with 72-point replay, log Bode display, progress, source/safety receipts, and MAT/CSV/TXT export. | The replay is simulated and explicitly labeled; production instrument adapters are not used. |
| Windows distribution | PyInstaller one-folder build plus an automated packaged fixture/export/reload smoke and machine-readable receipt. | Does not install VISA drivers, prove Windows GUI rendering on every host, or validate connected instruments. |
| Live instruments | Capability profiles and production adapters are present, but there is no current public bench-validation record. | Do not claim live AWG/oscilloscope, metrology, or production-system validation. |

## Demo

[![Auto-Load-off-Test point-by-point operator console replay](docs/images/auto-load-off-test-point-replay-demo.png)](https://youtu.be/fYokRzNnm84)

[Watch the point-by-point operator console demo on YouTube](https://youtu.be/fYokRzNnm84)

This capture shows the real Tkinter operator console replaying a deterministic 72-point fixture
point by point. It demonstrates the UI, plotting, progress/status updates, source receipt, and export
workflow without connected instruments.
The published YouTube walkthrough shows the real Tkinter operator console replaying a deterministic 72-point
fixture point by point. The repository poster and local MP4 are a newer recapture of the same workflow; they
also show the log-frequency gain/phase display, progress and latest-frequency updates, neutral `AWG/OSC not used`
status, the loaded 72-point reference/coverage receipt, and an export receipt.

It is labeled `No hardware - simulated fixture`: the production AWG/oscilloscope adapters are not used
in this demo, and it is not live hardware validation.

For offline review, the same capture is available as a [local MP4 fallback](docs/images/auto-load-off-test-point-replay-demo.mp4).
For offline review, the recaptured current-UI artifact is available as a
[local MP4 fallback](docs/images/auto-load-off-test-point-replay-demo.mp4).

## Why It Exists

Expand All @@ -53,8 +55,9 @@ src/
runtime/ runtime paths and environment helpers
presentation/tk/ Tkinter UI and plotting
application/ use cases, DTOs, events, ports
domain/ pure models, validation, sweep math, DSP
infrastructure/ instrument adapters and persistence
domain/ models, capability profiles, validation, sweep math, DSP
infrastructure/ adapter registry, discovery, instrument IO, persistence
demo/ deterministic no-hardware fixture and package smoke
equips.py legacy vendor/instrument compatibility layer
```

Expand All @@ -68,14 +71,16 @@ flowchart LR
APP --> PERSIST["Settings + Measurement Persistence"]
```

The UI and use cases do not call `src/equips.py` directly. That file is treated as a legacy vendor compatibility layer and is wrapped by infrastructure adapters.
The UI and use cases do not call `src/equips.py` directly. That file is treated as a legacy vendor compatibility
layer and is wrapped by registered infrastructure adapters. Supported model metadata comes from the capability
registry rather than UI string dispatch.

## Requirements

- Python 3.10 or newer
- Tkinter, usually included with the Python installer on macOS/Windows
- For live instrument use:
- supported AWG and oscilloscope models from `src/app/shared/mapping.py`
- a model with a registered capability profile and production adapter
- VISA access through `pyvisa` / `pyvisa-py`
- a working VISA backend for the connection type, such as NI-VISA / Keysight IO Libraries for LAN/USB/GPIB or the extra USB/GPIB libraries required by `pyvisa-py`
- correct LAN/VISA addresses for the instruments
Expand Down Expand Up @@ -124,7 +129,10 @@ For packaged installs or lab workstations, set `AUTO_LOAD_OFF_TEST_ROOT` to an e
PYTHONPATH=src python -m unittest discover -s tests
```

The test suite uses pure domain tests and mocked instrument ports. It covers sweep generation, signal processing, settings serialization, measurement I/O, start-sweep event flow, and the sweep task runner.
The suite covers sweep generation, DSP, capability validation, adapter/discovery fakes, settings serialization,
strict measurement/reference schemas, deterministic calibration/export round trips, UI receipt state, task-runner
cleanup, and the no-hardware package smoke. CI runs the suite on Linux, macOS, and Windows and also builds the
Windows one-folder artifact.

## Output Files

Expand Down Expand Up @@ -158,6 +166,7 @@ See [docs/safety.md](docs/safety.md) for stop/shutdown behavior and hardware ass
## Documentation

- [Architecture](docs/architecture.md)
- [Validation Matrix](docs/validation_matrix.md)
- [Operator Guide](docs/operator_guide.md)
- [Safety Notes](docs/safety.md)
- [Extending The Application](docs/extending.md)
Expand All @@ -170,14 +179,19 @@ See [docs/safety.md](docs/safety.md) for stop/shutdown behavior and hardware ass
## Evidence Map

- Architecture and code boundaries: [docs/architecture.md](docs/architecture.md)
- Hardware-free vs live validation boundary: [docs/validation_matrix.md](docs/validation_matrix.md)
- Operator workflow: [docs/operator_guide.md](docs/operator_guide.md)
- Hardware and safety boundary: [docs/safety.md](docs/safety.md)
- No-hardware fixture/demo boundary: [docs/hyperframe_demo.md](docs/hyperframe_demo.md)
- Deterministic demo data: [demo_data/README.md](demo_data/README.md)
- End-to-end fixture correction/export test: [tests/test_hardware_free_workflow.py](tests/test_hardware_free_workflow.py)
- Packaged no-hardware smoke: [src/app/demo/package_smoke.py](src/app/demo/package_smoke.py)
- CI workflow: [.github/workflows/ci.yml](.github/workflows/ci.yml)

## Project Status

The refactored app is local, single-process, and hardware-adapter based. Its strongest engineering signal is the separation between UI, use-case orchestration, pure domain logic, persistence, and instrument side effects.

Current public validation is hardware-free: unit tests, mocked/fake instrument paths, deterministic fixture loading/replay, export round trips, and documentation checks. Live hardware validation remains future work and should not be claimed from this repository alone.
Current public validation is hardware-free: cross-platform tests, mocked/fake instrument paths, strict data
contracts, deterministic fixture correction/replay, export round trips, a real Tk UI capture, and a packaged
Windows smoke. Live hardware validation remains future work and should not be claimed from this repository alone.
4 changes: 4 additions & 0 deletions demo_data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ These files are sample data for review and local testing. They are not a substit

The Hyperframe fixture is explicitly simulated no-hardware data. Do not describe it as a live hardware validation run.

`tests/test_hardware_free_workflow.py` uses the measurement/reference pair to reconstruct corrected gain and phase,
compare against checked-in expected arrays, export MAT/CSV/TXT, and reload MAT/CSV. This validates the deterministic
software workflow, not the physical plausibility or metrological accuracy of a connected bench.

Regenerate the deterministic Hyperframe fixture with:

```bash
Expand Down
33 changes: 29 additions & 4 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ flowchart LR
- Use-case orchestration for start/stop sweep, save/load, reference loading, and settings.
- Emits typed events for UI; no Tk widgets or message boxes.
- `app/domain`
- Pure dataclasses, enums, instrument capability profiles, validation, sweep generation, DSP, calibration, and export array shaping.
- Pure dataclasses, enums, instrument capability profiles, capability preflight, strict data validation,
sweep generation, DSP, calibration, plot-scale selection, and export array shaping.
- `app/infrastructure`
- Adapter registry and wrappers around `src/equips.py`.
- JSON settings and MAT/CSV/TXT persistence.
Expand Down Expand Up @@ -61,9 +62,10 @@ Forbidden:

## Instrument Access

- Instrument model and address resolution go through `equips_factory`.
- Supported model metadata is declared in `domain/instrument_capabilities.py`.
- Adapter construction goes through the explicit infrastructure adapter registry.
- Adapter construction goes through `infrastructure/instruments/adapter_registry.py`; unsupported model/role
combinations fail before the legacy vendor layer is entered.
- Address resolution remains isolated in infrastructure and is injected into the controller/discovery service.
- AWG and OSC commands are executed through `AwgPort` and `OscPort` adapters.
- Connection scanning is provided by `PyVisaResourceScanner`, `ConnectionMonitor`, and the
discovery/test-connect service. Test-connect uses short `*IDN?` probes and does not start a sweep.
Expand All @@ -77,6 +79,29 @@ Forbidden:

Runtime locations are centralized through `AppPaths` in `app/runtime/paths.py`.

Measurement and reference loaders normalize data through `domain/data_validation.py`. Frequencies must be finite,
positive, unique, and strictly increasing; gain arrays must be finite and aligned; phase may be absent but cannot
contain infinity. Export validates the `SweepResult` again before writing.

## Hardware-Free Evidence Flow

```mermaid
flowchart LR
FIXTURE["Deterministic fixture"] --> LOAD["Strict measurement loader"]
REF["Reference fixture"] --> CAL["Reference interpolator"]
LOAD --> CAL
CAL --> EXPECTED["Expected corrected gain and phase"]
EXPECTED --> EXPORT["MAT / CSV / TXT exporter"]
EXPORT --> RELOAD["MAT / CSV reload checks"]
RELOAD --> RECEIPT["Source and no-hardware receipt"]
```

The package smoke follows a smaller bundled-resource path: fixture/reference load, interpolation, export, reload,
and a JSON receipt. It intentionally does not initialize Tk, scan VISA resources, or construct production adapters.

## Test Strategy

The automated tests stay hardware-free by using pure domain tests and fake instrument ports. Live instrument verification remains a manual/operator workflow.
The automated suite stays hardware-free through pure domain tests, fake ports, fake scanners/identity probes,
temporary export directories, and deterministic fixtures. CI exercises the suite on Linux, macOS, and Windows;
Windows additionally builds and runs the PyInstaller smoke. See [validation_matrix.md](validation_matrix.md) for the
exact claim boundary. Live instrument verification remains a separate future bench workflow.
Loading