From 81452a86708e76a445427c025a5b46cf94f577a0 Mon Sep 17 00:00:00 2001 From: Eric Ratliff Date: Tue, 13 Jan 2026 23:58:43 -0600 Subject: [PATCH] Initial release: FTC Project Generator Generate clean, testable FTC robot projects with proper separation from SDK bloat. Features: - Composite build setup - one shared SDK, multiple clean projects - Subsystem pattern with hardware interfaces for easy testing - JUnit scaffolding - tests run on PC without robot - Minimal project structure (~50KB vs 200MB SDK) - Support for multiple FTC SDK versions Philosophy: Your code should be YOUR code. SDK is just a dependency. Built by Nexus Workshops for FTC teams tired of fighting the standard structure. License: MIT --- .dockerignore | 34 + .editorconfig | 58 ++ .gitattributes | 76 ++ .gitignore | 114 +++ LICENSE | 21 + README.md | 295 ++++++++ TROUBLESHOOTING.md | 108 +++ build.gradle.kts.simple | 64 ++ ftc-new-project | 1547 +++++++++++++++++++++++++++++++++++++++ install.sh | 33 + 10 files changed, 2350 insertions(+) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 TROUBLESHOOTING.md create mode 100644 build.gradle.kts.simple create mode 100755 ftc-new-project create mode 100755 install.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3971a91 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,34 @@ +# Git +.git +.gitignore +.gitattributes + +# CI/CD +.github + +# Documentation +README.md +LICENSE +*.md + +# Build artifacts +build/ +*/build/ +.gradle/ +*.apk +*.aab + +# IDE +.idea/ +*.iml +.vscode/ + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log + +# Local config +local.properties \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0fe24dc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,58 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +# Java files +[*.java] +indent_style = space +indent_size = 4 +continuation_indent_size = 8 + +# Gradle files +[*.gradle] +indent_style = space +indent_size = 4 + +[*.kts] +indent_style = space +indent_size = 4 + +# XML files +[*.xml] +indent_style = space +indent_size = 4 + +# Properties files +[*.properties] +indent_style = space +indent_size = 4 + +# Shell scripts +[*.sh] +indent_style = space +indent_size = 4 +end_of_line = lf + +# Markdown +[*.md] +trim_trailing_whitespace = false +indent_style = space +indent_size = 2 + +# YAML +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +# JSON +[*.json] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..91de56c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,76 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Java sources +*.java text diff=java +*.gradle text diff=java +*.gradle.kts text diff=java + +# These files are text and should be normalized (Convert crlf => lf) +*.css text +*.df text +*.htm text +*.html text +*.js text +*.json text +*.jsp text +*.jspf text +*.jspx text +*.properties text +*.sh text +*.svg text +*.tld text +*.txt text +*.tag text +*.tagx text +*.xml text +*.yml text +*.yaml text + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.class binary +*.dll binary +*.ear binary +*.jar binary +*.so binary +*.war binary +*.jks binary +*.keystore binary +*.aar binary +*.apk binary +*.ap_ binary +*.aab binary +*.dex binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.mov binary +*.mp4 binary +*.mp3 binary +*.flv binary +*.fla binary +*.swf binary +*.gz binary +*.zip binary +*.7z binary +*.ttf binary +*.eot binary +*.woff binary +*.woff2 binary +*.pyc binary +*.pdf binary +*.ez binary +*.bz2 binary +*.swp binary +*.bin binary + +# Gradle wrapper scripts should always use LF +gradlew text eol=lf +*.sh text eol=lf + +# Windows batch files should use CRLF +*.bat text eol=crlf +*.cmd text eol=crlf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2dd1a6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +# Built application files +*.apk +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +release/ + +# Gradle files +.gradle/ +build/ +**/build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ +.externalNativeBuild +.cxx + +# IntelliJ +*.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +*.jks +*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +*.lint + +# Android Profiling +*.hprof + +# OS-specific files +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini + +# Linux +*~ + +# IDE-specific +*.swp +*.swo +*~ + +# Node modules (if using any JS tooling) +node_modules/ + +# Backup files +*.bak +*.orig + +# FTC specific +libs/*.aar + +# TeamCode build outputs that shouldn't be committed +TeamCode/build/ +FtcRobotController/build/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..feb888b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Nexus Workshops LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b841614 --- /dev/null +++ b/README.md @@ -0,0 +1,295 @@ +# FTC Project Generator + +Generate clean, testable FTC robot projects. Keep YOUR code separate from the FTC SDK bloat. + +## Philosophy + +**Normal FTC Way:** Clone their repo, modify their code +**This Way:** Your code is clean, FTC SDK is just a dependency + +One shared SDK, multiple clean projects. Test on PC, deploy when ready. + +## Quick Start + +```bash +# Create your first project +./ftc-new-project my-robot + +# Create another project (reuses same SDK!) +./ftc-new-project another-bot + +# Use specific FTC version +./ftc-new-project test-bot --version v10.0.0 +``` + +## What It Does + +1. **Checks/clones FTC SDK** (once, shared across projects) +2. **Creates YOUR clean project** with: + - Minimal structure + - Test scaffolding + - Example subsystem + - Composite build to SDK +3. **Ready to code** - tests run immediately + +## Generated Project Structure + +``` +my-robot/ +├── build.gradle.kts # Your build (references SDK) +├── settings.gradle.kts # Composite build setup +├── gradlew # Gradle wrapper +├── README.md # Project-specific docs +└── src/ + ├── main/java/robot/ + │ ├── Pose2d.java # Utility classes + │ ├── subsystems/ + │ │ └── Drive.java # Example subsystem + │ ├── hardware/ + │ │ └── MecanumDrive.java # Hardware impl stub + │ └── opmodes/ + │ └── TeleOp.java # FTC OpMode + └── test/java/robot/ + └── subsystems/ + └── DriveTest.java # Unit test + +Separate (shared): +~/ftc-sdk/ # FTC SDK (one copy for all projects) +``` + +## Usage + +### Development (Day-to-Day) + +```bash +cd my-robot + +# Run tests +./gradlew test + +# Watch mode (auto-rerun on save) +./gradlew test --continuous + +# Write code, tests pass → you're good! +``` + +### Deployment (When Ready for Robot) + +```bash +# 1. Deploy your code to SDK +./gradlew deployToSDK + +# 2. Build APK +cd ~/ftc-sdk +./gradlew build + +# 3. Install to robot +# Use Android Studio's deploy button, or: +adb install FtcRobotController/build/outputs/apk/debug/FtcRobotController-debug.apk +``` + +## Multiple Projects + +The beauty: **one SDK, many projects** + +```bash +# Create project 1 +./ftc-new-project swerve-bot + +# Create project 2 (reuses SDK!) +./ftc-new-project mecanum-bot + +# Create project 3 (still same SDK!) +./ftc-new-project test-chassis +``` + +Each project: +- Has its own git repo +- Tests independently +- Deploys to shared SDK +- Can have different license +- Is YOUR code, not FTC's + +## Commands Reference + +```bash +# Basic usage +./ftc-new-project + +# Specify FTC version +./ftc-new-project --version v10.1.1 + +# Custom SDK location +./ftc-new-project --sdk-dir ~/my-ftc + +# Get help +./ftc-new-project --help +``` + +## Environment Variables + +```bash +# Set default SDK location +export FTC_SDK_DIR=~/ftc-sdk + +# Now just: +./ftc-new-project my-robot +``` + +## Pattern: Subsystems + +Every generated project follows this pattern: + +```java +// Your subsystem +public class MySubsystem { + private final Hardware hardware; + + public MySubsystem(Hardware hardware) { + this.hardware = hardware; + } + + // Business logic here - tests on PC + public void doSomething() { + hardware.doThing(); + } + + // Inner interface - implement for robot + public interface Hardware { + void doThing(); + } +} + +// Test (inline mock) +class MySubsystemTest { + static class MockHardware implements MySubsystem.Hardware { + boolean didThing = false; + public void doThing() { didThing = true; } + } + + @Test + void test() { + MockHardware mock = new MockHardware(); + MySubsystem sys = new MySubsystem(mock); + sys.doSomething(); + assertTrue(mock.didThing); + } +} + +// Hardware impl (for robot) +public class RealHardware implements MySubsystem.Hardware { + private DcMotor motor; + + public RealHardware(HardwareMap map) { + motor = map.get(DcMotor.class, "motor"); + } + + public void doThing() { + motor.setPower(1.0); + } +} +``` + +## Why This Is Better + +**Traditional FTC:** +- Clone 200MB repo +- Your code mixed with SDK +- Hard to test +- Hard to share +- Stuck with their structure + +**This Way:** +- Your project: ~50KB +- SDK: shared, separate +- Easy to test (runs on PC) +- Easy to share (just your code) +- Your structure, your license + +## FAQ + +**Q: Can I use multiple FTC versions?** +A: Yes! Different projects can reference different SDK dirs. + +**Q: What if I want to update the SDK?** +A: `cd ~/ftc-sdk && git pull && git checkout v10.2.0` + Or specify new version when creating project. + +**Q: Does this work with Android Studio?** +A: For deployment, yes. For development, use whatever you want (Sublime, vim, etc). + +**Q: Can I put my project on GitHub with a different license?** +A: Yes! Your code is separate. Just don't include SDK files. + +**Q: What about Control Hub having multiple programs?** +A: Each project creates OpModes. Deploy multiple projects to SDK, build once, all show up on Control Hub. + +## Installation + +```bash +# Extract the generator +tar xzf ftc-project-generator.tar.gz +cd ftc-project-gen + +# Option 1: Run install script (recommended) +./install.sh # Shows options +sudo ./install.sh # System-wide install + +# Option 2: Manual symlink +sudo ln -s $(pwd)/ftc-new-project /usr/local/bin/ + +# Option 3: Add to PATH +echo "export PATH=\$PATH:$(pwd)" >> ~/.bashrc +source ~/.bashrc + +# Verify installation +ftc-new-project --help +``` + +## The Magic + +When you run tests, your code uses **interfaces** and **mocks** - no FTC SDK needed. + +When you deploy, your code gets copied to SDK's TeamCode and built with the real FTC libraries. + +Your code stays clean. SDK is just a build tool. + +## Example Session + +```bash +# Create project +$ ./ftc-new-project my-bot +>>> Checking FTC SDK... +SDK directory exists, checking version... +✓ SDK already at version v10.1.1 +>>> Creating project: my-bot +✓ Project created: my-bot + +# Develop +$ cd my-bot +$ ./gradlew test +BUILD SUCCESSFUL +2 tests passed + +# Add code, tests pass... + +# Ready to deploy +$ ./gradlew deployToSDK +Code deployed to TeamCode - ready to build APK + +$ cd ~/ftc-sdk +$ ./gradlew build +BUILD SUCCESSFUL + +# Install to robot... +``` + +Clean, simple, modular. As it should be. + +## Credits + +Built by frustrated mentors who think the standard FTC setup is backwards. + +## License + +MIT - do whatever you want. Unlike FTC's forced BSD license nonsense. diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..37ce07f --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,108 @@ +# Troubleshooting + +## Error: "Project with path ':RobotCore' not found" + +**Symptom:** +``` +FAILURE: Build failed with an exception. +* What went wrong: +Project with path ':RobotCore' not found in build ':ftc-sdk'. +``` + +**Cause:** FTC SDK was not cloned or is at wrong location. + +**Solution:** + +1. Check if SDK exists: +```bash +ls ~/ftc-sdk +# Should show: FtcRobotController, RobotCore, Hardware, etc. +``` + +2. If SDK doesn't exist, clone it: +```bash +git clone --depth 1 --branch v10.1.1 \ + https://github.com/FIRST-Tech-Challenge/FtcRobotController.git \ + ~/ftc-sdk +``` + +3. Check your project's `gradle.properties`: +```bash +cat gradle.properties +# Should show: ftcSdkDir=/home/yourusername/ftc-sdk +``` + +4. Update if path is wrong: +```bash +echo "ftcSdkDir=$HOME/ftc-sdk" > gradle.properties +``` + +5. Try again: +```bash +./gradlew test +``` + +## Error: Can't find SDK at specified path + +**Solution:** Set environment variable: +```bash +export FTC_SDK_DIR=~/ftc-sdk +./gradlew test +``` + +Or edit `gradle.properties` in your project. + +## SDK exists but wrong version + +**Solution:** Update SDK to correct version: +```bash +cd ~/ftc-sdk +git fetch --tags +git checkout v10.1.1 # or whatever version you need +``` + +## Fresh Start + +If all else fails: + +```bash +# Remove old SDK +rm -rf ~/ftc-sdk + +# Remove old project +rm -rf my-project + +# Start fresh +ftc-new-project my-project + +# Verify SDK was cloned +ls ~/ftc-sdk + +# Test +cd my-project +./gradlew test +``` + +## Verify Setup + +Run this diagnostic: + +```bash +#!/bin/bash +echo "=== FTC Setup Diagnostic ===" +echo "" +echo "1. SDK Directory:" +ls -la ~/ftc-sdk 2>/dev/null || echo " ✗ SDK not found at ~/ftc-sdk" +echo "" +echo "2. SDK Modules:" +ls ~/ftc-sdk/RobotCore 2>/dev/null && echo " ✓ RobotCore found" || echo " ✗ RobotCore missing" +ls ~/ftc-sdk/Hardware 2>/dev/null && echo " ✓ Hardware found" || echo " ✗ Hardware missing" +echo "" +echo "3. Project gradle.properties:" +cat gradle.properties 2>/dev/null || echo " ✗ No gradle.properties in current dir" +echo "" +echo "4. Environment:" +echo " FTC_SDK_DIR=$FTC_SDK_DIR" +``` + +Save as `check-setup.sh` and run it in your project directory. diff --git a/build.gradle.kts.simple b/build.gradle.kts.simple new file mode 100644 index 0000000..07346b2 --- /dev/null +++ b/build.gradle.kts.simple @@ -0,0 +1,64 @@ +plugins { + java +} + +repositories { + mavenCentral() + google() +} + +dependencies { + // Testing (runs on PC without SDK) - THIS IS WHAT YOU USE FOR DEVELOPMENT + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testImplementation("org.mockito:mockito-core:5.5.0") + + // FTC SDK - ONLY needed when you uncomment FTC code for deployment + // These will be compile errors until you uncomment, that's OK! + // compileOnly(files("${System.getenv("HOME")}/ftc-sdk/RobotCore/build/libs/RobotCore-release.aar")) + // compileOnly(files("${System.getenv("HOME")}/ftc-sdk/Hardware/build/libs/Hardware-release.aar")) +} + +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("deployToSDK") { + group = "ftc" + description = "Copy code to FTC SDK TeamCode for deployment" + + val sdkDir = System.getenv("HOME") + "/ftc-sdk" + + from("src/main/java") + into("$sdkDir/TeamCode/src/main/java") + + doLast { + println("✓ Code deployed to $sdkDir/TeamCode") + println("") + println("Next steps:") + println(" cd $sdkDir") + println(" ./gradlew build") + } +} + +tasks.register("ftcBuild") { + group = "ftc" + description = "Deploy code and build FTC APK" + + dependsOn("deployToSDK") + + val sdkDir = System.getenv("HOME") + "/ftc-sdk" + workingDir(file(sdkDir)) + commandLine("./gradlew", "build") +} diff --git a/ftc-new-project b/ftc-new-project new file mode 100755 index 0000000..a7bb150 --- /dev/null +++ b/ftc-new-project @@ -0,0 +1,1547 @@ +#!/bin/bash +# +# FTC Project Generator +# Copyright (c) 2026 Nexus Workshops LLC +# Licensed under MIT License +# +set -e + +# FTC Project Generator +# Creates clean, minimal FTC robot projects with shared SDK + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +FTC_SDK_DIR="${FTC_SDK_DIR:-$HOME/ftc-sdk}" +DEFAULT_FTC_VERSION="v10.1.1" + +usage() { + cat << EOF +╔════════════════════════════════════════════════════════════════╗ +║ FTC Project Generator - Clean Robot Projects ║ +╚════════════════════════════════════════════════════════════════╝ + +Creates clean, testable FTC robot projects with shared SDK. +Your code stays separate - SDK is just a build dependency. + +USAGE: + $(basename $0) [options] + +OPTIONS: + -v, --version FTC SDK git tag (default: $DEFAULT_FTC_VERSION) + -d, --sdk-dir SDK location (default: $FTC_SDK_DIR) + -h, --help Show this help + +EXAMPLES: + # Create new project with latest SDK + $(basename $0) my-robot + + # Create project with specific FTC version + $(basename $0) competition-bot -v v10.0.0 + + # Use custom SDK location + $(basename $0) test-bot -d ~/my-ftc-sdk + + # Create second project (reuses existing SDK!) + $(basename $0) another-robot + +WORKFLOW: + 1. Create project: $(basename $0) my-robot + 2. Develop & test: cd my-robot && ./gradlew test --continuous + 3. Deploy to SDK: ./gradlew deployToSDK + 4. Build APK: cd ~/ftc-sdk && ./gradlew build + 5. Install to robot: Use Android Studio or adb + +WHAT IT DOES: + ✓ Checks/clones FTC SDK (shared across all projects) + ✓ Creates YOUR clean project structure + ✓ Generates test scaffolding with examples + ✓ Sets up composite build to reference SDK + ✓ Ready to code immediately + +WHY THIS IS BETTER: + Traditional FTC: This Way: + • Clone 200MB repo • Your project: 50KB + • Your code mixed in • SDK separate, shared + • Hard to test • Tests run on PC instantly + • One repo per project • One SDK, many projects + • Forced BSD license • Your license, your code + +ENVIRONMENT: + Set defaults to avoid typing them every time: + + export FTC_SDK_DIR=~/ftc-sdk # SDK location + export FTC_VERSION=v10.1.1 # Default version + +PROJECT STRUCTURE: + my-robot/ + ├── 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) + +MULTIPLE PROJECTS: + One SDK, unlimited projects: + + $(basename $0) swerve-bot # Creates project 1 + $(basename $0) mecanum-bot # Reuses SDK - just creates project 2 + $(basename $0) test-chassis # Reuses SDK - creates project 3 + + All projects share same SDK but are completely independent! + +INSTALLATION: + Add to PATH for global access: + + # Option 1: Symlink + sudo ln -s $(pwd)/$(basename $0) /usr/local/bin/ + + # Option 2: Add to PATH + echo 'export PATH=\$PATH:$(pwd)' >> ~/.bashrc + + Then use from anywhere: + cd ~/robotics && $(basename $0) awesome-bot + +DOCUMENTATION: + See README.md for detailed information and examples. + +EOF + exit 0 +} + +# Parse arguments +PROJECT_NAME="" +FTC_VERSION="$DEFAULT_FTC_VERSION" + +while [[ $# -gt 0 ]]; do + case $1 in + -v|--version) + FTC_VERSION="$2" + shift 2 + ;; + -d|--sdk-dir) + FTC_SDK_DIR="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + if [ -z "$PROJECT_NAME" ]; then + PROJECT_NAME="$1" + else + echo "Error: Unknown argument: $1" + exit 1 + fi + shift + ;; + esac +done + +if [ -z "$PROJECT_NAME" ]; then + echo "Error: Project name required" + echo "Usage: $0 [options]" + echo "Run '$0 --help' for more info" + exit 1 +fi + +PROJECT_DIR="$(pwd)/$PROJECT_NAME" + +echo "============================================" +echo "FTC Project Generator" +echo "============================================" +echo "Project: $PROJECT_NAME" +echo "SDK Dir: $FTC_SDK_DIR" +echo "SDK Version: $FTC_VERSION" +echo "Target: $PROJECT_DIR" +echo "" + +# Step 1: Check/setup FTC SDK +echo ">>> Checking FTC SDK..." + +if [ -d "$FTC_SDK_DIR" ]; then + echo "SDK directory exists, checking version..." + cd "$FTC_SDK_DIR" + + # Check if it's a git repo + if [ -d ".git" ]; then + CURRENT_TAG=$(git describe --tags --exact-match 2>/dev/null || echo "none") + + if [ "$CURRENT_TAG" = "$FTC_VERSION" ]; then + echo "✓ SDK already at version $FTC_VERSION" + else + echo "Current version: $CURRENT_TAG" + echo "Fetching and checking out $FTC_VERSION..." + git fetch --tags + git checkout "$FTC_VERSION" + echo "✓ Updated to $FTC_VERSION" + fi + else + echo "Warning: SDK directory exists but is not a git repo" + echo "Using existing directory anyway..." + fi +else + echo "Cloning FTC SDK $FTC_VERSION..." + echo "This will take a minute (SDK is ~200MB)..." + git clone --depth 1 --branch "$FTC_VERSION" \ + https://github.com/FIRST-Tech-Challenge/FtcRobotController.git \ + "$FTC_SDK_DIR" || { + echo "Error: Failed to clone FTC SDK" + echo "Check your internet connection or try a different version tag" + exit 1 + } + echo "✓ Cloned FTC SDK $FTC_VERSION to $FTC_SDK_DIR" +fi + +# Step 2: Create project structure +echo "" +echo ">>> Creating project: $PROJECT_NAME" + +if [ -d "$PROJECT_DIR" ]; then + echo "Error: Project directory already exists: $PROJECT_DIR" + exit 1 +fi + +mkdir -p "$PROJECT_DIR" +cd "$PROJECT_DIR" + +# Create directory structure +mkdir -p src/main/java/robot/{subsystems,hardware,opmodes} +mkdir -p src/test/java/robot/{subsystems,hardware} + +# Step 3: Generate build files +echo "Generating build configuration..." + +cat > build.gradle.kts << 'EOF' +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("deployToSDK") { + group = "ftc" + description = "Copy code to FTC SDK TeamCode for deployment" + + val homeDir = System.getProperty("user.home") + val sdkDir = providers.gradleProperty("ftcSdkDir") + .orElse(providers.environmentVariable("FTC_SDK_DIR")) + .getOrElse("${'$'}homeDir/ftc-sdk") + + from("src/main/java") + into("${'$'}sdkDir/TeamCode/src/main/java") + + doLast { + println("✓ Code deployed to TeamCode") + println("") + println("Next steps:") + println(" cd ${'$'}sdkDir") + println(" ./gradlew build") + } +} +EOF + +cat > settings.gradle.kts << EOF +rootProject.name = "$PROJECT_NAME" +EOF + +cat > gradle.properties << EOF +# FTC SDK location +ftcSdkDir=$FTC_SDK_DIR + +# Gradle daemon +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.jvmargs=-Xmx2048m +EOF + +# Step 4: Generate source files +echo "Generating source scaffolding..." + +cat > src/main/java/robot/Pose2d.java << 'EOF' +package robot; + +/** Simple 2D pose representation */ +public class Pose2d { + public final double x, y, heading; + + public Pose2d(double x, double y, double heading) { + this.x = x; + this.y = y; + this.heading = heading; + } + + public Pose2d() { this(0, 0, 0); } + + public double distanceTo(Pose2d other) { + double dx = x - other.x; + double dy = y - other.y; + return Math.sqrt(dx * dx + dy * dy); + } +} +EOF + +cat > src/main/java/robot/subsystems/Drive.java << 'EOF' +package robot.subsystems; + +import robot.Pose2d; + +/** + * Drive subsystem - your robot logic lives here. + * Tests run on PC, deploys to robot. + */ +public class Drive { + private final Hardware hardware; + private boolean fieldRelative = true; + + public Drive(Hardware hardware) { + this.hardware = hardware; + } + + public void drive(double x, double y, double rotation) { + if (fieldRelative) { + double heading = hardware.getHeading(); + double temp = x * Math.cos(-heading) - y * Math.sin(-heading); + y = x * Math.sin(-heading) + y * Math.cos(-heading); + x = temp; + } + hardware.setPower(x, y, rotation); + } + + public Pose2d getPose() { + return hardware.getPose(); + } + + public void setFieldRelative(boolean enabled) { + this.fieldRelative = enabled; + } + + /** Hardware interface - implement for real robot */ + public interface Hardware { + void setPower(double x, double y, double rotation); + Pose2d getPose(); + double getHeading(); + } +} +EOF + +cat > src/main/java/robot/hardware/MecanumDrive.java << 'EOF' +package robot.hardware; + +import robot.Pose2d; +import robot.subsystems.Drive; + +// Uncomment when deploying to robot: +// import com.qualcomm.robotcore.hardware.DcMotor; +// import com.qualcomm.robotcore.hardware.HardwareMap; + +/** + * Real hardware implementation. + * Stub for testing, uncomment FTC imports for robot. + */ +public class MecanumDrive implements Drive.Hardware { + + // Uncomment for robot: + // private DcMotor frontLeft, frontRight, backLeft, backRight; + + private Pose2d pose = new Pose2d(); + + public MecanumDrive(/* HardwareMap hardwareMap */) { + // frontLeft = hardwareMap.get(DcMotor.class, "frontLeft"); + // frontRight = hardwareMap.get(DcMotor.class, "frontRight"); + // backLeft = hardwareMap.get(DcMotor.class, "backLeft"); + // backRight = hardwareMap.get(DcMotor.class, "backRight"); + } + + @Override + public void setPower(double x, double y, double rotation) { + // Mecanum kinematics + double frontLeftPower = y + x + rotation; + double frontRightPower = y - x - rotation; + double backLeftPower = y - x + rotation; + double backRightPower = y + x - rotation; + + // Normalize + double max = 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 / max); + // frontRight.setPower(frontRightPower / max); + // backLeft.setPower(backLeftPower / max); + // backRight.setPower(backRightPower / max); + + System.out.printf("Drive: FL=%.2f FR=%.2f BL=%.2f BR=%.2f%n", + frontLeftPower/max, frontRightPower/max, backLeftPower/max, backRightPower/max); + } + + @Override + public Pose2d getPose() { + return pose; + } + + @Override + public double getHeading() { + return pose.heading; + } +} +EOF + +cat > src/main/java/robot/opmodes/TeleOp.java << 'EOF' +package robot.opmodes; + +// Uncomment when deploying to robot: +// import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; +// import com.qualcomm.robotcore.eventloop.opmode.TeleOp; + +import robot.subsystems.Drive; +import robot.hardware.MecanumDrive; + +/** + * Main TeleOp mode. + * Uncomment annotations and imports when deploying. + */ +// @TeleOp(name="Main TeleOp", group="Competition") +public class TeleOp /* extends LinearOpMode */ { + + /* + @Override + public void runOpMode() { + // Initialize hardware + Drive.Hardware hardware = new MecanumDrive(hardwareMap); + Drive drive = new Drive(hardware); + + telemetry.addLine("Ready!"); + telemetry.update(); + + waitForStart(); + + while (opModeIsActive()) { + // Driver controls + double x = -gamepad1.left_stick_x; + double y = -gamepad1.left_stick_y; + double rotation = -gamepad1.right_stick_x; + + drive.drive(x, y, rotation); + + // Telemetry + telemetry.addData("Pose", drive.getPose()); + telemetry.update(); + + sleep(20); + } + } + */ + + // Stub for testing compilation + public static void main(String[] args) { + System.out.println("TeleOp OpMode - deploy to robot to run"); + } +} +EOF + +cat > src/test/java/robot/subsystems/DriveTest.java << 'EOF' +package robot.subsystems; + +import org.junit.jupiter.api.Test; +import robot.Pose2d; +import static org.junit.jupiter.api.Assertions.*; + +class DriveTest { + + // Simple inline mock + static class MockHardware implements Drive.Hardware { + double lastX, lastY, lastRot; + Pose2d pose = new Pose2d(); + + @Override + public void setPower(double x, double y, double rotation) { + lastX = x; lastY = y; lastRot = rotation; + } + + @Override + public Pose2d getPose() { return pose; } + + @Override + public double getHeading() { return pose.heading; } + } + + @Test + void testRobotRelativeDrive() { + MockHardware mock = new MockHardware(); + Drive drive = new Drive(mock); + drive.setFieldRelative(false); + + drive.drive(1.0, 0.5, 0.0); + + assertEquals(1.0, mock.lastX, 0.01); + assertEquals(0.5, mock.lastY, 0.01); + assertEquals(0.0, mock.lastRot, 0.01); + } + + @Test + void testGetPose() { + MockHardware mock = new MockHardware(); + mock.pose = new Pose2d(1, 2, 0.5); + Drive drive = new Drive(mock); + + Pose2d pose = drive.getPose(); + assertEquals(1, pose.x, 0.01); + assertEquals(2, pose.y, 0.01); + } +} +EOF + +# Step 5: Create README +cat > LICENSE << 'EOF' +MIT License + +Copyright (c) $(date +%Y) $PROJECT_NAME + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +EOF + +cat > README.md << EOF +# $PROJECT_NAME + +FTC Robot Code - Clean architecture, fully testable on PC. + +## Quick Start + +\`\`\`bash +# Run tests (on PC, no robot needed) +./gradlew test + +# Watch tests (auto-rerun on save) +./gradlew test --continuous + +# Check for compile errors +./build.sh + +# Deploy to robot +./deploy-to-robot.sh +\`\`\` + +## Project Structure + +\`\`\` +src/ +├── main/java/robot/ +│ ├── subsystems/ # Your robot logic (tested on PC) +│ ├── hardware/ # Hardware implementations +│ └── opmodes/ # FTC OpModes +└── test/java/robot/ # Unit tests (run without robot) +\`\`\` + +## Development Workflow + +### 1. Develop on PC (Fast Iteration) + +\`\`\`bash +./gradlew test --continuous +\`\`\` + +Write your robot logic in \`subsystems/\`, test with mocks. No robot required! + +### 2. Check Build + +\`\`\`bash +./build.sh +\`\`\` + +Catches compile errors before deployment. + +### 3. Deploy to Robot + +\`\`\`bash +./deploy-to-robot.sh +\`\`\` + +Builds and installs to Control Hub (USB or WiFi). + +## Adding Subsystems + +Every subsystem follows this pattern: + +\`\`\`java +public class MySubsystem { + private final Hardware hardware; + + public MySubsystem(Hardware hardware) { + this.hardware = hardware; + } + + // Your robot logic here - tests on PC! + public void doSomething() { + hardware.doThing(); + } + + // Inner interface - implement for robot + public interface Hardware { + void doThing(); + } +} +\`\`\` + +Test with inline mock: + +\`\`\`java +class MySubsystemTest { + static class MockHardware implements MySubsystem.Hardware { + boolean didThing = false; + public void doThing() { didThing = true; } + } + + @Test + void test() { + MockHardware mock = new MockHardware(); + MySubsystem sys = new MySubsystem(mock); + sys.doSomething(); + assertTrue(mock.didThing); + } +} +\`\`\` + +## Commands + +| Command | Description | +|---------|-------------| +| \`./gradlew test\` | Run all unit tests | +| \`./gradlew test --continuous\` | Watch mode (auto-rerun on save) | +| \`./build.sh\` | Build and check for errors | +| \`./build.sh --clean\` | Clean build | +| \`./deploy-to-robot.sh\` | Deploy to Control Hub | +| \`./deploy-to-robot.sh --help\` | Show all deployment options | + +## Configuration + +- **FTC SDK Location**: \`$FTC_SDK_DIR\` +- **FTC SDK Version**: \`$FTC_VERSION\` +- **Java Version**: 11 + +## Generated By + +[ftc-new-project](https://github.com/your-org/ftc-project-gen) - Clean FTC robot projects + +## License + +MIT - See LICENSE file +EOF + +cat > .gitignore << 'EOF' +# Gradle +.gradle/ +build/ +gradle-app.setting +!gradle-wrapper.jar + +# IntelliJ IDEA +.idea/ +*.iml +*.ipr +*.iws +out/ + +# VS Code +.vscode/ + +# Eclipse +.classpath +.project +.settings/ +bin/ + +# macOS +.DS_Store + +# Linux +*~ + +# Windows +Thumbs.db +desktop.ini + +# Logs +*.log + +# Test output +*.class +hs_err_pid* +EOF + +cat > .gitattributes << 'EOF' +# Auto detect text files and perform LF normalization +* text=auto + +# Java sources +*.java text diff=java +*.gradle text diff=java +*.gradle.kts text diff=java + +# Shell scripts +*.sh text eol=lf + +# These files are text and should be normalized (convert crlf => lf) +*.css text +*.html text +*.js text +*.json text +*.md text +*.txt text +*.xml text +*.yaml text +*.yml text + +# These files are binary and should be left untouched +*.class binary +*.jar binary +*.so binary +*.dll binary +*.dylib binary +EOF + +cat > .editorconfig << 'EOF' +# EditorConfig for consistent coding styles +# Works with: VS Code, Sublime, IntelliJ, etc. +# https://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.java] +indent_style = space +indent_size = 4 + +[*.{gradle,gradle.kts}] +indent_style = space +indent_size = 4 + +[*.{md,markdown}] +trim_trailing_whitespace = false + +[*.{sh,bash}] +indent_style = space +indent_size = 2 +EOF + +# Initialize git repo +git init -q +git add . +git commit -q -m "Initial commit: $PROJECT_NAME + +Generated by ftc-new-project +FTC SDK: $FTC_VERSION + +Project structure: +- Pure Java core for PC testing +- FTC hardware implementations +- Deployment scripts included +" + +echo "" +echo "✓ Git repository initialized" + +# Step 6: Create Gradle wrapper +mkdir -p gradle/wrapper + +cat > gradle/wrapper/gradle-wrapper.properties << 'EOF' +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +EOF + +cat > gradlew << 'GRADLEW' +#!/bin/sh +GRADLE_VERSION=8.5 +GRADLE_HOME="$HOME/.gradle/wrapper/dists/gradle-${GRADLE_VERSION}-bin" + +if [ ! -d "$GRADLE_HOME" ]; then + echo "Downloading Gradle ${GRADLE_VERSION}..." + mkdir -p "$GRADLE_HOME" + wget -q "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" -O /tmp/gradle.zip || { + echo "Download failed. Install gradle manually from https://gradle.org" + exit 1 + } + unzip -q /tmp/gradle.zip -d "$GRADLE_HOME/.." + rm /tmp/gradle.zip +fi + +exec "$GRADLE_HOME/../gradle-${GRADLE_VERSION}/bin/gradle" "$@" +GRADLEW +chmod +x gradlew + +# Step 7: Create build script +cat > build.sh << 'BUILDSCRIPT' +#!/bin/bash +# Build robot code for FTC deployment + +set -e + +FTC_SDK_DIR="${FTC_SDK_DIR:-$HOME/ftc-sdk}" + +show_help() { + cat << EOF +╔════════════════════════════════════════════════════════════════╗ +║ Build FTC Robot Code - Help ║ +╚════════════════════════════════════════════════════════════════╝ + +Builds your robot code into an APK without deploying to Control Hub. +Useful for checking compile errors before deployment. + +USAGE: + $(basename $0) [options] + +OPTIONS: + -h, --help Show this help + -c, --clean Clean build (remove old artifacts) + -v, --verbose Verbose output + --check-only Only check if code compiles (no APK) + +WHAT THIS SCRIPT DOES: + + 1. Runs local tests (./gradlew test) + 2. Deploys code to FTC SDK + 3. Builds APK (checks for compile errors) + 4. Shows APK location + +WHY USE THIS: + + ✓ Check for compile errors before deploying + ✓ Faster than full deployment + ✓ Safe - doesn't touch the robot + ✓ Validates FTC imports are correct + +EXAMPLES: + + # Normal build + ./build.sh + + # Clean build (fresh start) + ./build.sh --clean + + # Just check compilation, don't build APK + ./build.sh --check-only + + # Verbose output + ./build.sh --verbose + +WORKFLOW: + + During development: + ./gradlew test --continuous # Fast PC testing + + Before deployment: + ./build.sh # Check compile errors + + Ready to deploy: + ./deploy-to-robot.sh # Build and deploy to robot + +SEE ALSO: + + ./gradlew test Run unit tests + ./deploy-to-robot.sh Build and deploy to robot + ./deploy-to-robot.sh --help Full deployment options + +EOF + exit 0 +} + +# Parse arguments +CLEAN_BUILD=false +VERBOSE=false +CHECK_ONLY=false + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + ;; + -c|--clean) + CLEAN_BUILD=true + shift + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + --check-only) + CHECK_ONLY=true + shift + ;; + *) + echo "Error: Unknown option: $1" + echo "Run with --help for usage information" + exit 1 + ;; + esac +done + +GRADLE_ARGS="" +if [ "$VERBOSE" = true ]; then + GRADLE_ARGS="--info" +fi + +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Build FTC Robot Code ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" + +# Step 1: Run tests +echo ">>> Step 1: Running unit tests..." +./gradlew test $GRADLE_ARGS || { + echo "" + echo "✗ Tests failed!" + echo "Fix test errors before building for robot." + exit 1 +} +echo "✓ All tests passed" +echo "" + +if [ "$CHECK_ONLY" = true ]; then + echo ">>> Check complete (--check-only)" + echo "✓ Code compiles and tests pass" + exit 0 +fi + +# Step 2: Deploy to SDK +echo ">>> Step 2: Deploying code to FTC SDK..." +./gradlew deployToSDK $GRADLE_ARGS || { + echo "" + echo "✗ Failed to deploy code" + exit 1 +} +echo "" + +# Step 3: Build APK +if [ "$CLEAN_BUILD" = true ]; then + echo ">>> Step 3: Clean build..." + cd "$FTC_SDK_DIR" + ./gradlew clean $GRADLE_ARGS + echo "" +fi + +echo ">>> Step 3: Building APK..." +cd "$FTC_SDK_DIR" + +# Check for Android SDK before building +if [ ! -f "local.properties" ] && [ -z "$ANDROID_HOME" ]; then + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ Android SDK Not Configured ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "FTC SDK needs Android SDK to build the APK." + echo "" + echo "Quick setup:" + echo "" + echo "1. Install Android Studio:" + echo " https://developer.android.com/studio" + echo "" + echo "2. After install, Android SDK is usually at:" + echo " Linux: ~/Android/Sdk" + echo " macOS: ~/Library/Android/sdk" + echo " Windows: C:\\Users\\\\AppData\\Local\\Android\\Sdk" + echo "" + echo "3. Create local.properties in FTC SDK:" + echo " echo 'sdk.dir=$HOME/Android/Sdk' > $FTC_SDK_DIR/local.properties" + echo "" + echo "Or set environment variable:" + echo " export ANDROID_HOME=~/Android/Sdk" + echo " echo 'export ANDROID_HOME=~/Android/Sdk' >> ~/.bashrc" + echo "" + echo "After setup, run ./build.sh again" + echo "" + exit 1 +fi + +./gradlew assembleDebug $GRADLE_ARGS || { + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ Build Failed - Compile Errors ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "Common issues:" + echo "" + echo "1. FTC imports not uncommented" + echo " → Edit hardware and OpMode files" + echo " → Uncomment: import com.qualcomm.robotcore..." + echo "" + echo "2. Missing FTC SDK" + echo " → Check: ls $FTC_SDK_DIR" + echo " → Should see: RobotCore, Hardware, FtcRobotController" + echo "" + echo "3. Syntax errors" + echo " → Check error messages above" + echo " → Fix in your src/ files" + echo "" + echo "Run with --verbose for more details" + echo "" + exit 1 +} + +APK_PATH="$FTC_SDK_DIR/FtcRobotController/build/outputs/apk/debug/FtcRobotController-debug.apk" + +echo "" +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ ✓ Build Successful! ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" +echo "APK Location:" +echo " $APK_PATH" +echo "" +echo "APK Size: $(du -h "$APK_PATH" | cut -f1)" +echo "" +echo "Next steps:" +echo "" +echo " Deploy to robot:" +echo " ./deploy-to-robot.sh --skip-build" +echo "" +echo " Or just run full deployment:" +echo " ./deploy-to-robot.sh" +echo "" +BUILDSCRIPT +chmod +x build.sh + +# Step 8: Create deployment script +cat > deploy-to-robot.sh << 'DEPLOYSCRIPT' +#!/bin/bash +# Deploy to FTC Control Hub + +set -e + +CONTROL_HUB_IP="${CONTROL_HUB_IP:-192.168.43.1}" +CONTROL_HUB_PORT="${CONTROL_HUB_PORT:-5555}" +FTC_SDK_DIR="${FTC_SDK_DIR:-$HOME/ftc-sdk}" + +show_help() { + cat << EOF +╔════════════════════════════════════════════════════════════════╗ +║ Deploy to FTC Control Hub - Help ║ +╚════════════════════════════════════════════════════════════════╝ + +Builds your robot code and deploys it to the Control Hub. + +USAGE: + $(basename $0) [options] + +OPTIONS: + -h, --help Show this help + -u, --usb Force USB connection (skip WiFi) + -w, --wifi Force WiFi connection (skip USB) + -i, --ip
Custom Control Hub IP address + -p, --port Custom adb port (default: 5555) + --skip-build Skip building, just install existing APK + --build-only Build APK but don't install + +CONNECTION METHODS: + + 1. USB (Recommended - Most Reliable) + ──────────────────────────────────────── + • Plug Control Hub into computer via USB + • Run: ./deploy-to-robot.sh + • That's it! + + Pros: Fast, reliable, no network needed + Cons: Requires cable + + 2. WiFi Direct (Default FTC Network) + ──────────────────────────────────────── + • Connect computer to 'FIRST-xxxx-RC' network + • Run: ./deploy-to-robot.sh + • Uses IP: 192.168.43.1:5555 (FTC standard) + + Pros: Wireless, convenient + Cons: Can be slower, network issues + + 3. Custom Network + ──────────────────────────────────────── + • Control Hub connected to your network + • Find IP: Check router or Control Hub settings + • Run: ./deploy-to-robot.sh -i 192.168.1.100 + + Pros: Can use your network + Cons: Need to find IP address + +EXAMPLES: + + # Auto-detect (tries USB first, then WiFi) + ./deploy-to-robot.sh + + # Force USB only + ./deploy-to-robot.sh --usb + + # Force WiFi only + ./deploy-to-robot.sh --wifi + + # Custom IP address + ./deploy-to-robot.sh -i 192.168.1.100 + + # Custom IP with environment variable + CONTROL_HUB_IP=10.0.0.50 ./deploy-to-robot.sh + + # Build only, no install (for testing build) + ./deploy-to-robot.sh --build-only + + # Install existing APK (skip build) + ./deploy-to-robot.sh --skip-build + +TROUBLESHOOTING: + + "adb: command not found" + ├─> Install Android Platform Tools: + │ Ubuntu/Debian: sudo apt install android-tools-adb + │ Arch: sudo pacman -S android-tools + │ Download: https://developer.android.com/studio/releases/platform-tools + + "cannot connect to 192.168.43.1:5555" + ├─> Make sure you're connected to FIRST-xxxx-RC WiFi network + ├─> Try USB instead: ./deploy-to-robot.sh --usb + └─> Check Control Hub is powered on + + "device unauthorized" + ├─> On Control Hub, allow USB debugging + └─> Check Control Hub screen for authorization prompt + + "no devices/emulators found" + ├─> USB: Check cable, try different port + ├─> WiFi: Verify network connection + └─> Run: adb devices (to see what adb sees) + +WHAT THIS SCRIPT DOES: + + 1. Deploys your code to ~/ftc-sdk/TeamCode + 2. Builds APK using FTC SDK + 3. Connects to Control Hub (USB or WiFi) + 4. Installs APK to Control Hub + 5. Your OpModes appear on Driver Station + +AFTER DEPLOYMENT: + + On Driver Station: + 1. Go to OpModes menu + 2. Select TeleOp → "Main TeleOp" + 3. Press INIT, then START + 4. Your code is now running! + +ENVIRONMENT VARIABLES: + + CONTROL_HUB_IP Control Hub IP (default: 192.168.43.1) + CONTROL_HUB_PORT adb port (default: 5555) + FTC_SDK_DIR FTC SDK location (default: ~/ftc-sdk) + + Example: + CONTROL_HUB_IP=192.168.1.100 FTC_SDK_DIR=~/my-ftc ./deploy-to-robot.sh + +SEE ALSO: + + adb devices List connected devices + adb connect Connect via WiFi manually + adb disconnect Disconnect WiFi connection + +EOF + exit 0 +} + +# Parse arguments +FORCE_USB=false +FORCE_WIFI=false +SKIP_BUILD=false +BUILD_ONLY=false + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + ;; + -u|--usb) + FORCE_USB=true + shift + ;; + -w|--wifi) + FORCE_WIFI=true + shift + ;; + -i|--ip) + CONTROL_HUB_IP="$2" + shift 2 + ;; + -p|--port) + CONTROL_HUB_PORT="$2" + shift 2 + ;; + --skip-build) + SKIP_BUILD=true + shift + ;; + --build-only) + BUILD_ONLY=true + shift + ;; + *) + echo "Error: Unknown option: $1" + echo "Run with --help for usage information" + exit 1 + ;; + esac +done + +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Deploy to FTC Control Hub ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" + +if [ "$SKIP_BUILD" = false ]; then + # Step 1: Deploy code to SDK + echo ">>> Step 1: Deploying code to FTC SDK..." + ./gradlew deployToSDK || { + echo "Error: Failed to deploy code" + exit 1 + } + echo "" + + # Step 2: Build APK + echo ">>> Step 2: Building APK..." + cd "$FTC_SDK_DIR" + ./gradlew assembleDebug || { + echo "Error: Failed to build APK" + exit 1 + } + echo "" +else + echo ">>> Skipping build (--skip-build)" + cd "$FTC_SDK_DIR" + echo "" +fi + +APK_PATH="$FTC_SDK_DIR/FtcRobotController/build/outputs/apk/debug/FtcRobotController-debug.apk" + +if [ ! -f "$APK_PATH" ]; then + echo "Error: APK not found at $APK_PATH" + echo "Try building first: ./deploy-to-robot.sh (without --skip-build)" + exit 1 +fi + +echo "✓ APK ready: $(basename $APK_PATH)" +echo "" + +if [ "$BUILD_ONLY" = true ]; then + echo ">>> Build complete (--build-only)" + echo "" + echo "APK location: $APK_PATH" + echo "" + echo "To install manually:" + echo " adb install -r $APK_PATH" + exit 0 +fi + +# Step 3: Install to robot +echo ">>> Step 3: Installing to Control Hub..." +echo "" + +# Check if adb is available +if ! command -v adb &> /dev/null; then + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ ERROR: adb not found ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "Android Debug Bridge (adb) is required for deployment." + echo "" + echo "Install it:" + echo " Ubuntu/Debian: sudo apt install android-tools-adb" + echo " Arch Linux: sudo pacman -S android-tools" + echo " macOS: brew install android-platform-tools" + echo "" + echo "Or download Android Platform Tools:" + echo " https://developer.android.com/studio/releases/platform-tools" + echo "" + exit 1 +fi + +INSTALLED=false + +# Try USB first (unless forcing WiFi) +if [ "$FORCE_WIFI" = false ]; then + echo "Checking for USB connection..." + USB_DEVICES=$(adb devices | grep -v "List" | grep "device$" | wc -l) + + if [ "$USB_DEVICES" -gt 0 ]; then + echo "✓ Control Hub connected via USB" + echo "" + adb install -r "$APK_PATH" && INSTALLED=true + else + if [ "$FORCE_USB" = true ]; then + echo "✗ No USB device found (--usb specified)" + echo "" + echo "Make sure:" + echo " • Control Hub is powered on" + echo " • USB cable is connected" + echo " • USB debugging is enabled on Control Hub" + echo "" + echo "Check with: adb devices" + exit 1 + else + echo " No USB device detected" + fi + fi +fi + +# Try WiFi if USB didn't work (unless forcing USB) +if [ "$INSTALLED" = false ] && [ "$FORCE_USB" = false ]; then + echo "Trying WiFi connection to $CONTROL_HUB_IP:$CONTROL_HUB_PORT..." + echo "" + + # Try to connect + adb connect "$CONTROL_HUB_IP:$CONTROL_HUB_PORT" 2>&1 | grep -v "cannot connect" || true + + # Wait for connection + sleep 2 + + # Check if connected + if adb devices | grep -q "$CONTROL_HUB_IP"; then + echo "✓ Connected via WiFi" + echo "" + adb install -r "$APK_PATH" && INSTALLED=true + fi +fi + +if [ "$INSTALLED" = false ]; then + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ Could not connect to Control Hub ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "Connection options:" + echo "" + echo "1. USB Connection (Recommended)" + echo " ────────────────────────────────" + echo " • Plug Control Hub into computer with USB cable" + echo " • Run: ./deploy-to-robot.sh --usb" + echo "" + echo "2. WiFi Direct Connection" + echo " ────────────────────────────────" + echo " • Connect to 'FIRST-xxxx-RC' network" + echo " • Run: ./deploy-to-robot.sh --wifi" + echo " • Default IP: 192.168.43.1:5555" + echo "" + echo "3. Custom Network" + echo " ────────────────────────────────" + echo " • Find Control Hub IP on your network" + echo " • Run: ./deploy-to-robot.sh -i YOUR_IP" + echo "" + echo "4. Manual Install" + echo " ────────────────────────────────" + echo " • APK built at: $APK_PATH" + echo " • Use Android Studio to deploy" + echo " • Or: adb connect :5555 && adb install -r " + echo "" + echo "Debug with:" + echo " adb devices # See connected devices" + echo " adb connect 192.168.43.1 # Connect manually" + echo "" + echo "Run with --help for more options" + echo "" + exit 1 +fi + +echo "" +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ ✓ Deployment Complete! ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" +echo "On Driver Station:" +echo " 1. Go to: OpModes menu" +echo " 2. Select: TeleOp → 'Main TeleOp'" +echo " 3. Press: INIT, then START" +echo "" +echo "Your code is now running on the robot! 🤖" +echo "" +DEPLOYSCRIPT +chmod +x deploy-to-robot.sh + +echo "" +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ ✓ Project Created! ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" +echo "Project: $PROJECT_NAME" +echo "Location: $PROJECT_DIR" +echo "SDK: $FTC_SDK_DIR ($FTC_VERSION)" +echo "Git: Initialized with initial commit" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " QUICK START" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "1. Enter project and run tests:" +echo " cd $PROJECT_NAME" +echo " ./gradlew test" +echo "" +echo "2. Watch tests (auto-rerun on save):" +echo " ./gradlew test --continuous" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " PROJECT STRUCTURE" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "$PROJECT_NAME/" +echo "├── src/main/java/robot/" +echo "│ ├── subsystems/" +echo "│ │ └── Drive.java ← Your robot logic (EDIT THIS)" +echo "│ ├── hardware/" +echo "│ │ └── MecanumDrive.java ← Hardware impl (EDIT THIS)" +echo "│ └── opmodes/" +echo "│ └── TeleOp.java ← FTC OpMode (EDIT THIS)" +echo "│" +echo "└── src/test/java/robot/" +echo " └── subsystems/" +echo " └── DriveTest.java ← Unit tests (ADD MORE)" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " DEVELOPMENT WORKFLOW" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Day-to-day development (on your PC):" +echo "" +echo " 1. Edit code in src/main/java/robot/" +echo " 2. Edit tests in src/test/java/robot/" +echo " 3. Run: ./gradlew test --continuous" +echo " 4. Tests pass → you're good!" +echo "" +echo "Example: Add a new subsystem:" +echo "" +echo " # Create subsystem with inner Hardware interface" +echo " src/main/java/robot/subsystems/Intake.java" +echo "" +echo " # Create test with inline mock" +echo " src/test/java/robot/subsystems/IntakeTest.java" +echo "" +echo " # Run tests" +echo " ./gradlew test" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " DEPLOYMENT TO ROBOT" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "When ready to test on actual robot:" +echo "" +echo " 1. Uncomment FTC imports in:" +echo " - src/main/java/robot/hardware/MecanumDrive.java" +echo " - src/main/java/robot/opmodes/TeleOp.java" +echo "" +echo " 2. Run deployment script:" +echo " ./deploy-to-robot.sh" +echo "" +echo " The script will:" +echo " ✓ Deploy your code to SDK" +echo " ✓ Build APK" +echo " ✓ Install to Control Hub (via USB or WiFi)" +echo "" +echo " Connection methods:" +echo " • USB: Just plug in and run" +echo " • WiFi: Connect to 'FIRST-xxxx-RC' network (IP: 192.168.43.1)" +echo " • Custom: CONTROL_HUB_IP=192.168.1.x ./deploy-to-robot.sh" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " KEY FILES TO EDIT" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Start here:" +echo "" +echo " src/main/java/robot/subsystems/Drive.java" +echo " → Your drive logic, tested on PC" +echo "" +echo " src/main/java/robot/hardware/MecanumDrive.java" +echo " → Real motor control (uncomment FTC code when deploying)" +echo "" +echo " src/main/java/robot/opmodes/TeleOp.java" +echo " → Your main OpMode (uncomment FTC code when deploying)" +echo "" +echo " src/test/java/robot/subsystems/DriveTest.java" +echo " → Unit tests - add more as you build!" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " USEFUL COMMANDS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " Development (on PC):" +echo " ./gradlew test Run all tests" +echo " ./gradlew test --continuous Watch mode (auto-rerun)" +echo "" +echo " Before deployment:" +echo " ./build.sh Check for compile errors" +echo " ./build.sh --clean Clean build" +echo "" +echo " Deploy to robot:" +echo " ./deploy-to-robot.sh Full deployment (build + install)" +echo " ./deploy-to-robot.sh --help Show all deployment options" +echo "" +echo " Other:" +echo " ./gradlew clean Clean build artifacts" +echo " ./gradlew tasks List all available tasks" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " TIPS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " • Keep FTC imports commented during development" +echo " • Write tests for everything - they run instantly on PC" +echo " • Use './gradlew test --continuous' for fast iteration" +echo " • See README.md for detailed documentation" +echo " • Multiple projects can share the same FTC SDK!" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Ready to start coding? Run:" +echo "" +echo " cd $PROJECT_NAME && ./gradlew test --continuous" +echo "" +echo "Happy coding! 🤖" +echo "" diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..f555457 --- /dev/null +++ b/install.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Install FTC project generator + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +INSTALL_DIR="/usr/local/bin" + +echo "FTC Project Generator - Installation" +echo "" + +# Check if running as root for system install +if [ -w "$INSTALL_DIR" ]; then + echo "Installing to $INSTALL_DIR (system-wide)..." + ln -sf "$SCRIPT_DIR/ftc-new-project" "$INSTALL_DIR/ftc-new-project" + echo "✓ Installed! Use 'ftc-new-project' from anywhere." +else + echo "No write access to $INSTALL_DIR" + echo "" + echo "Choose installation method:" + echo "" + echo "1. System-wide (requires sudo):" + echo " sudo $0" + echo "" + echo "2. User-only (no sudo needed):" + echo " mkdir -p ~/bin" + echo " ln -sf $SCRIPT_DIR/ftc-new-project ~/bin/ftc-new-project" + echo " echo 'export PATH=\$PATH:~/bin' >> ~/.bashrc" + echo " source ~/.bashrc" + echo "" + echo "3. Add this directory to PATH:" + echo " echo 'export PATH=\$PATH:$SCRIPT_DIR' >> ~/.bashrc" + echo " source ~/.bashrc" + exit 1 +fi