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
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: python -m pip install --upgrade pip
- run: python -m pip install -r requirements.txt
- run: PYTHONPATH=src python -m unittest discover -s tests
27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.DS_Store

# Python bytecode and test/tool caches
__pycache__/
*.py[cod]
*$py.class
.pytest_cache/
.mypy_cache/
.ruff_cache/
.matplotlib-cache/
.coverage
htmlcov/

# Local environments
.venv/
venv/
env/

# Build and packaging output
build/
dist/
*.egg-info/

# Runtime files created by the desktop app
__config__/
__data__/
*.log
138 changes: 103 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,68 +1,136 @@
# LoadoffTest (Decoupled Architecture)
# Auto-Load-off-Test

LoadoffTest is a Python desktop tool for AWG/OSC sweep measurement and calibration.
[![CI](https://github.com/lishehao-ctrl/Auto-Load-off-Test/actions/workflows/ci.yml/badge.svg)](https://github.com/lishehao-ctrl/Auto-Load-off-Test/actions/workflows/ci.yml)

This version is fully refactored into a layered architecture:
Auto-Load-off-Test is a local Python desktop tool for AWG/oscilloscope sweep measurement, calibration, plotting, and data export.

- `presentation` (Tkinter UI only)
- `application` (use cases and event flow)
- `domain` (pure business models and algorithms)
- `infrastructure` (instrument adapters and persistence)
It turns a repetitive manual lab workflow into a layered application:

## Project Layout
- configure an arbitrary waveform generator (AWG)
- configure oscilloscope acquisition channels
- sweep frequency points
- measure gain and optional phase
- apply reference calibration
- export MAT/CSV/TXT data and optional plot images

## Why It Exists

Manual AWG/oscilloscope sweep measurements are repetitive and easy to misconfigure. This project separates the workflow into testable layers so the sweep math, signal processing, settings serialization, and use-case flow can be verified without physical instruments.

## Architecture

```text
src/
main.py
app/
presentation/tk/
application/
domain/
infrastructure/
bootstrap.py desktop composition root
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
equips.py legacy vendor/instrument compatibility layer
```

```mermaid
flowchart LR
UI["Tkinter UI"] --> APP["Application Use Cases"]
APP --> DOMAIN["Domain Models / Sweep / DSP"]
APP --> PORTS["Instrument Ports"]
PORTS --> INFRA["AWG / OSC Adapters"]
INFRA --> LEGACY["equips.py Vendor Layer"]
APP --> PERSIST["Settings + Measurement Persistence"]
```

Legacy coupled modules (`src/ui.py`, `src/test.py`, `src/channel.py`, `src/deviceMng.py`) are removed.
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.

## Run
## 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`
- VISA access through `pyvisa` / `pyvisa-py`
- correct LAN/VISA addresses for the instruments

Automated tests do not require AWG/OSC hardware.

## Install

```bash
python3 src/main.py
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
```

## Configuration
For development tooling:

Settings are stored in JSON:
```bash
python -m pip install -r requirements-dev.txt
```

The project also exposes an optional console script when installed as a package:

```bash
python -m pip install -e .
auto-load-off-test
```

## Run The Desktop App

- `__config__/settings.json`
```bash
python src/main.py
```

Schema version is tracked in the settings payload (`schema_version`).
Settings are stored at:

```text
__config__/settings.json
```

## Run Tests Without Hardware

```bash
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.

## Output Files

Save operation writes:
Saving a measurement writes:

- `*.mat`
- `*.csv`
- `*.txt`
- plot images (`*_gain.png`, `*_gain_db.png`) when figure handles are provided
- `*_gain.png` and `*_gain_db.png` when plot figures are supplied

## Testing
Auto-save writes timestamped files under:

Run automated tests:

```bash
python3 -m unittest discover -s tests
```text
__data__/measurement/
```

Tests cover:
Example result generated from `demo_data/Demo(2).mat`:

![Demo sweep result](docs/images/sweep_result.png)

## Safety Notes

This is a local lab automation tool, not a certified production test platform. Operators are responsible for confirming the connected instrument model, address, voltage range, frequency range, impedance, coupling, and device-under-test limits before running a live sweep.

See [docs/safety.md](docs/safety.md) for stop/shutdown behavior and hardware assumptions.

## Documentation

- domain sweep generation
- signal processing behavior
- start-sweep use case event flow with mock ports
- settings repository round-trip
- [Architecture](docs/architecture.md)
- [Operator Guide](docs/operator_guide.md)
- [Safety Notes](docs/safety.md)
- [Extending The Application](docs/extending.md)
- [Case Study](docs/case_study.md)
- [Demo Data](demo_data/README.md)

## Notes
## Project Status

- The application remains local single-process.
- No HTTP backend is introduced.
- UI thread safety is enforced through event queue dispatch (`Tk.after`).
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.
8 changes: 8 additions & 0 deletions UserGuide/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# User Guide

The original operator guide is kept as a Word document:

- `网络分析仪_使用说明.docx`

For GitHub review and day-to-day repository navigation, use the Markdown guide:

- `../docs/operator_guide.md`
23 changes: 23 additions & 0 deletions demo_data/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
# Demo Data

This folder contains sample MAT files that can be used to inspect the measurement data shape without connecting instruments.

## Files

- `Deme(1).mat`
- Contains 15 frequency points.
- Keys observed: `freq`, `gain_db_raw`, `config`.
- Useful for checking older/raw gain-only measurement loading behavior.

- `Demo(2).mat`
- Contains 50 frequency points.
- Keys observed: `freq`, `gain_db_corr`, `phase_corr`, `config`.
- Useful for checking corrected gain/phase measurement structure.

## How To Use

1. Start the desktop app with `python src/main.py`.
2. Use the load-measurement action.
3. Select one of the MAT files in this directory.
4. Confirm the plot and loaded point count look reasonable.

These files are sample data for review and local testing. They are not a substitute for live instrument verification.
53 changes: 39 additions & 14 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
# Architecture

Auto-Load-off-Test is organized as a local desktop application with explicit boundaries between UI code, use-case orchestration, pure domain logic, persistence, and hardware side effects.

## Layer Diagram

```mermaid
flowchart LR
UI["presentation/tk<br/>Tkinter widgets, variables, dialogs, plots"] --> APP["application<br/>use cases, DTOs, events, ports"]
APP --> DOMAIN["domain<br/>models, validation, sweep math, DSP, calibration"]
APP --> PORTS["ports<br/>AwgPort, OscPort, repositories"]
PORTS --> INFRA["infrastructure<br/>adapters, scanner, JSON/MAT/CSV IO"]
INFRA --> LEGACY["src/equips.py<br/>legacy vendor compatibility layer"]
```

## Layers

- `app/bootstrap.py`
- Desktop composition root. Wires repositories, use cases, scanner, instrument factories, runtime paths, and the Tk controller.
- `app/presentation/tk`
- Tk widgets, variable bindings, dialogs, chart rendering.
- Consumes application events and dispatches user intents.
- `app/application`
- Use-case orchestration (`start_sweep`, `stop_sweep`, `save/load`, `settings`).
- Emits typed events for UI; no Tk or message boxes.
- 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, validation, sweep generation, DSP, calibration.
- Pure dataclasses, enums, validation, sweep generation, DSP, calibration, and export array shaping.
- `app/infrastructure`
- Adapter wrappers around `equips.py`.
- Adapter wrappers around `src/equips.py`.
- JSON settings and MAT/CSV/TXT persistence.

## Dependency Rules
Expand All @@ -20,35 +35,45 @@ Allowed:

- `presentation -> application`
- `application -> domain`
- `application -> infrastructure` (ports / repositories)
- `application -> ports`
- `infrastructure -> ports`
- `infrastructure -> domain`
- `infrastructure -> src/equips.py`

Forbidden:

- `domain` importing Tkinter / PyVISA / Matplotlib
- `application` showing dialogs (`messagebox` / `filedialog`)
- UI accessing `equips` directly
- `domain` importing Tkinter, PyVISA, serial, or Matplotlib.
- `application` showing dialogs through `messagebox` or `filedialog`.
- UI or use cases accessing `src/equips.py` directly.

## Event Flow

1. UI collects parameters from `ViewModel`.
2. Controller maps to `AppSettings` and starts `StartSweepUseCase` in worker thread.
3. Use case emits:
2. `TkController` maps the view model to `AppSettings`.
3. `SweepTaskRunner` starts `StartSweepUseCase` in a worker thread.
4. Use case emits:
- `SweepStarted`
- `SweepProgress`
- `SweepDataUpdated`
- `SweepWarning` / `SweepFailed`
- `SweepCompleted` / `SweepStopped`
4. Controller polls event queue on main thread via `after()` and updates UI safely.
5. Controller polls the event queue on the Tk main thread via `after()` and updates UI safely.

## Instrument Access

- Instrument model + address resolve through `equips_factory`.
- AWG and OSC commands are executed via `AwgPort` / `OscPort` adapters.
- Instrument model and address resolution go through `equips_factory`.
- AWG and OSC commands are executed through `AwgPort` and `OscPort` adapters.
- Connection scanning is provided by `PyVisaResourceScanner` and `ConnectionMonitor`.
- `src/equips.py` is intentionally treated as a vendor compatibility layer. It contains legacy SCPI/serial behavior that should not be casually refactored without physical instrument verification.

## Persistence

- Settings: `__config__/settings.json`
- Measurement files: MAT/CSV/TXT (+ optional plot PNG)
- Measurement files: MAT/CSV/TXT plus optional plot PNG files
- Reference files: MAT

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

## 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.
43 changes: 43 additions & 0 deletions docs/case_study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Case Study

## Problem

Manual AWG/oscilloscope sweep measurement is repetitive and error-prone. An operator must configure generator output, oscilloscope channels, trigger mode, acquisition timing, calibration/reference behavior, and data export for each run.

## Constraints

- The application controls physical instruments through VISA/LAN/serial paths.
- The UI must stay responsive while long sweeps run.
- Sweep math and signal processing should be testable without hardware.
- Instrument-specific commands should be isolated from application logic.
- Output data should be usable in analysis tools through MAT/CSV/TXT files.

## Architecture

The refactor separates the workflow into four main layers:

- `presentation/tk`: Tkinter controls, dialogs, event handling, and plots.
- `application`: use cases, events, DTOs, and ports.
- `domain`: settings models, validation, sweep generation, signal processing, calibration, and export shaping.
- `infrastructure`: instrument adapters, resource scanning, settings persistence, and measurement IO.

The legacy `src/equips.py` driver file remains as a vendor compatibility layer and is wrapped by infrastructure adapters.

## Testing Strategy

The automated tests avoid physical instruments by using:

- pure tests for sweep generation, signal processing, auto range, and serialization
- fake AWG/OSC ports for the start-sweep use case
- temporary directories for measurement export/load round trips
- task-runner tests around threading, auto-save, cleanup, and warnings

This keeps the core behavior reviewable on any development machine.

## Output

The app exports measurement data as MAT, CSV, and TXT files. Plot PNGs can be saved when the UI provides figure handles.

## What This Demonstrates

This project demonstrates real-world engineering in a physical-system context: separating hardware side effects from testable logic, preserving a practical desktop workflow, and improving maintainability without pretending the tool is a certified lab platform.
Loading
Loading