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
8 changes: 8 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Code owners must approve PRs that touch these paths (branch protection:
# "require review from code owners"). Everything else needs any one approval.
# Listed paths are the ones that can move hardware or run the station - see
# CONTRIBUTING.md in UTATRocketry/Nova-Collected.
#
# Replace the owner with a team once one exists, e.g. @UTATRocketry/nova-leads

/.github/ @Awesome-Ace22
37 changes: 37 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!--
PRs go into `dev`. Only release/* and hotfix/* branches go into `main`.
Title: type(scope): what changed e.g. fix(fas): reopen the COM port after unplug
Rules: https://github.com/UTATRocketry/Nova-Collected/blob/main/CONTRIBUTING.md
-->

## What and why

<!-- One or two sentences. Link the issue if there is one: "Closes #12". -->

## How was this tested?

<!-- Tick the highest level reached (CONTRIBUTING.md section 7), then describe
what you did and what you saw. -->

- [ ] 1 Unit / Automated Test - CI
- [ ] 2 Simulator Test - testing procedure with the hardware simulators
- [ ] 3 Bench Test - testing procedure on the dev stack, with some real hardware
- [ ] 4 Integration Test - full engine test procedure, real hardware, no propellants

<!-- What you did and what you saw: -->

## Hardware impact

- [ ] None - UI, docs or tooling only
- [ ] Changes how a sensor is read, converted or shown
- [ ] Changes how an actuator is commanded, or safety rules, interlocks, timeouts or sequencing → add the `hardware-risk` label; a lead must approve
- [ ] Changes a message format, MQTT topic or FAS protocol that another repo depends on → title needs `!`, link the matching PRs:

<!-- Linked PRs in other repos: -->

## Checklist

- [ ] Targets `dev` (or `main` for a release/hotfix, or with a reason given above)
- [ ] Branch is up to date ("Update branch" button)
- [ ] No station config/calibrations, data files, secrets, `.env` files or build output committed
- [ ] README/docs updated if behaviour changed
89 changes: 89 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: PR checks

on:
pull_request:
# `edited` so fixing the PR title re-runs the checks.
types: [opened, edited, reopened, synchronize]

permissions:
contents: read

concurrency:
group: pr-checks-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
# Branch names, PR direction and PR title. See CONTRIBUTING.md in
# UTATRocketry/Nova-Collected: work goes into dev; main takes releases and
# hotfixes (anything else into main is allowed but flagged).
rules:
name: rules
runs-on: ubuntu-latest
steps:
- name: Branch name and target
env:
HEAD_REF: ${{ github.head_ref }}
BASE_REF: ${{ github.base_ref }}
run: |
words='[a-z0-9]+(-[a-z0-9]+)*'
work="^(feature|bugfix|chore)/${words}$"
hotfix="^hotfix/${words}$"
release='^release/v[0-9]{4}\.[0-9]{2}\.[0-9]{2}(\.[0-9]+)?$'
github_made='^(revert-[0-9]+-|dependabot/)'
fail() { echo "::error::$1"; exit 1; }

if [[ ! ( "$HEAD_REF" =~ $work || "$HEAD_REF" =~ $hotfix || "$HEAD_REF" =~ $release \
|| "$HEAD_REF" == "main" || "$HEAD_REF" =~ $github_made ) ]]; then
fail "Branch '$HEAD_REF' must be named feature/..., bugfix/..., chore/... (lower case, words joined by single hyphens), e.g. bugfix/fas-port-reconnect. Rename it on GitHub (Branches page, pencil icon) - that keeps this PR."
fi

case "$BASE_REF" in
dev)
if [[ "$HEAD_REF" =~ $release ]]; then
fail "Release branches go into main, not dev."
fi ;;
main)
if [[ ! ( "$HEAD_REF" =~ $release || "$HEAD_REF" =~ $hotfix ) ]]; then
echo "::warning::'$HEAD_REF' -> main is not the normal path (main takes release/* and hotfix/* branches; work goes into dev). If this is deliberate, say why in the PR description."
fi ;;
hotfix/*) ;;
*)
fail "PRs go into dev (or main for releases and hotfixes), not '$BASE_REF'. Change the base branch at the top of the PR." ;;
esac
echo "Branch '$HEAD_REF' -> '$BASE_REF' is fine."

- name: PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: |
conventional='^(feat|fix|refactor|docs|test|chore)(\([a-z0-9._/-]+\))?!?: .{3,}$'
github_revert='^Revert "'
if [[ ! "$TITLE" =~ $conventional && ! "$TITLE" =~ $github_revert ]]; then
echo "::error::PR title '$TITLE' must look like 'fix(fas): reopen the COM port after unplug' - type(optional scope): what changed. Edit the title; the checks re-run by themselves."
exit 1
fi
if (( ${#TITLE} > 72 )); then
echo "::error::PR title is ${#TITLE} characters; keep it to 72 or fewer."
exit 1
fi
echo "Title is fine."

build:
name: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- uses: actions/setup-node@v5
with:
node-version: "20"
cache: npm

- name: Install
run: npm ci

- name: Type check
run: npm run typecheck

- name: Build
run: npm run build
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# dependencies
# dependencies
/node_modules
/.pnp
.pnp.js
Expand Down Expand Up @@ -31,4 +31,7 @@ yarn-error.log*

# typescript
*.tsbuildinfo
next-env.d.ts

# generated by ops/Nova.ps1 and next build
.env.production
next-env.d.ts
41 changes: 41 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Contributing to novaOPS-front

**The rules for all Nova repos are in
[Nova-Collected/CONTRIBUTING.md](https://github.com/UTATRocketry/Nova-Collected/blob/main/CONTRIBUTING.md).
Read that first.** In short:

- `dev` collects finished work for hardware testing; `main` is what prod runs.
- Branch from `dev` as `feature/...`, `bugfix/...` or `chore/...`, e.g. `bugfix/fas-port-reconnect`.
- Open a PR **into `dev`**, titled `type(scope): what changed`, and fill in the template.
- CI green + 1 approval (a lead's if it can move hardware), then **Squash and merge**.
- Leads bring tested releases into `main`; you never merge into `main` yourself.

This file only covers what is specific to this repo.

## Before you open a PR

```bash
npm ci
npm run typecheck
npm run build
```

CI runs the same checks on every PR.

## Simulator Test (level 2)

Run the backend with its simulators (see novaOps-back `CONTRIBUTING.md`), then
`npm run dev`, and go through the testing procedure
(`docs/testing-procedure.md` in Nova-Collected). The UI talks to the backend
URL in `.env.local`; copy `.env.example` to start.

## Scopes for PR titles

`ui`, `plots`, `diagram`, `commands`, `config`, `api`, `ws`

## Things to know

- Controls that command actuators are hardware changes even though they're
"just UI": tick the actuator box in the PR template.
- Never commit `.env.local` or `.env.production` (`Nova.ps1` generates the
latter on the ground station).
1 change: 0 additions & 1 deletion src/app/flight/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ export default function FlightPage() {
<FmcAuxCard
fmc={fmc}
aux={telemetry?.aux}
rf={telemetry?.rf}
node={fmcKey ? fmcKey.replace(":", "_").toUpperCase() : "FMC_0"}
/>
<RabCard rab={telemetry?.rab} />
Expand Down
23 changes: 23 additions & 0 deletions src/app/tools/radio/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import { Box, Button } from "@chakra-ui/react";
import Link from "next/link";
import { PageHeader } from "@/components/shell";
import { RadioConfigView } from "@/components/tools";

export default function RadioConfigPage() {
return (
<Box>
<PageHeader
title="Vehicle Radio"
subtitle="STM32WL configuration record — LoRa link, pressure streams, RF chain"
action={
<Button size="sm" variant="outline" asChild>
<Link href="/tools">← Back to Tools</Link>
</Button>
}
/>
<RadioConfigView />
</Box>
);
}
8 changes: 8 additions & 0 deletions src/app/tools/radio/tool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { registerTool } from "@/lib/tools/registry";

registerTool({
slug: "radio",
label: "Vehicle Radio",
description: "STM32WL config record — LoRa parameters, pressure streams, RF chain timings.",
icon: "settings_input_antenna",
});
16 changes: 15 additions & 1 deletion src/components/devices/ChargerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,13 @@ export function ChargerCard({ boardKey, charger, chgCfg, stale = false }: Charge
<Text fontSize="2xs" color="text.muted" textTransform="uppercase" letterSpacing="0.06em">
Firmware limits
</Text>
{chgCfg.vlimit === true && <Chip status="warn">voltage cutoff active</Chip>}
<Flex gap={2} flexWrap="wrap">
{chgCfg.vlimit === true && <Chip status="warn">voltage cutoff active</Chip>}
{/* The LTC gate state cannot be proven, so no reading here is trustworthy. */}
{chgCfg.controlUnknown === true && <Chip status="error">control state unknown</Chip>}
{/* Flash write failed: the running state may not survive a reset. */}
{chgCfg.persistError === true && <Chip status="warn">persist error</Chip>}
</Flex>
</Flex>
<Flex gap={6} flexWrap="wrap">
<Flex direction="column" gap={1} flex="1" minW="130px">
Expand All @@ -152,6 +158,14 @@ export function ChargerCard({ boardKey, charger, chgCfg, stale = false }: Charge
label="cells"
value={chgCfg.cells != null ? String(chgCfg.cells) : "—"}
/>
<Readout
label="auto-charge intent"
value={chgCfg.enabledIntent == null ? "—" : chgCfg.enabledIntent ? "on" : "off"}
/>
<Readout
label="targets verified"
value={chgCfg.targetsOk == null ? "—" : chgCfg.targetsOk ? "yes" : "no"}
/>
</Flex>
</Flex>
</Box>
Expand Down
Loading
Loading