feat: Add template system to weevil new command

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.
This commit is contained in:
Eric Ratliff
2026-02-02 19:14:50 -06:00
parent 0431425f38
commit 60679e097f
42 changed files with 5521 additions and 82 deletions

View File

@@ -0,0 +1,173 @@
# Quick Reference Guide
## Project Commands
### Testing (Windows JRE - No Robot Needed)
```bash
gradlew test # Run all tests
gradlew test --tests MotorCyclerTest # Run specific test
```
### Building for Robot
```bash
build.bat # Build APK (Windows)
./build.sh # Build APK (Linux/Mac)
```
### Deployment
```bash
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
1. **OpMode starts** → Creates FtcMotorController from hardware map
2. **OpMode.init()** → Creates MotorCycler, passes controller
3. **OpMode.loop()** → Calls motorCycler.update(currentTime)
4. **MotorCycler** → Updates state, controls motor via interface
5. **MotorController** → Abstraction hides whether it's real or mock
## Testing Flow
1. **Test creates** → MockMotorController
2. **Test creates** → MotorCycler with mock
3. **Test calls** → motorCycler.init()
4. **Test calls** → motorCycler.update() with simulated time
5. **Test verifies** → Mock motor received correct commands
## Key Design Patterns
### Dependency Injection
```java
// 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
```java
// Good: Program to interface
MotorController motor = new FtcMotorController(dcMotor);
// Bad: Program to implementation
// FtcMotorController motor = new FtcMotorController(dcMotor);
```
### Time-Based State Machine
```java
// 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
1. Create interface in `hardware/` (e.g., `ServoController.java`)
2. Create FTC implementation (e.g., `FtcServoController.java`)
3. Create business logic in `subsystems/` (e.g., `ClawController.java`)
4. Create mock in `test/hardware/` (e.g., `MockServoController.java`)
5. Create tests in `test/subsystems/` (e.g., `ClawControllerTest.java`)
6. Wire into OpMode
### Run a Specific Test
```bash
gradlew test --tests "MotorCyclerTest.testFullCycle"
```
### Debug Test Failure
1. Look at test output (shows which assertion failed)
2. Check expected vs actual values
3. Add println() to MotorCycler if needed
4. Re-run test instantly (no robot deploy needed!)
### Modify Timing
Edit MotorCycleOpMode.java line 20:
```java
// 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.md` for deep dive into design decisions
- Read `README.md` for 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!**