Implements template-based project creation allowing teams to start with
professional example code instead of empty projects.
Features:
- Two templates: 'basic' (minimal) and 'testing' (45-test showcase)
- Template variable substitution ({{PROJECT_NAME}}, etc.)
- Template validation with helpful error messages
- `weevil new --list-templates` command
- Template files embedded in binary at compile time
Technical details:
- Templates stored in templates/basic/ and templates/testing/
- Files ending in .template have variables replaced
- Uses include_dir! macro to embed templates in binary
- Returns file count for user feedback
Testing template includes:
- 3 complete subsystems (MotorCycler, WallApproach, TurnController)
- Hardware abstraction layer with mock implementations
- 45 comprehensive tests (unit, integration, system)
- Professional documentation (DESIGN_AND_TEST_PLAN.md, etc.)
Usage:
weevil new my-robot # basic template
weevil new my-robot --template testing # testing showcase
weevil new --list-templates # show available templates
This enables FTC teams to learn from working code and best practices
rather than starting from scratch.
5.0 KiB
5.0 KiB
Quick Reference Guide
Project Commands
Testing (Windows JRE - No Robot Needed)
gradlew test # Run all tests
gradlew test --tests MotorCyclerTest # Run specific test
Building for Robot
build.bat # Build APK (Windows)
./build.sh # Build APK (Linux/Mac)
Deployment
deploy.bat # Deploy to robot (Windows)
./deploy.sh # Deploy to robot (Linux/Mac)
Project Structure Quick View
my-robot/
│
├── src/main/java/robot/
│ ├── hardware/ # Hardware abstractions
│ │ ├── MotorController.java [Interface - No FTC deps]
│ │ └── FtcMotorController.java [FTC SDK wrapper]
│ │
│ ├── subsystems/ # Business logic
│ │ └── MotorCycler.java [Pure Java - Testable!]
│ │
│ └── opmodes/ # FTC integration
│ └── MotorCycleOpMode.java [Glue code]
│
├── src/test/java/robot/
│ ├── hardware/
│ │ └── MockMotorController.java [Test mock]
│ └── subsystems/
│ └── MotorCyclerTest.java [Unit tests]
│
├── build.gradle.kts # Build configuration
├── build.bat / build.sh # Build scripts
└── deploy.bat / deploy.sh # Deploy scripts
Code Flow
- OpMode starts → Creates FtcMotorController from hardware map
- OpMode.init() → Creates MotorCycler, passes controller
- OpMode.loop() → Calls motorCycler.update(currentTime)
- MotorCycler → Updates state, controls motor via interface
- MotorController → Abstraction hides whether it's real or mock
Testing Flow
- Test creates → MockMotorController
- Test creates → MotorCycler with mock
- Test calls → motorCycler.init()
- Test calls → motorCycler.update() with simulated time
- Test verifies → Mock motor received correct commands
Key Design Patterns
Dependency Injection
// Good: Pass dependencies in constructor
MotorCycler cycler = new MotorCycler(motorController, 2000, 1000);
// Bad: Create dependencies internally
// class MotorCycler {
// DcMotor motor = hardwareMap.get(...); // Hard to test!
// }
Interface Abstraction
// Good: Program to interface
MotorController motor = new FtcMotorController(dcMotor);
// Bad: Program to implementation
// FtcMotorController motor = new FtcMotorController(dcMotor);
Time-Based State Machine
// Good: Pass time as parameter (testable)
void update(long currentTimeMs) { ... }
// Bad: Read time internally (hard to test)
// void update() {
// long time = System.currentTimeMillis();
// }
Common Tasks
Add a New Subsystem
- Create interface in
hardware/(e.g.,ServoController.java) - Create FTC implementation (e.g.,
FtcServoController.java) - Create business logic in
subsystems/(e.g.,ClawController.java) - Create mock in
test/hardware/(e.g.,MockServoController.java) - Create tests in
test/subsystems/(e.g.,ClawControllerTest.java) - Wire into OpMode
Run a Specific Test
gradlew test --tests "MotorCyclerTest.testFullCycle"
Debug Test Failure
- Look at test output (shows which assertion failed)
- Check expected vs actual values
- Add println() to MotorCycler if needed
- Re-run test instantly (no robot deploy needed!)
Modify Timing
Edit MotorCycleOpMode.java line 20:
// Change from 2000, 1000 to whatever you want
motorCycler = new MotorCycler(motorController, 2000, 1000, 0.5);
// ^^^^ ^^^^ ^^^
// on-ms off-ms power
Hardware Configuration
Your FTC Robot Configuration needs:
- One DC Motor named
"motor"(exact spelling matters!)
Troubleshooting
"Could not find motor"
→ Check hardware configuration has motor named "motor"
"Tests won't run"
→ Make sure you're using gradlew test not gradlew build
→ Tests run on PC, build needs FTC SDK
"Build can't find FTC SDK"
→ Check .weevil.toml has correct ftc_sdk_path
→ Run weevil init if SDK is missing
"Motor not cycling"
→ Check OpMode is selected and started on Driver Station → Verify motor is plugged in and configured correctly
Learning More
- Read
ARCHITECTURE.mdfor deep dive into design decisions - Read
README.mdfor overview - Look at tests to see how each component works
- Modify values and re-run tests to see behavior change
Best Practices
✓ Write tests first (they're fast!) ✓ Keep subsystems independent ✓ Use interfaces for hardware ✓ Pass time as parameters ✓ Mock everything external
✗ Don't put hardware maps in subsystems ✗ Don't read System.currentTimeMillis() in logic ✗ Don't skip tests ✗ Don't mix hardware and logic code
Remember: Test locally, deploy confidently!