Skip to content
Draft
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
160 changes: 160 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Tank Controller Python - Copilot Instructions

## Project Overview

**Purpose**: Python port of TankController for Raspberry Pi, controlling an alkalinity titrator for ocean acidification research.

**Scale**: ~8,790 lines Python, 102 files, 32 tests. State machine architecture controlling pH probes, temperature sensors, syringe pumps, heaters, LCD display.

**Tech Stack**: Python 3.9-3.11, pipenv, pytest, tkinter. Raspberry Pi with Adafruit CircuitPython hardware libraries, includes mocks for development.

## Build and Test Instructions

### Essential Prerequisites & Dependencies
**ALWAYS install pipenv first**: `pip install pipenv`

**CRITICAL**: Always run `pipenv sync -d` before any other command (includes dev dependencies for testing/linting).

**Known Issue**: First run can take 5-10 minutes; may timeout on slow networks. Retry if needed.

### Running Tests
```bash
./test.sh # Runs: pipenv sync -d && pipenv run pytest -vv
pipenv run pytest -vv # Direct (after pipenv sync -d)
```

**Auto-Configuration**: `conftest.py` sets `mock_config.MOCK_ENABLED = True` automatically. Tests run in ~1-2 seconds.

### Running the Application

**GUI Mode** (for development/testing without hardware):
```bash
./run_gui.sh # Runs: pipenv sync -d && pipenv run python main.py -gui
```

**Hardware Mode** (requires Raspberry Pi with connected devices):
```bash
./run.sh # Runs: pipenv sync && pipenv run python main.py
```

**Device Installation** (Raspberry Pi only):
```bash
./device_install.sh # Installs system packages: apt-get update, pipenv, rpi.gpio
```

### Linting and Code Quality

**Tools**: flake8, pylint, isort, codespell (all configured in repo root)

**Quick Reference**:
- `flake8 <file>` - Max line 120, ignores E203/F401/W503
- `pylint <file>` - Python 3.11, max line 120 (ignore config warnings)
- `isort --check-only <file>` - Black-compatible import ordering
- `codespell <file>` - Spell checking

**CI**: GitHub Actions runs Super-Linter (ghcr.io/github/super-linter:slim-v4) on all PRs.

## Repository Structure

### Root Files
- `main.py` - Entry point; accepts `-gui` flag to enable mock mode
- `conftest.py` - Pytest configuration; sets MOCK_ENABLED=True for all tests
- `Pipfile` / `Pipfile.lock` - Dependency definitions (managed by pipenv)
- `.flake8`, `.pylintrc`, `.isort.cfg`, `.codespellrc` - Linter configurations
- `*.sh` - Shell scripts for common operations (test, run, run_gui, device_install)

### Source Code Layout

**titration/** - Main application code
- `titrator.py` - Main Titrator class (state machine controller)
- `titrator_driver.py` - Threading setup for GUI and titrator loop
- `gui.py` - Tkinter-based GUI for mock mode
- `mock_config.py` - Global flag: `MOCK_ENABLED` (controls hardware vs. mock)
- `devices/` - Hardware abstraction layer
- `library.py` - Main device imports (Keypad, LiquidCrystal, PHProbe, etc.)
- Hardware implementations: `keypad.py`, `liquid_crystal.py`, `ph_probe.py`, `temperature_probe.py`, `syringe_pump.py`, `stir_control.py`, `temperature_control.py`
- Mock implementations: `*_mock.py` files (used when MOCK_ENABLED=True)
- `ui_state/` - State machine UI states
- `ui_state.py` - Base class for all states
- `main_menu.py` - Initial state
- `titration/` - Titration states (initial, manual, automatic, setup, calibrate_ph)
- `calibration/` - Calibration states (ph, temp, setup)
- `update_settings/` - Settings modification states
- `user_value/` - User input states (solution_weight, salinity, etc.)
- `prime_pump/` - Pump priming states
- `demo_mode/` - Hardware demo states

**test/** - Mirror structure of titration/ with `*_test.py` files
- 32 test files organized by module
- Uses pytest and pytest-mock

**arduino/** - Arduino firmware for stepper motor control
- `Pi-ArduinoStepStickDriver/Pi-ArduinoStepStickDriver.ino` - Controls syringe pump stepper motor via serial communication from Pi

### GitHub Actions Workflows

1. **pytest.yml** - Python 3.9/3.10/3.11 matrix, runs on all pushes/PRs (~2-3 min)
2. **linter.yml** - Super-Linter on non-main pushes and PRs (~3-5 min)
3. **check-spelling.yml** - Codespell on PRs to main/master

## Key Architecture Patterns

**State Machine**: All states inherit from `UIState` with methods: `start()`, `handle_key(key)`, `loop()`, `name()`

**Hardware Abstraction**: `titration/devices/` classes auto-switch to mocks when `mock_config.MOCK_ENABLED = True`

**Threading**: Titrator loop in daemon thread, GUI on main thread (see `titrator_driver.py`)

## Common Pitfalls and Solutions

| Problem | Solution |
|---------|----------|
| `ModuleNotFoundError` for adafruit/gpiozero/digitalio | Hardware-specific; ensure `mock_config.MOCK_ENABLED = True` (tests auto-set via `conftest.py`) |
| `pipenv sync` timeout | Network issue; retry (can take 5-10 min) |
| Test import errors | Verify `conftest.py` loaded and sets `MOCK_ENABLED = True` |
| GUI won't start | Install tkinter: `apt-get install python3-tk` |
| CI linting failures | Run locally: `flake8`, `pylint`, `isort --check-only`, `codespell` |

## Making Changes

**Before**: 1) `pipenv sync -d` 2) `./test.sh` 3) Check similar code style

**After**: 1) Lint (`flake8`, `pylint`, `isort --check-only`) 2) `./test.sh` 3) Add tests if needed 4) `isort` to fix imports

**Style**: 120 char lines, snake_case (functions/vars), PascalCase (classes), docstrings on classes/functions, 4 spaces, import order: stdlib→3rd-party→local

## Validation Steps

**Full validation sequence** (run before submitting PR):
```bash
# 1. Install dependencies
pipenv sync -d

# 2. Run all tests
./test.sh

# 3. Run linters on modified files
flake8 <modified_files>
pylint <modified_files>
isort --check-only <modified_files>
codespell <modified_files>

# 4. Test the application (optional but recommended)
./run_gui.sh # Verify GUI still works
```

## Additional Notes

- **Never modify Pipfile.lock directly** - use `pipenv install <package>` or `pipenv install --dev <package>`
- **Device hardware** is Raspberry Pi specific - all development should use mock mode unless on actual hardware
- **Arduino code** is separate - Python communicates with Arduino via serial for stepper motor control
- **Logging**: The application prints extensive debug output to console (see `titrator.py` loop() and update_state())
- **Data files**: Generated data files (CSV, JSON) should go in `data/` directory (gitignored except structure)

## Trust These Instructions

These instructions have been validated by running commands and inspecting the actual repository state. If you encounter discrepancies:
1. First verify you're running commands from the repository root
2. Check that `pipenv` is installed and working (`pipenv --version`)
3. Try cleaning state: `rm -rf ~/.local/share/virtualenvs/TankController*` and start over
4. Only if instructions are provably incorrect should you search for alternative approaches