mirror of
https://github.com/TimeCrafters/FTC_2022
synced 2025-12-16 08:52:35 +00:00
Setup new repo, implemented basic mecanum drive for minibot
This commit is contained in:
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
6
.idea/compiler.xml
generated
Normal file
6
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="11" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
30
.idea/jarRepositories.xml
generated
Normal file
30
.idea/jarRepositories.xml
generated
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="MavenRepo" />
|
||||||
|
<option name="name" value="MavenRepo" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="BintrayJCenter" />
|
||||||
|
<option name="name" value="BintrayJCenter" />
|
||||||
|
<option name="url" value="https://jcenter.bintray.com/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="Google" />
|
||||||
|
<option name="name" value="Google" />
|
||||||
|
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/misc.xml
generated
Normal file
9
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
package org.cyberarm.engine.V2;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.qualcomm.robotcore.eventloop.opmode.OpMode;
|
||||||
|
import com.qualcomm.robotcore.hardware.Gamepad;
|
||||||
|
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CyberarmEngine Version 2.0 | October 26th 2018
|
||||||
|
* AN Experimental reimplementation of GoldfishPi's original Engine system.
|
||||||
|
* Designed to be easily maintainable, extensible, and easy to understand.
|
||||||
|
*/
|
||||||
|
public abstract class CyberarmEngine extends OpMode {
|
||||||
|
|
||||||
|
public static CyberarmEngine instance;
|
||||||
|
//Array To Hold States
|
||||||
|
final private CopyOnWriteArrayList<CyberarmState> cyberarmStates = new CopyOnWriteArrayList<>();
|
||||||
|
private int activeStateIndex = 0;
|
||||||
|
private boolean isRunning;
|
||||||
|
|
||||||
|
private final static String TAG = "PROGRAM.ENGINE";
|
||||||
|
public boolean showStateChildrenListInTelemetry = false;
|
||||||
|
|
||||||
|
private GamepadChecker gamepadCheckerGamepad1, gamepadCheckerGamepad2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when INIT button on Driver Station is pushed
|
||||||
|
* ENSURE to call super.init() if you override this method
|
||||||
|
*/
|
||||||
|
public void init() {
|
||||||
|
CyberarmEngine.instance = this;
|
||||||
|
isRunning = false;
|
||||||
|
gamepadCheckerGamepad1 = new GamepadChecker(this, gamepad1);
|
||||||
|
gamepadCheckerGamepad2 = new GamepadChecker(this, gamepad2);
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
||||||
|
isRunning = true;
|
||||||
|
|
||||||
|
for (CyberarmState state: cyberarmStates) {
|
||||||
|
initState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup states for engine to use
|
||||||
|
* For example:
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* public void setup() {
|
||||||
|
* addState(new TestState());
|
||||||
|
* addState(new AnotherState(100, 500));
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public abstract void setup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when START button on Driver Station is pushed
|
||||||
|
* ENSURE to call super.start() if you override this method
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
if (cyberarmStates.size() > 0) {
|
||||||
|
runState(cyberarmStates.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Engine main loop
|
||||||
|
* ENSURE to call super.loop() if you override this method
|
||||||
|
*/
|
||||||
|
public void loop() {
|
||||||
|
CyberarmState state;
|
||||||
|
|
||||||
|
// Try to set state to the current state, if it fails assume that there are no states to run
|
||||||
|
try {
|
||||||
|
state = cyberarmStates.get(activeStateIndex);
|
||||||
|
} catch(IndexOutOfBoundsException e) {
|
||||||
|
// The engine is now out of states.
|
||||||
|
stop();
|
||||||
|
|
||||||
|
telemetry.addLine("" + this.getClass().getSimpleName() + " is out of states to run!");
|
||||||
|
telemetry.addLine();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add telemetry to show currently running state
|
||||||
|
telemetry.addLine("Running state: " +state.getClass().getSimpleName() + ". State: " + activeStateIndex + " of " + (cyberarmStates.size()-1));
|
||||||
|
if (showStateChildrenListInTelemetry && state.hasChildren()) {
|
||||||
|
for(CyberarmState child: state.children) {
|
||||||
|
telemetry.addLine(" Child: " + child.getClass().getSimpleName() + " [" + child.children.size() + "] grandchildren");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
telemetry.addLine();
|
||||||
|
|
||||||
|
if (state.getHasFinished() && state.childrenHaveFinished()) {
|
||||||
|
activeStateIndex++;
|
||||||
|
|
||||||
|
try {
|
||||||
|
state = cyberarmStates.get(activeStateIndex);
|
||||||
|
runState(state);
|
||||||
|
} catch(IndexOutOfBoundsException e) { /* loop will handle this in a few milliseconds */ }
|
||||||
|
|
||||||
|
} else {
|
||||||
|
stateTelemetry(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
gamepadCheckerGamepad1.update();
|
||||||
|
gamepadCheckerGamepad2.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops every known state
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
for (CyberarmState state: cyberarmStates) {
|
||||||
|
stopState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively calls telemetry() on states
|
||||||
|
* @param state State to get telemetry
|
||||||
|
*/
|
||||||
|
private void stateTelemetry(CyberarmState state) {
|
||||||
|
if (!state.getHasFinished()) {
|
||||||
|
state.telemetry();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(CyberarmState childState : state.children) {
|
||||||
|
if (!childState.getHasFinished()) {
|
||||||
|
stateTelemetry(childState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when INIT button on Driver Station is pressed
|
||||||
|
* Recursively initiates states
|
||||||
|
* @param state State to initiate
|
||||||
|
*/
|
||||||
|
private void initState(CyberarmState state) {
|
||||||
|
state.init();
|
||||||
|
|
||||||
|
for(CyberarmState childState : state.children) {
|
||||||
|
initState(childState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when programs ends or STOP button on Driver Station is pressed
|
||||||
|
* Recursively stop states
|
||||||
|
* @param state State to stop
|
||||||
|
*/
|
||||||
|
public void stopState(CyberarmState state) {
|
||||||
|
state.setHasFinished(true);
|
||||||
|
state.stop();
|
||||||
|
|
||||||
|
for(CyberarmState childState : state.children) {
|
||||||
|
stopState(childState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively start up states
|
||||||
|
* @param state State to run
|
||||||
|
*/
|
||||||
|
protected void runState(CyberarmState state) {
|
||||||
|
final CyberarmState finalState = state;
|
||||||
|
// if (state.isRunning()) { return; } // Assume that we have already started running this state
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
finalState.prestart();
|
||||||
|
finalState.start();
|
||||||
|
finalState.startTime = System.currentTimeMillis();
|
||||||
|
finalState.run();
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
for (CyberarmState kid : state.children) {
|
||||||
|
runState(kid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add state to queue, will call init() on state if engine is running
|
||||||
|
* @param state State to add to queue
|
||||||
|
*/
|
||||||
|
public CyberarmState addState(CyberarmState state) {
|
||||||
|
Log.i(TAG, "Adding cyberarmState "+ state.getClass());
|
||||||
|
cyberarmStates.add(state);
|
||||||
|
|
||||||
|
if (isRunning()) { initState(state); }
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts state after the query state plus an offset to ensure logical insertion
|
||||||
|
* @param query State to add state after
|
||||||
|
* @param state State to be inserted
|
||||||
|
* @return CyberarmState
|
||||||
|
*/
|
||||||
|
public CyberarmState insertState(CyberarmState query, CyberarmState state) {
|
||||||
|
int index = cyberarmStates.indexOf(query) + query.insertOffset;
|
||||||
|
Log.i(TAG, "Adding cyberarmState "+ state.getClass());
|
||||||
|
|
||||||
|
cyberarmStates.add(index, state);
|
||||||
|
query.insertOffset++;
|
||||||
|
|
||||||
|
if (isRunning()) { initState(state); }
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buttonDownForStates(CyberarmState state, Gamepad gamepad, String button) {
|
||||||
|
state.buttonDown(gamepad, button);
|
||||||
|
|
||||||
|
for (CyberarmState child : state.children) {
|
||||||
|
child.buttonDown(gamepad, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buttonUpForStates(CyberarmState state, Gamepad gamepad, String button) {
|
||||||
|
state.buttonUp(gamepad, button);
|
||||||
|
|
||||||
|
for (CyberarmState child : state.children) {
|
||||||
|
child.buttonUp(gamepad, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by GamepadChecker when it detects that a gamepad button has been pressed
|
||||||
|
* @param gamepad Gamepad
|
||||||
|
* @param button String
|
||||||
|
*/
|
||||||
|
protected void buttonDown(Gamepad gamepad, String button) {
|
||||||
|
try {
|
||||||
|
buttonDownForStates(cyberarmStates.get(activeStateIndex), gamepad, button);
|
||||||
|
} catch(IndexOutOfBoundsException e){
|
||||||
|
/* loop will handle this in a few milliseconds */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by GamepadChecker when it detects that a gamepad button has been released
|
||||||
|
* @param gamepad Gamepad
|
||||||
|
* @param button String
|
||||||
|
*/
|
||||||
|
protected void buttonUp(Gamepad gamepad, String button) {
|
||||||
|
try {
|
||||||
|
buttonUpForStates(cyberarmStates.get(activeStateIndex), gamepad, button);
|
||||||
|
} catch(IndexOutOfBoundsException e){
|
||||||
|
/* loop will handle this in a few milliseconds */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will return false while Engine.setup() is executing, and be true after.
|
||||||
|
* @return Whether the engine main loop is running
|
||||||
|
*/
|
||||||
|
public boolean isRunning() {
|
||||||
|
return isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return The index used to lookup the current state from cyberarmStates
|
||||||
|
*/
|
||||||
|
public int getActiveStateIndex() {
|
||||||
|
return activeStateIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
235
TeamCode/src/main/java/org/cyberarm/engine/V2/CyberarmState.java
Normal file
235
TeamCode/src/main/java/org/cyberarm/engine/V2/CyberarmState.java
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
package org.cyberarm.engine.V2;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.qualcomm.robotcore.hardware.Gamepad;
|
||||||
|
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A State for use with CyberarmEngineV2
|
||||||
|
*/
|
||||||
|
public abstract class CyberarmState implements Runnable {
|
||||||
|
|
||||||
|
private volatile boolean isRunning, hasFinished;
|
||||||
|
public static String TAG = "PROGRAM.STATE";
|
||||||
|
public CyberarmEngine engine = CyberarmEngine.instance;
|
||||||
|
public CopyOnWriteArrayList<CyberarmState> children = new CopyOnWriteArrayList<>();
|
||||||
|
public long startTime = 0;
|
||||||
|
public int insertOffset = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when INIT button on Driver Station is pushed
|
||||||
|
*/
|
||||||
|
public void init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called just before start to ensure state is in correct state
|
||||||
|
*/
|
||||||
|
protected void prestart() {
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when state has begin to run
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called while State is running
|
||||||
|
*/
|
||||||
|
public abstract void exec();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State's main loop, calls exec() until hasFinished is true
|
||||||
|
* DO NO OVERRIDE
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!hasFinished) {
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place telemetry calls in here instead of inside exec() to have them displayed correctly on the Driver Station
|
||||||
|
* (States update thousands of times per second, resulting in missing or weirdly formatted telemetry if telemetry is added in exec())
|
||||||
|
*/
|
||||||
|
public void telemetry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when Engine is finished
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when GamepadChecker detects that a gamepad button has been pressed
|
||||||
|
* @param gamepad Gamepad
|
||||||
|
* @param button String
|
||||||
|
*/
|
||||||
|
public void buttonDown(Gamepad gamepad, String button) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when GamepadChecker detects that a gamepad button has been released
|
||||||
|
* @param gamepad Gamepad
|
||||||
|
* @param button String
|
||||||
|
*/
|
||||||
|
public void buttonUp(Gamepad gamepad, String button) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a state which runs in parallel with this one
|
||||||
|
*/
|
||||||
|
public CyberarmState addParallelState(CyberarmState state) {
|
||||||
|
Log.i(TAG, "Adding " + state.getClass() + " to " + this.getClass());
|
||||||
|
children.add(state);
|
||||||
|
|
||||||
|
if (isRunning()) {
|
||||||
|
state.init();
|
||||||
|
engine.runState(state);
|
||||||
|
Log.i(TAG, "Started " + state.getClass() + " in " + this.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a state to engine which will run after this one finishes
|
||||||
|
*/
|
||||||
|
public CyberarmState addState(CyberarmState state) {
|
||||||
|
engine.insertState(this, state);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not state has children
|
||||||
|
* @return True if state has children, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean hasChildren() {
|
||||||
|
return (children.size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have all of the states children finished running themselves?
|
||||||
|
* @return Whether or not all children have finished running
|
||||||
|
*/
|
||||||
|
public boolean childrenHaveFinished() {
|
||||||
|
return childrenHaveFinished(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have all of the states children finished running themselves?
|
||||||
|
* @param kids CopyOnWriteArrayList of children to check for hasFinished()
|
||||||
|
* @return Whether or not all children have finished running
|
||||||
|
*/
|
||||||
|
public boolean childrenHaveFinished(CopyOnWriteArrayList<CyberarmState> kids) {
|
||||||
|
boolean allDone = true;
|
||||||
|
|
||||||
|
for (CyberarmState state : kids) {
|
||||||
|
if (!state.hasFinished) {
|
||||||
|
allDone = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (!state.childrenHaveFinished()) {
|
||||||
|
allDone = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return The number of milliseconds this state has been running for
|
||||||
|
*/
|
||||||
|
public double runTime() {
|
||||||
|
return (System.currentTimeMillis() - startTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether state has finished or not
|
||||||
|
* @param value boolean
|
||||||
|
*/
|
||||||
|
public void setHasFinished(boolean value) {
|
||||||
|
hasFinished = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return Get value of hasFinished
|
||||||
|
*/
|
||||||
|
public boolean getHasFinished() {
|
||||||
|
return hasFinished;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return Get value of isRunning
|
||||||
|
*/
|
||||||
|
public boolean isRunning() {
|
||||||
|
return isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param timems How long to sleep in milliseconds
|
||||||
|
*/
|
||||||
|
public void sleep(long timems) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(timems);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param width How many characters wide to be
|
||||||
|
* @param percentCompleted Number between 0.0 and 100.0
|
||||||
|
* @param bar What character to draw the completion bar with
|
||||||
|
* @param padding What character to draw non-completed bar with
|
||||||
|
* @return A string
|
||||||
|
*/
|
||||||
|
public String progressBar(int width, double percentCompleted, String bar, String padding) {
|
||||||
|
String percentCompletedString = "" + Math.round(percentCompleted) + "%";
|
||||||
|
double activeWidth = (width - 2) - percentCompletedString.length();
|
||||||
|
|
||||||
|
String string = "[";
|
||||||
|
double completed = (percentCompleted / 100.0) * activeWidth;
|
||||||
|
|
||||||
|
for (int i = 0; i <= ((int) activeWidth); i++) {
|
||||||
|
if (i == ((int) activeWidth) / 2) {
|
||||||
|
string += percentCompletedString;
|
||||||
|
} else {
|
||||||
|
if (i <= (int) completed && (int) completed > 0) {
|
||||||
|
string += bar;
|
||||||
|
} else {
|
||||||
|
string += padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string += "]";
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param width How many characters wide to be
|
||||||
|
* @param percentCompleted Number between 0.0 and 100.0
|
||||||
|
* @return A string
|
||||||
|
*/
|
||||||
|
public String progressBar(int width, double percentCompleted) {
|
||||||
|
return progressBar(width, percentCompleted, "=", " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package org.cyberarm.engine.V2;
|
||||||
|
|
||||||
|
import com.qualcomm.robotcore.hardware.Gamepad;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class GamepadChecker {
|
||||||
|
private final CyberarmEngine engine;
|
||||||
|
private final Gamepad gamepad;
|
||||||
|
private final HashMap<String, Boolean> buttons = new HashMap<>();
|
||||||
|
|
||||||
|
public GamepadChecker(CyberarmEngine engine, Gamepad gamepad) {
|
||||||
|
this.engine = engine;
|
||||||
|
this.gamepad = gamepad;
|
||||||
|
|
||||||
|
buttons.put("a", false);
|
||||||
|
buttons.put("b", false);
|
||||||
|
buttons.put("x", false);
|
||||||
|
buttons.put("y", false);
|
||||||
|
|
||||||
|
buttons.put("start", false);
|
||||||
|
buttons.put("guide", false);
|
||||||
|
buttons.put("back", false);
|
||||||
|
|
||||||
|
buttons.put("left_bumper", false);
|
||||||
|
buttons.put("right_bumper", false);
|
||||||
|
|
||||||
|
buttons.put("left_stick_button", false);
|
||||||
|
buttons.put("right_stick_button", false);
|
||||||
|
|
||||||
|
buttons.put("dpad_left", false);
|
||||||
|
buttons.put("dpad_right", false);
|
||||||
|
buttons.put("dpad_up", false);
|
||||||
|
buttons.put("dpad_down", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
for (String btn : buttons.keySet()) {
|
||||||
|
try {
|
||||||
|
Field field = gamepad.getClass().getDeclaredField(btn);
|
||||||
|
|
||||||
|
if (field.getBoolean(gamepad)) {
|
||||||
|
if (!buttons.get(btn)) {
|
||||||
|
engine.buttonDown(gamepad, btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.put(btn, true);
|
||||||
|
} else {
|
||||||
|
if (buttons.get(btn)) {
|
||||||
|
engine.buttonUp(gamepad, btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.put(btn, false);
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException|IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
TeamCode/src/main/java/org/cyberarm/engine/V2/README.txt
Normal file
28
TeamCode/src/main/java/org/cyberarm/engine/V2/README.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
CyberarmEngine V2 Architecture
|
||||||
|
|
||||||
|
Engine
|
||||||
|
-> [States]
|
||||||
|
-> [ParallelStates]
|
||||||
|
|
||||||
|
Start with an Engine and override setup():
|
||||||
|
|
||||||
|
public class Engine extends CyberarmEngineV2 {
|
||||||
|
public void setup() {
|
||||||
|
addState(new State(arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTE: states do not need to be passed the instance of Engine as they have a field 'cyberarmEngine'
|
||||||
|
which is set to CyberarmEngineV2.instance when they are created.
|
||||||
|
|
||||||
|
States can have 'children' which are also States, which run in parallel with their parent. There is
|
||||||
|
no fixed limit to how many grandchildren can exist (Children themselves can have children.):
|
||||||
|
|
||||||
|
public class State extends CyberarmEngineStateV2 {
|
||||||
|
public init() {
|
||||||
|
addParallelState(new ParallelState(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
public exec() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
## TeamCode Module
|
|
||||||
|
|
||||||
Welcome!
|
|
||||||
|
|
||||||
This module, TeamCode, is the place where you will write/paste the code for your team's
|
|
||||||
robot controller App. This module is currently empty (a clean slate) but the
|
|
||||||
process for adding OpModes is straightforward.
|
|
||||||
|
|
||||||
## Creating your own OpModes
|
|
||||||
|
|
||||||
The easiest way to create your own OpMode is to copy a Sample OpMode and make it your own.
|
|
||||||
|
|
||||||
Sample opmodes exist in the FtcRobotController module.
|
|
||||||
To locate these samples, find the FtcRobotController module in the "Project/Android" tab.
|
|
||||||
|
|
||||||
Expand the following tree elements:
|
|
||||||
FtcRobotController / java / org.firstinspires.ftc.robotcontroller / external / samples
|
|
||||||
|
|
||||||
A range of different samples classes can be seen in this folder.
|
|
||||||
The class names follow a naming convention which indicates the purpose of each class.
|
|
||||||
The full description of this convention is found in the samples/sample_convention.md file.
|
|
||||||
|
|
||||||
A brief synopsis of the naming convention is given here:
|
|
||||||
The prefix of the name will be one of the following:
|
|
||||||
|
|
||||||
* Basic: This is a minimally functional OpMode used to illustrate the skeleton/structure
|
|
||||||
of a particular style of OpMode. These are bare bones examples.
|
|
||||||
* Sensor: This is a Sample OpMode that shows how to use a specific sensor.
|
|
||||||
It is not intended as a functioning robot, it is simply showing the minimal code
|
|
||||||
required to read and display the sensor values.
|
|
||||||
* Hardware: This is not an actual OpMode, but a helper class that is used to describe
|
|
||||||
one particular robot's hardware devices: eg: for a Pushbot. Look at any
|
|
||||||
Pushbot sample to see how this can be used in an OpMode.
|
|
||||||
Teams can copy one of these to create their own robot definition.
|
|
||||||
* Pushbot: This is a Sample OpMode that uses the Pushbot robot structure as a base.
|
|
||||||
* Concept: This is a sample OpMode that illustrates performing a specific function or concept.
|
|
||||||
These may be complex, but their operation should be explained clearly in the comments,
|
|
||||||
or the header should reference an external doc, guide or tutorial.
|
|
||||||
* Library: This is a class, or set of classes used to implement some strategy.
|
|
||||||
These will typically NOT implement a full OpMode. Instead they will be included
|
|
||||||
by an OpMode to provide some stand-alone capability.
|
|
||||||
|
|
||||||
Once you are familiar with the range of samples available, you can choose one to be the
|
|
||||||
basis for your own robot. In all cases, the desired sample(s) needs to be copied into
|
|
||||||
your TeamCode module to be used.
|
|
||||||
|
|
||||||
This is done inside Android Studio directly, using the following steps:
|
|
||||||
|
|
||||||
1) Locate the desired sample class in the Project/Android tree.
|
|
||||||
|
|
||||||
2) Right click on the sample class and select "Copy"
|
|
||||||
|
|
||||||
3) Expand the TeamCode / java folder
|
|
||||||
|
|
||||||
4) Right click on the org.firstinspires.ftc.teamcode folder and select "Paste"
|
|
||||||
|
|
||||||
5) You will be prompted for a class name for the copy.
|
|
||||||
Choose something meaningful based on the purpose of this class.
|
|
||||||
Start with a capital letter, and remember that there may be more similar classes later.
|
|
||||||
|
|
||||||
Once your copy has been created, you should prepare it for use on your robot.
|
|
||||||
This is done by adjusting the OpMode's name, and enabling it to be displayed on the
|
|
||||||
Driver Station's OpMode list.
|
|
||||||
|
|
||||||
Each OpMode sample class begins with several lines of code like the ones shown below:
|
|
||||||
|
|
||||||
```
|
|
||||||
@TeleOp(name="Template: Linear OpMode", group="Linear Opmode")
|
|
||||||
@Disabled
|
|
||||||
```
|
|
||||||
|
|
||||||
The name that will appear on the driver station's "opmode list" is defined by the code:
|
|
||||||
``name="Template: Linear OpMode"``
|
|
||||||
You can change what appears between the quotes to better describe your opmode.
|
|
||||||
The "group=" portion of the code can be used to help organize your list of OpModes.
|
|
||||||
|
|
||||||
As shown, the current OpMode will NOT appear on the driver station's OpMode list because of the
|
|
||||||
``@Disabled`` annotation which has been included.
|
|
||||||
This line can simply be deleted , or commented out, to make the OpMode visible.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## ADVANCED Multi-Team App management: Cloning the TeamCode Module
|
|
||||||
|
|
||||||
In some situations, you have multiple teams in your club and you want them to all share
|
|
||||||
a common code organization, with each being able to *see* the others code but each having
|
|
||||||
their own team module with their own code that they maintain themselves.
|
|
||||||
|
|
||||||
In this situation, you might wish to clone the TeamCode module, once for each of these teams.
|
|
||||||
Each of the clones would then appear along side each other in the Android Studio module list,
|
|
||||||
together with the FtcRobotController module (and the original TeamCode module).
|
|
||||||
|
|
||||||
Selective Team phones can then be programmed by selecting the desired Module from the pulldown list
|
|
||||||
prior to clicking to the green Run arrow.
|
|
||||||
|
|
||||||
Warning: This is not for the inexperienced Software developer.
|
|
||||||
You will need to be comfortable with File manipulations and managing Android Studio Modules.
|
|
||||||
These changes are performed OUTSIDE of Android Studios, so close Android Studios before you do this.
|
|
||||||
|
|
||||||
Also.. Make a full project backup before you start this :)
|
|
||||||
|
|
||||||
To clone TeamCode, do the following:
|
|
||||||
|
|
||||||
Note: Some names start with "Team" and others start with "team". This is intentional.
|
|
||||||
|
|
||||||
1) Using your operating system file management tools, copy the whole "TeamCode"
|
|
||||||
folder to a sibling folder with a corresponding new name, eg: "Team0417".
|
|
||||||
|
|
||||||
2) In the new Team0417 folder, delete the TeamCode.iml file.
|
|
||||||
|
|
||||||
3) the new Team0417 folder, rename the "src/main/java/org/firstinspires/ftc/teamcode" folder
|
|
||||||
to a matching name with a lowercase 'team' eg: "team0417".
|
|
||||||
|
|
||||||
4) In the new Team0417/src/main folder, edit the "AndroidManifest.xml" file, change the line that contains
|
|
||||||
package="org.firstinspires.ftc.teamcode"
|
|
||||||
to be
|
|
||||||
package="org.firstinspires.ftc.team0417"
|
|
||||||
|
|
||||||
5) Add: include ':Team0417' to the "/settings.gradle" file.
|
|
||||||
|
|
||||||
6) Open up Android Studios and clean out any old files by using the menu to "Build/Clean Project""
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package org.timecrafters.minibots.cyberarm;
|
||||||
|
|
||||||
|
import com.qualcomm.robotcore.hardware.DcMotor;
|
||||||
|
import com.qualcomm.robotcore.hardware.DcMotorEx;
|
||||||
|
import com.qualcomm.robotcore.hardware.DcMotorSimple;
|
||||||
|
|
||||||
|
import org.cyberarm.engine.V2.CyberarmEngine;
|
||||||
|
|
||||||
|
public class MecanumMinibot {
|
||||||
|
public static final int TURN_LEFT = 0;
|
||||||
|
public static final int TURN_RIGHT = 1;
|
||||||
|
public static final int STRAFE_LEFT = 0;
|
||||||
|
public static final int STRAFE_RIGHT = 1;
|
||||||
|
|
||||||
|
private CyberarmEngine engine;
|
||||||
|
|
||||||
|
public DcMotor frontLeftDrive, frontRightDrive, backLeftDrive, backRightDrive;
|
||||||
|
|
||||||
|
public MecanumMinibot(CyberarmEngine engine) {
|
||||||
|
this.engine = engine;
|
||||||
|
|
||||||
|
setupDrivetrain();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDrivetrain() {
|
||||||
|
frontLeftDrive = engine.hardwareMap.dcMotor.get("frontLeft");
|
||||||
|
frontRightDrive = engine.hardwareMap.dcMotor.get("frontRight");
|
||||||
|
backLeftDrive = engine.hardwareMap.dcMotor.get("backLeft");
|
||||||
|
backRightDrive = engine.hardwareMap.dcMotor.get("backRight");
|
||||||
|
|
||||||
|
frontLeftDrive.setDirection(DcMotorSimple.Direction.REVERSE);
|
||||||
|
frontRightDrive.setDirection(DcMotorSimple.Direction.REVERSE);
|
||||||
|
backLeftDrive.setDirection(DcMotorSimple.Direction.REVERSE);
|
||||||
|
backRightDrive.setDirection(DcMotorSimple.Direction.REVERSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void driveAll(double power) {
|
||||||
|
frontLeftDrive.setPower(power);
|
||||||
|
frontRightDrive.setPower(power);
|
||||||
|
backLeftDrive.setPower(power);
|
||||||
|
backRightDrive.setPower(power);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void driveStop() {
|
||||||
|
driveAll(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void driveTurn(int direction, double power) {
|
||||||
|
if (direction == TURN_LEFT) {
|
||||||
|
frontLeftDrive.setPower(-power);
|
||||||
|
backLeftDrive.setPower(-power);
|
||||||
|
|
||||||
|
frontRightDrive.setPower(power);
|
||||||
|
backRightDrive.setPower(power);
|
||||||
|
|
||||||
|
} else if (direction == TURN_RIGHT) {
|
||||||
|
frontLeftDrive.setPower(power);
|
||||||
|
backLeftDrive.setPower(power);
|
||||||
|
|
||||||
|
frontRightDrive.setPower(-power);
|
||||||
|
backRightDrive.setPower(-power);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Invalid direction for driveTurn()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void driveStrafe(int direction, double power) {
|
||||||
|
if (direction == STRAFE_LEFT) {
|
||||||
|
frontLeftDrive.setPower(power);
|
||||||
|
frontRightDrive.setPower(-power);
|
||||||
|
|
||||||
|
backLeftDrive.setPower(-power);
|
||||||
|
backRightDrive.setPower(power);
|
||||||
|
|
||||||
|
} else if (direction == STRAFE_RIGHT) {
|
||||||
|
frontLeftDrive.setPower(-power);
|
||||||
|
frontRightDrive.setPower(power);
|
||||||
|
|
||||||
|
backLeftDrive.setPower(power);
|
||||||
|
backRightDrive.setPower(-power);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Invalid direction for driveStrafe()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.timecrafters.minibots.cyberarm.engines;
|
||||||
|
|
||||||
|
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
|
||||||
|
|
||||||
|
import org.cyberarm.engine.V2.CyberarmEngine;
|
||||||
|
import org.timecrafters.minibots.cyberarm.MecanumMinibot;
|
||||||
|
import org.timecrafters.minibots.cyberarm.states.MecanumMinibotTeleOpState;
|
||||||
|
|
||||||
|
@TeleOp(name = "MecanumMinibot TeleOp", group = "minibot")
|
||||||
|
public class MecanumMinibotTeleOpEngine extends CyberarmEngine {
|
||||||
|
MecanumMinibot robot;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() {
|
||||||
|
robot = new MecanumMinibot(this);
|
||||||
|
|
||||||
|
addState(new MecanumMinibotTeleOpState(robot));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package org.timecrafters.minibots.cyberarm.states;
|
||||||
|
|
||||||
|
import com.qualcomm.robotcore.hardware.Gamepad;
|
||||||
|
|
||||||
|
import org.cyberarm.engine.V2.CyberarmEngine;
|
||||||
|
import org.cyberarm.engine.V2.CyberarmState;
|
||||||
|
import org.timecrafters.minibots.cyberarm.MecanumMinibot;
|
||||||
|
|
||||||
|
public class MecanumMinibotTeleOpState extends CyberarmState {
|
||||||
|
private final MecanumMinibot robot;
|
||||||
|
private float speed;
|
||||||
|
|
||||||
|
public MecanumMinibotTeleOpState(MecanumMinibot robot) {
|
||||||
|
this.robot = robot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exec() {
|
||||||
|
speed = 1.0f - engine.gamepad1.left_trigger;
|
||||||
|
|
||||||
|
if (engine.gamepad1.y) {
|
||||||
|
robot.driveAll(speed);
|
||||||
|
} else if (engine.gamepad1.a) {
|
||||||
|
robot.driveAll(-speed);
|
||||||
|
} else if (engine.gamepad1.x) {
|
||||||
|
robot.driveStrafe(MecanumMinibot.STRAFE_LEFT, speed);
|
||||||
|
} else if (engine.gamepad1.b) {
|
||||||
|
robot.driveStrafe(MecanumMinibot.STRAFE_RIGHT, speed);
|
||||||
|
|
||||||
|
} else if (engine.gamepad1.left_bumper) {
|
||||||
|
robot.driveTurn(MecanumMinibot.TURN_LEFT, speed);
|
||||||
|
|
||||||
|
} else if (engine.gamepad1.right_bumper) {
|
||||||
|
robot.driveTurn(MecanumMinibot.TURN_RIGHT, speed);
|
||||||
|
} else {
|
||||||
|
robot.driveStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void buttonDown(Gamepad gamepad, String button) {
|
||||||
|
super.buttonDown(gamepad, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void buttonUp(Gamepad gamepad, String button) {
|
||||||
|
super.buttonUp(gamepad, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void telemetry() {
|
||||||
|
engine.telemetry.addData("speed", speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user