diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b0b40a8233..86825ab7ed 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -424,6 +424,27 @@ jobs: steps: *build-package-steps + build-ipk: + needs: [validation] + if: github.event_name == 'push' + runs-on: ubuntu-24.04 + name: "Build photon-sc-app IPK" + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Make build script executable + run: chmod +x photon-sc-app/build.sh + - name: Build IPK + working-directory: photon-sc-app + run: ./build.sh + - name: Upload IPK artifact + uses: actions/upload-artifact@v7 + with: + archive: false + name: photon-sc-ipk + path: '*.ipk' + run-smoketest-native: needs: [build-package-linux, build-package-macos, build-package-windows] @@ -629,7 +650,7 @@ jobs: release: # Require smoketest-native so that if those fail, we don't release broken artifacts - needs: [build-photonlib-vendorjson, build-image, combine, build-package-linux, build-package-macos, build-package-windows, run-smoketest-native] + needs: [build-photonlib-vendorjson, build-image, combine, build-package-linux, build-package-macos, build-package-windows, run-smoketest-native, build-ipk] if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && github.repository == 'PhotonVision/photonvision' runs-on: ubuntu-24.04 steps: @@ -652,6 +673,11 @@ jobs: with: merge-multiple: true pattern: photonvision-*.xz + # Download IPK package + - uses: actions/download-artifact@v8 + with: + name: photon-sc-ipk + path: . - run: find # Push to dev release @@ -667,6 +693,7 @@ jobs: **/*win*.jar **/photonlib*.json **/photonlib*.zip + **/*.ipk if: github.event_name == 'push' - name: Create Vendor JSON Repo PR uses: wpilibsuite/vendor-json-repo/.github/actions/add_vendordep@HEAD diff --git a/.gitignore b/.gitignore index 032a1d7e11..5406f830d4 100644 --- a/.gitignore +++ b/.gitignore @@ -150,6 +150,13 @@ dist-ssr components.d.ts photon-server/src/main/resources/web/index.html +# photon-sc-app IPK build artifacts +photon-sc-app/build/ +photon-sc-app/*.ipk + +# NT4 persist files +photon-sc-app/*.json + # Playwright photon-client/test-results/ photon-client/playwright-report/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..1660f315e6 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,75 @@ +# AGENTS.md + +## Repo overview + +Monorepo with three package systems: +- **Gradle** multi-project (Java 25, C++) -- root `./gradlew` +- **pnpm** workspaces -- `photon-client/` (Vue 3 + Vuetify 3 SPA) and `website/` (Vite SSG marketing site) +- **Python** -- `photon-lib/py/` (photonlibpy wheel, pytest + mypy) + +Key Gradle subprojects (defined in `settings.gradle`): +- `photon-server` -- fat JAR entrypoint (`org.photonvision.Main.main()`, port **5800**) +- `photon-core` -- vision pipelines, hardware manager, config +- `photon-targeting` -- native C++ AprilTag + JNI +- `photon-lib` -- robot-side vendor library (Java, C++, Python) +- `photon-docs` -- JavaDoc + Doxygen + +## Commands (run from repo root) + +| What | Command | +|------|---------| +| Full app (backend + frontend) | `./gradlew run` | +| Build all Java/C++ | `./gradlew build` | +| Run Java tests | `./gradlew test` | +| Java format | `./gradlew spotlessApply` | +| Cross-compile toolchain | `./gradlew installArm64Toolchain` | +| Deploy to coprocessor | `./gradlew deploy -PArchOverride=linuxarm64` | +| Frontend dev (hot reload) | `pnpm dev` (in `photon-client/`) | +| Frontend type-check | `pnpm type-check` (in `photon-client/`) | +| Frontend lint/format | `pnpm lint` / `pnpm format` (in `photon-client/`) | +| E2E tests (Playwright) | `pnpm test` (auto-starts `./gradlew run`) | +| Python tests | `pytest` (in `photon-lib/py/`) | + +Use `pnpm` not `npm` for JS packages. + +## Frontend conventions + +- **Formatter**: Prettier — `semi: true`, `singleQuote: false`, `tabWidth: 2`, `printWidth: 120`, `trailingComma: "none"` +- **Linter**: ESLint 9 flat config — enforces same style as Prettier +- **Router**: hash-based (`/#/dashboard`, `/#/cameras`, `/#/settings`, `/#/cameraConfigs`, `/#/docs`) +- **API base**: `http://{host}:5800/api` (Axios baseURL set automatically in `src/main.ts`) +- **Path alias**: `@` → `src/` +- **State**: Pinia stores in `src/stores/`, key store is `StateStore` +- **WebSocket**: `ws://{host}:5800/websocket_data` for real-time data + +## Ports + +- App UI + API: **5800** +- Vite dev server (photon-client): default Vite port (usually 5173) + +## Build artifacts + +- `photon-server/build/libs/photonvision.jar` (fat JAR via Shadow plugin) +- `photon-client/dist/` (built SPA, served by Gradle during `./gradlew run`) +- `photon-lib/py/dist/` (Python wheel) + +## Testing quirks + +- Playwright tests expect `./gradlew run` on port 5800 (auto-configured in `playwright.config.ts`) +- Java tests use JUnit 5 (Jupiter) with JaCoCo coverage +- Python tests use pytest + mypy type checking +- CI runs typecheck-client (`vue-tsc --noEmit`) before Playwright + +## CI workflows + +- `build.yml` — main CI: typecheck, Playwright, Gradle build/test, cross-compile, IPK, disk images, smoketests +- `lint-format.yml` — wpiformat, Spotless, ESLint + Prettier +- `python.yml` — Python wheel build, pytest, mypy, PyPI publish +- `website.yml` — marketing site build + deploy +- `photon-api-docs.yml` — JavaDoc + Doxygen publish + +## Repository quick reference + +- Non-obvious dirs: `photon-serde/` (YAML→Java/C++/Python codegen), `photon-sc-app/` (IPK packaging), `test-resources/` (test images/configs) +- Version: WPILib 2027.0.0-alpha-6, JDK 25, Node 24, Python 3.14, Gradle 9.4.0 +- License: GPL-3.0 diff --git a/photon-sc-app/README.md b/photon-sc-app/README.md new file mode 100644 index 0000000000..1fea03ea47 --- /dev/null +++ b/photon-sc-app/README.md @@ -0,0 +1,128 @@ +# photon-sc-app + +IPK-packaged dashboard aggregator for PhotonVision. Runs on a coprocessor (SystemCore / Orange Pi 5) and provides a tabbed web UI that auto-discovers all PhotonVision camera coprocessors on the FRC team network, loading each into its own iframe. + +## How to build + +```sh +bash build.sh +``` + +Produces `photon-sc-app_1.0.0.ipk` in the current directory. + +## How to develop + +```sh +bash run_local.sh +``` + +Opens a dev server at `http://127.0.0.1:8080`. The web UI is served from the `www/` directory — refresh the browser to see changes. Pass `--team 5123` to enable FRC network scanning, or `--help` to see all options. + +## File layout + +| Path | Purpose | +|------|---------| +| `build.sh` | Assembles the IPK: copies overlay/ into a directory, merges control/ files, tars both, wraps with `ar` | +| `run_local.sh` / `run_local.bat` | Launches `photon_sc_app.py --local` for development without systemd | +| `control/` | OPKG metadata (`control`) and lifecycle scripts (`postinst`, `prerm`, `postrm`) | +| `overlay/` | Filesystem tree installed verbatim to the target coprocessor root | +| `overlay/usr/local/bin/photon-sc-app/photon_sc_app.py` | Python HTTP server entrypoint | +| `overlay/usr/local/bin/photon-sc-app/discovery/` | Network discovery strategies (one module per strategy) | +| `overlay/usr/local/bin/photon-sc-app/www/` | Web frontend (HTML, CSS, JS) | +| `overlay/etc/systemd/system/` | Systemd socket + service units for production | +| `overlay/usr/share/photon-sc-app.png` | App icon for SystemCore launcher | + +## Requirements + +### Web UI + +A dark-themed single-page app served from a built-in Python HTTP `ThreadingHTTPServer`. No build step, no framework — just static HTML/CSS/JS served from `www/`. The UI must: + +- Display a horizontal tab bar. Each tab is a `