Skip to content
This repository was archived by the owner on Jan 24, 2026. It is now read-only.
Open
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
7 changes: 7 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM mcr.microsoft.com/devcontainers/java:1-17-bookworm

RUN apt update && \
export DEBIAN_FRONTEND=noninteractive && \
apt install -y --no-install-recommends \
screen \
&& rm -rf /var/lib/apt/lists/*
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// README at: https://github.com/devcontainers/templates/tree/main/src/java
{
"name": "Java",
"image": "mcr.microsoft.com/devcontainers/java:1-17-bookworm",
"build": { "dockerfile": "Dockerfile" },

"features": {
"ghcr.io/devcontainers/features/java:1": {
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ out/
# Simulation GUI and other tools window save file
networktables.json
simgui.json
simgui-ds.json
*-window.json

# Simulation data log directory
Expand All @@ -188,4 +189,4 @@ compile_commands.json
.factorypath

# Don't commit the cache of downloaded sim files
/buildSim/
/build-sim/
15 changes: 4 additions & 11 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,11 @@
"desktop": false,
},
{
"type": "java",
"name": "Start 3d simulation mode",
"request": "launch",
"mainClass": "com.team766.hal.simulator.RobotMain",
"args": ["-vr_connector"]
},
{
"type": "java",
"name": "Start Maroon simulation",
"type": "wpilib",
"name": "Run simulation",
"request": "launch",
"mainClass": "com.team766.hal.simulator.RobotMain",
"args": ["-maroon_sim"]
"desktop": true,
"preLaunchTask": "Run Simulator"
}
]
}
65 changes: 62 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,79 @@
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
// The "Run Simulator" task starts the simulator to run in parallel with the robot code.
{
"label": "Run Simulator",
"type": "shell",
"command": "./deploy_sim.py",
"hide": true,
"showOutput": "always",
// This task is a dependency of the Deploy Sim task which runs the robot code, and so
// it needs to finish before the robot code will be allowed to start running. However,
// we want the simulator to run in parallel with the robot code, so we set it as a
// background task, which allows a task to signal when it has finished (with console
// output that matches the endsPattern of the problemMatcher) and then continue running
// in the background. We then reactivate the task after the robot code starts running
// (using the beginsPattern).
"isBackground": true,
"problemMatcher": {
"pattern": {
"regexp": "Simulation doesn't report errors so we don't need a pattern"
},
"background": {
"activeOnStart": true,
"beginsPattern": ".*Robot code started.*",
"endsPattern": ".*Waiting for robot code to start.*"
}
},
"presentation": {
"group": "robotSim",
"echo": true,
"reveal": "always",
"panel": "dedicated",
"showReuseMessage": false,
"clear": true,
}
},
// The "Cleanup old robot code instances" task is used to terminate any existing instances
// of the robot code before starting a new one.
// Codespaces can orphan task processes if the user refreshes the browser tab or
// disconnects, so we need to manually clean them up.
{
"label": "Cleanup old robot code instances",
"type": "shell",
"command": "pkill --signal TERM --full simulateJava || true",
"hide": true,
"problemMatcher": [],
"presentation": {
"group": "robotSim",
"echo": true,
"showReuseMessage": false,
"clear": true,
"close": true
},
},
{
"label": "Deploy Sim",
"type": "shell",
"command": "./deploy_sim.sh",
"command": "./gradlew simulateJava",
"problemMatcher": [],
"showOutput": "always",
"presentation": {
"group": "robotSim",
"echo": true,
"reveal": "always",
"focus": true,
"panel": "dedicated",
"showReuseMessage": false,
"clear": true
}
},
"dependsOrder": "sequence",
"dependsOn": [
// Before starting the robot code, make sure that any previous instances are
// stopped, and that the simulator is started.
"Cleanup old robot code instances",
"Run Simulator"
]
},
{
"label": "Gradle Build",
Expand Down
141 changes: 141 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is Team 766's FRC (FIRST Robotics Competition) 2025 robot code, written in Java using WPILib. The codebase uses a custom framework built around a Mechanism/Procedure architecture with resource reservation, plus an integrated physics simulator for testing without hardware.

## Build and Development Commands

### Building and Testing
- `./gradlew build` - Compile code and run all tests
- `./gradlew test` - Run tests only
- `./gradlew spotlessApply` - Auto-format code (Java, Gradle, XML) using Spotless
- `./gradlew spotlessCheck` - Check code formatting without applying changes

### Deployment
- `./gradlew deploy` - Deploy to the RoboRIO (robot must be connected)
- Config files are deployed from `src/main/deploy/configs/` to `/home/lvuser/deploy/configs` on the robot

### Simulation
- Run the "Simulate Robot Code" task in VS Code to start robot code in simulation mode
- `./deploy_sim.py` - Start the external physics simulator (waits for robot code on port 5800, then launches in a `screen` session)
- Simulation mode is configured via `simConfig.txt` (JSON format)
- The simulator provides a web-based 3D visualization of robot physics

## Architecture

### Framework Layer (`com.team766.framework`)
The custom framework provides the core abstractions:

**Mechanism**: Base class for all robot subsystems. Mechanisms must be reserved before use via Procedures. Each Mechanism has a `run()` method called periodically and an `onMechanismIdle()` method called when no Procedure is using it. Mechanisms integrate with WPILib2's Command system via internal proxy subsystems.

**Procedure**: Asynchronous tasks that can reserve Mechanisms. Procedures receive a `Context` object that provides cooperative-multitasking primitives like `waitFor()`, `waitForSeconds()`, etc. Procedures run as WPILib2 Commands under the hood.

**Context**: Execution context for Procedures providing wait/yield operations. Procedures can be interrupted if another Procedure with overlapping Mechanism reservations is started.

**Rule/RuleEngine**: Event-driven behavior system. Rules trigger Procedures based on conditions (e.g., button presses in operator interface). Used for teleoperated control.

### Hardware Abstraction Layer (`com.team766.hal`)
Abstraction layer over WPILib hardware APIs:
- Multiple implementations: `wpilib` (real robot), `simulator` (integrated sim), `mock` (unit tests)
- `RobotProvider` provides factory methods for motors, sensors, etc.
- `MotorController` interface for motor control with position/velocity PID, current limiting, and sensor scaling
- Configuration loaded from JSON files at runtime (see Configuration section)

### Robot Implementations (`com.team766.robot`)
Multiple robot configurations in separate packages:
- `reva` - 2024 robot with swerve drive, shoulder, intake, shooter, climber
- `reva_2025` - Active development for 2025 season
- `common` - Shared code including swerve drive implementation
- `example`, `gatorade`, etc. - Other robot configurations

Each robot package contains:
- `Robot.java` - Implements `RobotConfigurator` to initialize mechanisms, operator interface, and autonomous modes
- `mechanisms/` - Mechanism implementations
- `procedures/` - Procedure implementations for autonomous and complex actions
- `OI.java` - RuleEngine implementation that binds controls to Procedures

### Swerve Drive
Swerve drive is in `com.team766.robot.common.mechanisms.SwerveDrive`:
- Field-oriented control with gyro-based rotation compensation
- Independent control of translation and rotation
- Cross-wheels mode for resisting movement
- Odometry integration for position tracking
- Configuration via `SwerveConfig` class (wheel locations, CAN bus, current limits)
- Controlled via `controlFieldOriented(x, y, rotation)` or `drive(chassisSpeeds)`

See `docs/SwerveDrive.md` for detailed implementation notes and bringup instructions.

### Operator Interface
Controls are organized by role:
- **Driver** (`DriverOI`) - Two Thrustmaster T.16000M joysticks for robot movement
- **Box Operator** (`BoxOpOI`) - Xbox controller for mechanism control
- **Debug** (`DebugOI`) - Megalodon macro pad for pit testing individual mechanisms

See `docs/OperatorInterface.md` for complete control mappings.

## Configuration System

Robot hardware configuration is stored in JSON files:
- Main config: `simConfig.txt` (JSON despite extension) at project root
- Additional configs can be in `src/main/deploy/configs/`
- Config is read via `com.team766.config.ConfigFileReader`
- Access config values using `ConfigValue<T>` types in code
- Supports motor controllers, sensors, PID constants, physical parameters
- Specify which robot configuration to load via `"robotConfigurator"` field pointing to a class implementing `RobotConfigurator`

## Logging and Telemetry

Uses AdvantageKit for logging:
- `@AutoLog` annotation for automatic logging of mechanism state
- Logs written to `logs/` directory
- Web dashboard on port 5800 during simulation
- Replay logs using `./gradlew replayWatch`

## Testing

- JUnit 5 for unit tests
- ErrorProne static analysis (custom check: `DontDiscardProcedures` ensures Procedures are scheduled)
- AspectJ used to enforce that Procedures are not discarded without being scheduled
- Mock HAL implementation for hardware-independent testing

## Common Development Patterns

### Creating a New Mechanism
1. Extend `Mechanism` (or `MechanismWithStatus` for status publishing)
2. Add config entries for hardware in config file
3. Initialize hardware in constructor using `RobotProvider.instance.getXXX(configValue)`
4. Implement `run()` for periodic updates
5. Implement `onMechanismIdle()` to stop/safe state when not reserved
6. Add to `Robot.initializeMechanisms()`

### Creating a New Procedure
1. Extend `Procedure` or `InstantProcedure`
2. Call `reserve(mechanism)` in constructor for any Mechanisms you'll use
3. Implement `run(Context context)` with your logic
4. Use `context.waitFor()`, `context.waitForSeconds()`, etc. for timing
5. Trigger from OI using `Rule.when(condition).then(procedureName, mechanism1, mechanism2, ...)`

### Creating a New Autonomous Mode
1. Create a Procedure for the autonomous routine in `procedures/auton_routines/`
2. Add to `Robot.getAutonomousModes()` array
3. Use WPILib `PathPlanner` for path following with swerve drive

## Simulation Mode

The codebase includes an integrated physics simulator:
- Set `"simulationMode": "VrConnector"` in config to enable
- Simulator runs separately and connects via network sockets
- Simulates motors, sensors, pneumatics, and robot physics
- Web UI shows 3D robot visualization and controls
- Implementation in `com.team766.simulator` package

## Project Structure Notes

- Generated code (protobuf, build constants) in `build/generated/`
- Vendor dependencies in `vendordeps/` (AdvantageKit, CTRE, REV, etc.)
- Uses Gradle with GradleRIO plugin for FRC-specific build tasks
- DevContainer support for consistent development environment
- CI via GitHub Actions: builds and tests on every push/PR
86 changes: 86 additions & 0 deletions deploy_sim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env python3

import os
import shutil
import socket
import subprocess
import time

def main(argv=None):
# Handle the case where somebody tried to run this script from somewhere besides the root
# directory of the repo.
os.chdir(os.path.dirname(os.path.abspath(__file__)))
project_base = os.getcwd()

# Create a directory in which to keep all of the simulator's files.
os.makedirs("build-sim", exist_ok=True)
os.chdir("build-sim")

# Download the simulator package from github.
# The -N flag to wget skips the download if we already have the most recent version (as
# determined by comparing the modified timestamp of the file to the Last-Modified header
# returned by the server).
sim_package = "sim.tar.gz"
try:
t1 = os.path.getmtime(sim_package)
except OSError:
t1 = None
subprocess.check_call([
"wget",
"-N",
f"https://github.com/Team766/2020Sim/releases/latest/download/{sim_package}",
])
t2 = os.path.getmtime(sim_package)

# Extract the simulator package (unless we've already extracted the most recent version).
extracted_dir = "files"
if t1 != t2 or not os.path.isdir(extracted_dir):
shutil.rmtree(extracted_dir, ignore_errors=True)
os.makedirs(extracted_dir, exist_ok=True)
subprocess.check_call([
"tar",
f"--directory={extracted_dir}",
"-xf",
sim_package,
])

os.chdir(extracted_dir)

# If you change this print, also update the regex in .vscode/tasks.json
print("Waiting for robot code to start")

# Wait until the robot code has started before starting the simulator.
# It determines whether the robot code is running by polling whether it can connect to the HTTP
# server on port 5800 that's built into the robot code.
# This is because the simulator's run script prints the link to the viewer webpage, but we don't
# want the user opening that until the robot code has actually started running (otherwise, the
# right-side pane that tries to display data from the HTTP server built into the robot code will
# fail to load).
iter = 0
while True:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(("localhost", 5800))
break
except ConnectionRefusedError:
# Display a simple animation to show signs of life while we're waiting for the
# robot code to start.
iter += 1
print("." * (iter % 6) + " ", end="\r")
time.sleep(1)

# Invoke the simulator package's entrypoint.
# NOTE(2025-09-19): There appears to be some issue in Codespaces right now that causes
# the Run Simulator task to randomly restart (sometimes accompanied by the message
# "Extension Host Process exited with code: null, signal: SIGTERM"). Thus, we run the simulator
# in a GNU Screen session so that if the task restarts, we can just reconnect to the existing
# instance of the simulator without any disruption to the user.
os.execlp(
"screen", "screen", "-D", "-R", "-S", "simulator",
"./run.sh", project_base)

if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass
Loading