Restructured linux to match Windows

This commit is contained in:
Eric Ratliff
2026-01-24 12:39:32 -06:00
parent b1593a4f87
commit fd9c573131
30 changed files with 3120 additions and 1746 deletions

View 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);
}
}

View 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");
}
}

View 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);
}
}

View 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);
}
}

View 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}}

View 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();
// }
}
}

View 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")
}
}

40
linux/templates/build.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
# Quick build/check script for FTC project
set -e
CLEAN=false
while [[ $# -gt 0 ]]; do
case $1 in
--clean|-c)
CLEAN=true
shift
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
if [ "$CLEAN" = true ]; then
echo "Cleaning build..."
./gradlew clean
fi
echo "Building and testing..."
./gradlew build test
echo ""
echo "════════════════════════════════════════════════════════════════"
echo " ✓ Build Successful!"
echo "════════════════════════════════════════════════════════════════"
echo ""
echo "Your code compiles and all tests pass."
echo "Ready to deploy to robot when you are."
echo ""
echo "Next steps:"
echo " 1. Uncomment FTC imports in hardware and opmodes"
echo " 2. Run: ./deploy-to-robot.sh"
echo ""

View 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

View File

@@ -0,0 +1,2 @@
// Include FTC SDK as composite build
includeBuild("{{SDK_DIR}}")