Restructured linux to match Windows
This commit is contained in:
70
windows/templates/Drive.java
Normal file
70
windows/templates/Drive.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package robot.subsystems;
|
||||
|
||||
import robot.Pose2d;
|
||||
|
||||
/**
|
||||
* Drive subsystem - business logic only.
|
||||
* Hardware interface defined as inner interface.
|
||||
* Tests use inline mocks - no FTC SDK needed.
|
||||
*/
|
||||
public class Drive {
|
||||
private final Hardware hardware;
|
||||
private Pose2d pose;
|
||||
|
||||
public Drive(Hardware hardware) {
|
||||
this.hardware = hardware;
|
||||
this.pose = new Pose2d();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive using field-centric controls.
|
||||
* @param forward Forward/backward speed (-1.0 to 1.0)
|
||||
* @param strafe Left/right speed (-1.0 to 1.0)
|
||||
* @param rotate Rotation speed (-1.0 to 1.0)
|
||||
*/
|
||||
public void drive(double forward, double strafe, double rotate) {
|
||||
// Your drive logic here
|
||||
double heading = hardware.getHeading();
|
||||
|
||||
// Example: field-centric conversion
|
||||
double cos = Math.cos(heading);
|
||||
double sin = Math.sin(heading);
|
||||
|
||||
double rotatedForward = forward * cos - strafe * sin;
|
||||
double rotatedStrafe = forward * sin + strafe * cos;
|
||||
|
||||
hardware.setPowers(rotatedForward, rotatedStrafe, rotate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all drive motors.
|
||||
*/
|
||||
public void stop() {
|
||||
hardware.setPowers(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current estimated pose.
|
||||
*/
|
||||
public Pose2d getPose() {
|
||||
return pose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hardware interface - implement this for real robot.
|
||||
*/
|
||||
public interface Hardware {
|
||||
/**
|
||||
* Get robot heading in radians.
|
||||
*/
|
||||
double getHeading();
|
||||
|
||||
/**
|
||||
* Set drive motor powers.
|
||||
* @param forward Forward power
|
||||
* @param strafe Strafe power
|
||||
* @param rotate Rotation power
|
||||
*/
|
||||
void setPowers(double forward, double strafe, double rotate);
|
||||
}
|
||||
}
|
||||
66
windows/templates/DriveTest.java
Normal file
66
windows/templates/DriveTest.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package robot.subsystems;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Unit tests for Drive subsystem.
|
||||
* Uses inline mock - no FTC SDK required.
|
||||
*/
|
||||
class DriveTest {
|
||||
|
||||
/**
|
||||
* Simple mock implementation of Drive.Hardware.
|
||||
* Captures method calls for verification in tests.
|
||||
*/
|
||||
static class MockHardware implements Drive.Hardware {
|
||||
double lastForward = 0;
|
||||
double lastStrafe = 0;
|
||||
double lastRotate = 0;
|
||||
double heading = 0;
|
||||
int setPowersCallCount = 0;
|
||||
|
||||
@Override
|
||||
public double getHeading() {
|
||||
return heading;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPowers(double forward, double strafe, double rotate) {
|
||||
this.lastForward = forward;
|
||||
this.lastStrafe = strafe;
|
||||
this.lastRotate = rotate;
|
||||
this.setPowersCallCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDriveCallsSetPowers() {
|
||||
MockHardware mock = new MockHardware();
|
||||
Drive drive = new Drive(mock);
|
||||
|
||||
drive.drive(0.5, 0.3, 0.1);
|
||||
|
||||
assertEquals(1, mock.setPowersCallCount, "setPowers should be called once");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStopSetsZeroPower() {
|
||||
MockHardware mock = new MockHardware();
|
||||
Drive drive = new Drive(mock);
|
||||
|
||||
drive.stop();
|
||||
|
||||
assertEquals(0.0, mock.lastForward, 0.001);
|
||||
assertEquals(0.0, mock.lastStrafe, 0.001);
|
||||
assertEquals(0.0, mock.lastRotate, 0.001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPoseReturnsNonNull() {
|
||||
MockHardware mock = new MockHardware();
|
||||
Drive drive = new Drive(mock);
|
||||
|
||||
assertNotNull(drive.getPose(), "Pose should never be null");
|
||||
}
|
||||
}
|
||||
74
windows/templates/MecanumDrive.java
Normal file
74
windows/templates/MecanumDrive.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package robot.hardware;
|
||||
|
||||
import robot.subsystems.Drive;
|
||||
|
||||
// Uncomment when deploying to robot:
|
||||
// import com.qualcomm.robotcore.hardware.DcMotor;
|
||||
// import com.qualcomm.robotcore.hardware.HardwareMap;
|
||||
// import com.qualcomm.robotcore.hardware.IMU;
|
||||
// import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
|
||||
|
||||
/**
|
||||
* Mecanum drive hardware implementation.
|
||||
* Implements Drive.Hardware interface.
|
||||
*
|
||||
* DEPLOYMENT NOTE:
|
||||
* Uncomment FTC imports and implementation when ready to deploy.
|
||||
* Keep commented during development/testing on PC.
|
||||
*/
|
||||
public class MecanumDrive implements Drive.Hardware {
|
||||
|
||||
// Uncomment when deploying:
|
||||
// private final DcMotor frontLeft;
|
||||
// private final DcMotor frontRight;
|
||||
// private final DcMotor backLeft;
|
||||
// private final DcMotor backRight;
|
||||
// private final IMU imu;
|
||||
|
||||
public MecanumDrive(/* HardwareMap hardwareMap */) {
|
||||
// Uncomment when deploying:
|
||||
// frontLeft = hardwareMap.get(DcMotor.class, "frontLeft");
|
||||
// frontRight = hardwareMap.get(DcMotor.class, "frontRight");
|
||||
// backLeft = hardwareMap.get(DcMotor.class, "backLeft");
|
||||
// backRight = hardwareMap.get(DcMotor.class, "backRight");
|
||||
// imu = hardwareMap.get(IMU.class, "imu");
|
||||
//
|
||||
// // Configure motors
|
||||
// frontLeft.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
|
||||
// frontRight.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
|
||||
// backLeft.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
|
||||
// backRight.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHeading() {
|
||||
// Stub for testing - returns 0
|
||||
return 0.0;
|
||||
|
||||
// Uncomment when deploying:
|
||||
// return imu.getRobotYawPitchRollAngles().getYaw(AngleUnit.RADIANS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPowers(double forward, double strafe, double rotate) {
|
||||
// Stub for testing - does nothing
|
||||
|
||||
// Uncomment when deploying:
|
||||
// // Mecanum drive kinematics
|
||||
// double frontLeftPower = forward + strafe + rotate;
|
||||
// double frontRightPower = forward - strafe - rotate;
|
||||
// double backLeftPower = forward - strafe + rotate;
|
||||
// double backRightPower = forward + strafe - rotate;
|
||||
//
|
||||
// // Normalize powers
|
||||
// double maxPower = Math.max(1.0, Math.max(
|
||||
// Math.max(Math.abs(frontLeftPower), Math.abs(frontRightPower)),
|
||||
// Math.max(Math.abs(backLeftPower), Math.abs(backRightPower))
|
||||
// ));
|
||||
//
|
||||
// frontLeft.setPower(frontLeftPower / maxPower);
|
||||
// frontRight.setPower(frontRightPower / maxPower);
|
||||
// backLeft.setPower(backLeftPower / maxPower);
|
||||
// backRight.setPower(backRightPower / maxPower);
|
||||
}
|
||||
}
|
||||
26
windows/templates/Pose2d.java
Normal file
26
windows/templates/Pose2d.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package robot;
|
||||
|
||||
/**
|
||||
* Simple 2D pose representation (x, y, heading).
|
||||
* Pure data class - no dependencies.
|
||||
*/
|
||||
public class Pose2d {
|
||||
public final double x;
|
||||
public final double y;
|
||||
public final double heading;
|
||||
|
||||
public Pose2d(double x, double y, double heading) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.heading = heading;
|
||||
}
|
||||
|
||||
public Pose2d() {
|
||||
this(0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Pose2d(x=%.2f, y=%.2f, heading=%.2f)", x, y, heading);
|
||||
}
|
||||
}
|
||||
132
windows/templates/README.md.template
Normal file
132
windows/templates/README.md.template
Normal file
@@ -0,0 +1,132 @@
|
||||
# {{PROJECT_NAME}}
|
||||
|
||||
FTC robot project generated by FTC Project Generator v{{GENERATOR_VERSION}}.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Run tests
|
||||
./gradlew test
|
||||
|
||||
# Watch tests (auto-rerun)
|
||||
./gradlew test --continuous
|
||||
|
||||
# Build and check
|
||||
./build.sh
|
||||
|
||||
# Deploy to robot
|
||||
./deploy-to-robot.sh
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
{{PROJECT_NAME}}/
|
||||
├── src/main/java/robot/
|
||||
│ ├── subsystems/ Your robot logic (tested on PC)
|
||||
│ ├── hardware/ FTC hardware implementations
|
||||
│ └── opmodes/ FTC OpModes for Control Hub
|
||||
└── src/test/java/robot/ Unit tests (run without robot)
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Write code** in `src/main/java/robot/`
|
||||
2. **Write tests** in `src/test/java/robot/`
|
||||
3. **Run tests** with `./gradlew test --continuous`
|
||||
4. **Tests pass** → You're good!
|
||||
|
||||
## Deployment to Robot
|
||||
|
||||
When ready to test on actual hardware:
|
||||
|
||||
1. **Uncomment FTC imports** in:
|
||||
- `src/main/java/robot/hardware/MecanumDrive.java`
|
||||
- `src/main/java/robot/opmodes/TeleOp.java`
|
||||
|
||||
2. **Run deployment script:**
|
||||
```bash
|
||||
./deploy-to-robot.sh
|
||||
```
|
||||
|
||||
The script will:
|
||||
- Deploy your code to SDK TeamCode
|
||||
- Build APK
|
||||
- Install to Control Hub (via USB or WiFi)
|
||||
|
||||
### Connection Methods
|
||||
|
||||
- **USB**: Just plug in and run (recommended)
|
||||
- **WiFi**: Connect to 'FIRST-xxxx-RC' network (IP: 192.168.43.1)
|
||||
- **Custom**: `./deploy-to-robot.sh -i 192.168.1.x`
|
||||
|
||||
## Adding New Subsystems
|
||||
|
||||
Follow the pattern:
|
||||
|
||||
1. **Create subsystem** with inner Hardware interface:
|
||||
```java
|
||||
public class MySubsystem {
|
||||
public interface Hardware {
|
||||
void doThing();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Create test** with inline mock:
|
||||
```java
|
||||
class MySubsystemTest {
|
||||
static class MockHardware implements MySubsystem.Hardware {
|
||||
boolean didThing = false;
|
||||
public void doThing() { didThing = true; }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Create hardware impl** for robot (keep FTC imports commented during dev)
|
||||
|
||||
## Tips
|
||||
|
||||
- Keep FTC imports commented during development
|
||||
- Write tests for everything - they run instantly on PC
|
||||
- Use `./gradlew test --continuous` for fast iteration
|
||||
- Multiple projects can share the same FTC SDK
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Development (on PC)
|
||||
./gradlew test Run all tests
|
||||
./gradlew test --continuous Watch mode
|
||||
|
||||
# Before deployment
|
||||
./build.sh Check for compile errors
|
||||
./build.sh --clean Clean build
|
||||
|
||||
# Deploy to robot
|
||||
./deploy-to-robot.sh Full deployment
|
||||
./deploy-to-robot.sh --help Show options
|
||||
|
||||
# Other
|
||||
./gradlew clean Clean build artifacts
|
||||
./gradlew tasks List available tasks
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
To upgrade this project to a newer version of the generator:
|
||||
|
||||
```bash
|
||||
# From parent directory
|
||||
ftc-new-project {{PROJECT_NAME}} --upgrade
|
||||
```
|
||||
|
||||
This will update build files and scripts while preserving your code.
|
||||
|
||||
## Generated by FTC Project Generator
|
||||
|
||||
This project structure separates your robot code from the FTC SDK,
|
||||
making it easy to test on PC and deploy when ready.
|
||||
|
||||
Generator version: {{GENERATOR_VERSION}}
|
||||
FTC SDK version: {{FTC_VERSION}}
|
||||
60
windows/templates/TeleOp.java
Normal file
60
windows/templates/TeleOp.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package robot.opmodes;
|
||||
|
||||
import robot.hardware.MecanumDrive;
|
||||
import robot.subsystems.Drive;
|
||||
|
||||
// Uncomment when deploying to robot:
|
||||
// import com.qualcomm.robotcore.eventloop.opmode.OpMode;
|
||||
// import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
|
||||
|
||||
/**
|
||||
* Main TeleOp OpMode.
|
||||
*
|
||||
* DEPLOYMENT NOTE:
|
||||
* Uncomment FTC imports and @TeleOp annotation when ready to deploy.
|
||||
* Keep commented during development/testing on PC.
|
||||
*/
|
||||
// @TeleOp(name="Main TeleOp")
|
||||
public class TeleOp /* extends OpMode */ {
|
||||
|
||||
private Drive drive;
|
||||
|
||||
// Uncomment when deploying:
|
||||
// @Override
|
||||
public void init() {
|
||||
// Uncomment when deploying:
|
||||
// MecanumDrive hardware = new MecanumDrive(hardwareMap);
|
||||
// drive = new Drive(hardware);
|
||||
//
|
||||
// telemetry.addData("Status", "Initialized");
|
||||
// telemetry.update();
|
||||
}
|
||||
|
||||
// Uncomment when deploying:
|
||||
// @Override
|
||||
public void loop() {
|
||||
// Uncomment when deploying:
|
||||
// // Get gamepad inputs
|
||||
// double forward = -gamepad1.left_stick_y; // Inverted
|
||||
// double strafe = gamepad1.left_stick_x;
|
||||
// double rotate = gamepad1.right_stick_x;
|
||||
//
|
||||
// // Drive the robot
|
||||
// drive.drive(forward, strafe, rotate);
|
||||
//
|
||||
// // Show telemetry
|
||||
// telemetry.addData("Forward", "%.2f", forward);
|
||||
// telemetry.addData("Strafe", "%.2f", strafe);
|
||||
// telemetry.addData("Rotate", "%.2f", rotate);
|
||||
// telemetry.update();
|
||||
}
|
||||
|
||||
// Uncomment when deploying:
|
||||
// @Override
|
||||
public void stop() {
|
||||
// Uncomment when deploying:
|
||||
// if (drive != null) {
|
||||
// drive.stop();
|
||||
// }
|
||||
}
|
||||
}
|
||||
49
windows/templates/build.gradle.kts
Normal file
49
windows/templates/build.gradle.kts
Normal file
@@ -0,0 +1,49 @@
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Testing (runs on PC without SDK)
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testImplementation("org.mockito:mockito-core:5.5.0")
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events("passed", "skipped", "failed")
|
||||
showStandardStreams = false
|
||||
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
}
|
||||
}
|
||||
|
||||
// Task to deploy to FTC SDK
|
||||
tasks.register<Copy>("deployToSDK") {
|
||||
group = "ftc"
|
||||
description = "Copy code to FTC SDK TeamCode for deployment"
|
||||
|
||||
val homeDir = System.getProperty("user.home")
|
||||
val sdkDir = providers.gradleProperty("ftcSdkDir")
|
||||
.orElse("$homeDir/ftc-sdk")
|
||||
|
||||
from("src/main/java") {
|
||||
include("robot/**/*.java")
|
||||
}
|
||||
|
||||
into(layout.projectDirectory.dir("${sdkDir.get()}/TeamCode/src/main/java"))
|
||||
|
||||
doLast {
|
||||
println("✓ Code deployed to TeamCode - ready to build APK")
|
||||
}
|
||||
}
|
||||
19
windows/templates/gitignore.template
Normal file
19
windows/templates/gitignore.template
Normal file
@@ -0,0 +1,19 @@
|
||||
# Gradle
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
*.iml
|
||||
.vscode/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Java
|
||||
*.class
|
||||
*.log
|
||||
|
||||
# Gradle wrapper jar
|
||||
gradle/wrapper/gradle-wrapper.jar
|
||||
2
windows/templates/settings.gradle.kts.template
Normal file
2
windows/templates/settings.gradle.kts.template
Normal file
@@ -0,0 +1,2 @@
|
||||
// Include FTC SDK as composite build
|
||||
includeBuild("{{SDK_DIR}}")
|
||||
Reference in New Issue
Block a user