Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Tests

on:
pull_request:
push:
branches: [master]
workflow_dispatch:

jobs:
unit-tests:
name: TeamCode unit tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

# JDK 17: the project compiles with source/target 8, and JDK 21 emits a
# deprecation warning for that. 17 keeps the build output clean and is
# what AGP 8.7 targets.
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: gradle

# The ubuntu-latest runner ships with the Android SDK preinstalled and
# ANDROID_HOME set, which AGP needs even for local JVM unit tests.
- name: Run unit tests
run: ./gradlew :TeamCode:testDebugUnitTest --no-daemon

- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report
path: TeamCode/build/reports/tests/testDebugUnitTest/
retention-days: 14
26 changes: 26 additions & 0 deletions TeamCode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# TeamCode organization

Robot code belongs under `src/main/java/org/firstinspires/ftc/teamcode`.

```text
teamcode/
├── actions/ Reusable, nonblocking robot behaviors
├── autonomous/
│ ├── paths/ Pedro path and pose definitions
│ └── routines/ Autonomous sequences built from paths and actions
├── config/ Robot-wide names, limits, and setpoints
├── control/ Gamepad mappings and TeleOp subsystem control
├── opmodes/
│ ├── autonomous/ Competition autonomous entry points
│ ├── teleop/ Competition driver-controlled entry points
│ └── tests/ Test, calibration, and diagnostic OpModes
├── pedro/ Pedro configuration and tuning supplied by Quickstart
├── subsystems/ Hardware-owning classes, grouped by mechanism
└── util/ Small helpers with no mechanism ownership
```

Keep registered OpModes thin. They should select and coordinate behavior while
subsystems own hardware access and mechanism state. Add a child package under
`subsystems` when a mechanism is known instead of creating speculative mechanism
folders at the start of the season.

13 changes: 13 additions & 0 deletions TeamCode/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,21 @@ android {
packagingOptions {
jniLibs.useLegacyPackaging true
}

testOptions {
unitTests {
// Stub out android.* calls (e.g. android.util.Log) instead of throwing
// "not mocked". Pure classes like Rates don't need this, but anything
// that touches an SDK type will.
returnDefaultValues = true
}
}
}

dependencies {
implementation project(':FtcRobotController')

// Local JVM unit tests (src/test/java). Run with:
// ./gradlew :TeamCode:testDebugUnitTest
testImplementation 'junit:junit:4.13.2'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Reusable, nonblocking behaviors that coordinate one or more subsystems.
* Actions may be shared by autonomous and TeleOp code.
*/
package org.firstinspires.ftc.teamcode.actions;

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Pedro path, pose, and route definitions with no OpMode registration.
*/
package org.firstinspires.ftc.teamcode.autonomous.paths;

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Reusable autonomous sequences composed from paths, actions, and subsystems.
*/
package org.firstinspires.ftc.teamcode.autonomous.routines;

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Robot-wide hardware names, operating limits, setpoints, and season configuration.
*/
package org.firstinspires.ftc.teamcode.config;

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Gamepad mappings and TeleOp controllers. Control classes translate driver input
* into subsystem calls without owning hardware devices or mechanism state.
*/
package org.firstinspires.ftc.teamcode.control;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Registered competition autonomous OpModes. Keep entry points thin and delegate
* reusable behavior to autonomous routines, actions, and subsystems.
*/
package org.firstinspires.ftc.teamcode.opmodes.autonomous;

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Registered competition driver-controlled OpModes.
*/
package org.firstinspires.ftc.teamcode.opmodes.teleop;

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Test, calibration, and diagnostic OpModes.
*/
package org.firstinspires.ftc.teamcode.opmodes.tests;

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Robot mechanisms that own their hardware devices and internal control state.
* Create one child package per mechanism when the robot design is established.
*/
package org.firstinspires.ftc.teamcode.subsystems;

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.firstinspires.ftc.teamcode.util;

public class Rates {

private double centerRate;
private double maxRate;
private double expo;
private double deadzone;

public Rates(double centerRate, double maxRate, double expo, double deadzone) {
if (centerRate > maxRate) {
throw new IllegalArgumentException("Center rate must be less than max rate.");
}

this.centerRate = centerRate;
this.maxRate = maxRate;
this.expo = expo;
this.deadzone = deadzone;
}

/**
* apply Deadzone prevents robot twitches while the joystick is moving without intent.
* @param stickPosition
* @return
*/
private double applyDeadzone(double stickPosition) {
double magnitude = Math.abs(stickPosition);
if (magnitude < deadzone) {
return 0.0;
}

return Math.signum(stickPosition) * (magnitude - deadzone) / (1 - deadzone);
}

/**
*
* @param stickPosition A parameter read from a controller that should be in the domain {0..1}
* @return A power value representative of what we want to hand to a Drive train / motors
*/
public double apply(double stickPosition) {
if (Math.abs(stickPosition) > 1) {
throw new IllegalArgumentException("Stick Position cannot be more than 1 or less than -1");
}

stickPosition = applyDeadzone(stickPosition);
double actualRatesExpoFactor =
Math.abs(stickPosition) * (Math.pow(stickPosition, 5) * expo + stickPosition * (1-expo));

return (centerRate * stickPosition) +
((maxRate - centerRate) * actualRatesExpoFactor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Small, broadly reusable helpers that do not own robot mechanisms.
*/
package org.firstinspires.ftc.teamcode.util;

Loading