diff --git a/FtcRobotController/FtcRobotController.iml b/FtcRobotController/FtcRobotController.iml index 63d071be8b5..2937674df69 100644 --- a/FtcRobotController/FtcRobotController.iml +++ b/FtcRobotController/FtcRobotController.iml @@ -87,7 +87,7 @@ - + diff --git a/FtcRobotController/build.gradle b/FtcRobotController/build.gradle index b21489db558..46ee3233543 100644 --- a/FtcRobotController/build.gradle +++ b/FtcRobotController/build.gradle @@ -1,41 +1,28 @@ apply plugin: 'com.android.library' android { + compileSdkVersion 19 + buildToolsVersion '21.1.2' + defaultConfig { minSdkVersion 16 targetSdkVersion 19 } - compileSdkVersion 'Google Inc.:Google APIs:19' - buildToolsVersion '21.1.2' -} - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:1.2.3' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + buildTypes { + release { + debuggable true + } + debug { + debuggable true + } } } - repositories { flatDir { dirs 'libs' } } -allprojects { - repositories { - jcenter() - flatDir { - dirs 'out' - } - } -} - dependencies { compile files('libs/android-support-v4.jar') compile (name:'RobotCore-release', ext:'aar') diff --git a/FtcRobotController/src/main/java/com/qualcomm/ftcrobotcontroller/FtcRobotControllerActivity.java b/FtcRobotController/src/main/java/com/qualcomm/ftcrobotcontroller/FtcRobotControllerActivity.java index 8e35343673e..34ac8e77da7 100644 --- a/FtcRobotController/src/main/java/com/qualcomm/ftcrobotcontroller/FtcRobotControllerActivity.java +++ b/FtcRobotController/src/main/java/com/qualcomm/ftcrobotcontroller/FtcRobotControllerActivity.java @@ -334,7 +334,7 @@ private void requestRobotSetup() { modernRoboticsFactory.setXmlInputStream(fis); factory = modernRoboticsFactory; - eventLoop = new FtcEventLoop(factory, new FtcOpModeRegister(), callback, this); + eventLoop = new SwerveFtcEventLoop(factory, new FtcOpModeRegister(), callback, this); controllerService.setCallback(callback); controllerService.setupRobot(eventLoop); @@ -420,22 +420,22 @@ static class SwerveEventLoopMonitor implements EventLoopManager.EventLoopMonitor } // Make sure we're installed in the in the hook of the current event loop - public synchronized static void installIfNecessary(FtcRobotControllerService service) + public synchronized static boolean installIfNecessary(FtcRobotControllerService service) { if (service == null) - return; + return false; - Robot robot = robotOfFtcRobotControllerService(service); + Robot robot = MemberUtil.robotOfFtcRobotControllerService(service); if (robot == null) - return; + return false; - EventLoopManager eventLoopManager = eventLoopManagerOfRobot(robot); + EventLoopManager eventLoopManager = MemberUtil.eventLoopManagerOfRobot(robot); if (eventLoopManager == null) - return; + return false; - EventLoopManager.EventLoopMonitor monitor = monitorOfEventLoopManager(eventLoopManager); + EventLoopManager.EventLoopMonitor monitor = MemberUtil.monitorOfEventLoopManager(eventLoopManager); if (monitor == null) - return; + return false; if (monitor instanceof SwerveEventLoopMonitor) { @@ -446,75 +446,66 @@ public synchronized static void installIfNecessary(FtcRobotControllerService ser SwerveEventLoopMonitor newMonitor = new SwerveEventLoopMonitor(monitor); eventLoopManager.setMonitor(newMonitor); } + + return true; } - //---------------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------------ // Notifications - //---------------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------------ @Override public void onStateChange(RobotState newState) { this.prevMonitor.onStateChange(newState); - RobotStateTransitionNotifier.onEventLoopStateChange(newState); + RobotStateTransitionNotifier.onRobotStateChange(newState); } - //---------------------------------------------------------------------------------------------- - // Skullduggery - //---------------------------------------------------------------------------------------------- - - public static Robot robotOfFtcRobotControllerService(FtcRobotControllerService service) - { - return Util.getLocalPrivateObjectField(service, 2); - } - public static EventLoopManager eventLoopManagerOfRobot(Robot robot) - { - return Util.getLocalPrivateObjectField(robot, 0); - } - public static EventLoopManager.EventLoopMonitor monitorOfEventLoopManager(EventLoopManager manager) - { - return Util.getLocalPrivateObjectField(manager, 8); - } } class SwerveUpdateUIHook extends UpdateUI // Hook used to augment the user interface { - //---------------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------------ // State - //---------------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------------ - FtcRobotControllerActivity activity; - FtcRobotControllerService controllerService; + FtcRobotControllerActivity activity; + FtcRobotControllerService controllerService; - //---------------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------------ // Construction - //---------------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------------ SwerveUpdateUIHook(FtcRobotControllerActivity activity, Dimmer dimmer) - { - super(activity, dimmer); - this.activity = activity; - this.controllerService = null; - } + { + super(activity, dimmer); + this.activity = activity; + this.controllerService = null; + } @Override public void setControllerService(FtcRobotControllerService controllerService) - { - super.setControllerService(controllerService); - this.controllerService = controllerService; - } + { + super.setControllerService(controllerService); + this.controllerService = controllerService; + } + + //------------------------------------------------------------------------------------------ + // Operations + //------------------------------------------------------------------------------------------ - //---------------------------------------------------------------------------------------------- - // Operations - //---------------------------------------------------------------------------------------------- + class CallbackHook extends UpdateUI.Callback + { + //-------------------------------------------------------------------------------------- + // Operations + //-------------------------------------------------------------------------------------- - class CallbackHook extends UpdateUI.Callback - { @Override public void robotUpdate(final String status) { super.robotUpdate(status); + RobotStateTransitionNotifier.onRobotUpdate(status); // Make sure we get to see all the robot state transitions SwerveEventLoopMonitor.installIfNecessary(controllerService); @@ -522,24 +513,23 @@ public void robotUpdate(final String status) @Override public void wifiDirectUpdate(WifiDirectAssistant.Event event) - { - super.wifiDirectUpdate(event); + { + super.wifiDirectUpdate(event); - final String message = controllerService == null - ? "" - : String.format("Wifi Direct passphrase: %s", controllerService.getWifiDirectAssistant().getPassphrase()); + final String message = controllerService == null + ? "" + : String.format("Wifi Direct passphrase: %s", controllerService.getWifiDirectAssistant().getPassphrase()); SwerveUpdateUIHook.this.activity.runOnUiThread(new Runnable() - { - @Override - public void run() - { - activity.textWifiDirectPassphrase.setText(message); + { + @Override public void run() + { + activity.textWifiDirectPassphrase.setText(message); + } + }); + } + } } - }); - } - } - } } diff --git a/SwerveRoboticsLibrary/build.gradle b/SwerveRoboticsLibrary/build.gradle index a8c61a5804c..d1ab44dfb96 100644 --- a/SwerveRoboticsLibrary/build.gradle +++ b/SwerveRoboticsLibrary/build.gradle @@ -8,7 +8,7 @@ android { minSdkVersion 16 targetSdkVersion 19 versionCode 1 - versionName "0.6" + versionName "1.0" } buildTypes { release { diff --git a/SwerveRoboticsLibrary/doc/javadoc/index-all.html b/SwerveRoboticsLibrary/doc/javadoc/index-all.html index 9509e5f5d64..f8dfb8bbce5 100644 --- a/SwerveRoboticsLibrary/doc/javadoc/index-all.html +++ b/SwerveRoboticsLibrary/doc/javadoc/index-all.html @@ -862,7 +862,7 @@

M

 
MyRobotControllerAdministration - Class in org.usfirst.ftc.exampleteam.yourcodehere
-
MyRobotControllerAdministration is a container for 'administrative' methods that intereract +
MyRobotControllerAdministration is a container for 'administrative' methods that interact with the Swerve library.
MyRobotControllerAdministration() - Constructor for class MyRobotControllerAdministration
@@ -1021,6 +1021,8 @@

P

Advanced: a hook for subclasses
+
powerMotors(double) - Method in class SynchMotorLoopPerf
+
 
preInitHook() - Method in class SynchronousOpMode
Advanced: the various 'hook' calls calls preInitHook(), postInitHook(), preLoopHook(), diff --git a/SwerveRoboticsLibrary/doc/javadoc/org/swerverobotics/library/examples/SynchMotorLoopPerf.html b/SwerveRoboticsLibrary/doc/javadoc/org/swerverobotics/library/examples/SynchMotorLoopPerf.html index f40039002ad..56adac82aaa 100644 --- a/SwerveRoboticsLibrary/doc/javadoc/org/swerverobotics/library/examples/SynchMotorLoopPerf.html +++ b/SwerveRoboticsLibrary/doc/javadoc/org/swerverobotics/library/examples/SynchMotorLoopPerf.html @@ -17,7 +17,7 @@ catch(err) { } //--> -var methods = {"i0":10}; +var methods = {"i0":10,"i1":10}; var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]}; var altColor = "altColor"; var rowColor = "rowColor"; @@ -128,7 +128,9 @@

Class SynchMotorLoopPerf

extends SynchronousOpMode
An op mode that investigates how many loop() cycles it takes to do full mode switching on a motor controller. Each main loop cycle does both - a read and a write to the motor.
+ a read and a write to the motor. + + This OpMode expects two motors, named 'motorLeft' and 'motorRight'.
@@ -192,6 +194,10 @@

Method Summary

Implement main() (in a subclass) to contain your robot logic.
+ +void +powerMotors(double power)  +
  • @@ -248,7 +254,7 @@

    Method Detail

    -
      +
      • main

        public void main()
        @@ -285,6 +291,15 @@ 

        main

      + + + +
        +
      • +

        powerMotors

        +
        public void powerMotors(double power)
        +
      • +
  • diff --git a/SwerveRoboticsLibrary/doc/javadoc/org/swerverobotics/library/interfaces/package-tree.html b/SwerveRoboticsLibrary/doc/javadoc/org/swerverobotics/library/interfaces/package-tree.html index c0d0b92785c..afa94a8c42b 100644 --- a/SwerveRoboticsLibrary/doc/javadoc/org/swerverobotics/library/interfaces/package-tree.html +++ b/SwerveRoboticsLibrary/doc/javadoc/org/swerverobotics/library/interfaces/package-tree.html @@ -113,12 +113,12 @@

    Interface Hierarchy

Annotation Type Hierarchy

    -
  • OpModeRegistrar (implements java.lang.annotation.Annotation)
  • -
  • TeleOp (implements java.lang.annotation.Annotation)
  • -
  • OnRobotRunning (implements java.lang.annotation.Annotation)
  • +
  • Disabled (implements java.lang.annotation.Annotation)
  • OnRobotStartupFailure (implements java.lang.annotation.Annotation)
  • Autonomous (implements java.lang.annotation.Annotation)
  • -
  • Disabled (implements java.lang.annotation.Annotation)
  • +
  • TeleOp (implements java.lang.annotation.Annotation)
  • +
  • OpModeRegistrar (implements java.lang.annotation.Annotation)
  • +
  • OnRobotRunning (implements java.lang.annotation.Annotation)

Enum Hierarchy

    @@ -126,14 +126,14 @@

    Enum Hierarchy

    diff --git a/SwerveRoboticsLibrary/doc/javadoc/org/usfirst/ftc/exampleteam/yourcodehere/MyRobotControllerAdministration.html b/SwerveRoboticsLibrary/doc/javadoc/org/usfirst/ftc/exampleteam/yourcodehere/MyRobotControllerAdministration.html index ff9387ade3c..d1223122b8d 100644 --- a/SwerveRoboticsLibrary/doc/javadoc/org/usfirst/ftc/exampleteam/yourcodehere/MyRobotControllerAdministration.html +++ b/SwerveRoboticsLibrary/doc/javadoc/org/usfirst/ftc/exampleteam/yourcodehere/MyRobotControllerAdministration.html @@ -109,7 +109,7 @@

    Class MyRobotCon
    public class MyRobotControllerAdministration
     extends java.lang.Object
    -
    MyRobotControllerAdministration is a container for 'administrative' methods that intereract +
    MyRobotControllerAdministration is a container for 'administrative' methods that interact with the Swerve library. You don't have to put your administrative methods in a separate class as we do here, but it does help keep them neat and tidy. Administrative methods are each tagged with a Java annotation that connotes and bestows their significance; see the diff --git a/SwerveRoboticsLibrary/doc/javadoc/org/usfirst/ftc/exampleteam/yourcodehere/package-summary.html b/SwerveRoboticsLibrary/doc/javadoc/org/usfirst/ftc/exampleteam/yourcodehere/package-summary.html index 155cd0f463c..db191834595 100644 --- a/SwerveRoboticsLibrary/doc/javadoc/org/usfirst/ftc/exampleteam/yourcodehere/package-summary.html +++ b/SwerveRoboticsLibrary/doc/javadoc/org/usfirst/ftc/exampleteam/yourcodehere/package-summary.html @@ -96,7 +96,7 @@

    Package org.usfirst.ftc.exampleteam.yourc MyRobotControllerAdministration -
    MyRobotControllerAdministration is a container for 'administrative' methods that intereract +
    MyRobotControllerAdministration is a container for 'administrative' methods that interact with the Swerve library.
    diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/ClassFactory.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/ClassFactory.java index f6e4926c3b2..9c788c65b55 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/ClassFactory.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/ClassFactory.java @@ -188,8 +188,8 @@ public static ColorSensor createHiTechnicColorSensor(OpMode context, I2cControll */ public static II2cDevice createI2cDevice(I2cDevice i2cDevice) { - I2cController i2cController = Util.getPrivateObjectField(i2cDevice, 0); - int port = Util.getPrivateIntField(i2cDevice, 1); + I2cController i2cController = MemberUtil.i2cControllerOfI2cDevice(i2cDevice); + int port = MemberUtil.portOfI2cDevice(i2cDevice); return createI2cDevice(i2cController, port); } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/SynchronousOpMode.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/SynchronousOpMode.java index 5dede6c0201..dc06fc75a29 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/SynchronousOpMode.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/SynchronousOpMode.java @@ -457,7 +457,7 @@ private static void gamepadAssign(com.qualcomm.robotcore.hardware.Gamepad dst, c private Thread loopThread; private Thread mainThread; - private final Queue synchronousWorkerThreads = new ConcurrentLinkedQueue(); + private final Collection synchronousWorkerThreads = new ConcurrentLinkedQueue(); private RuntimeException exceptionThrownOnMainThread; private final AtomicReference firstExceptionThrownOnASynchronousWorkerThread = new AtomicReference(); private final static int msWaitForMainThreadTermination = 250; @@ -677,53 +677,31 @@ public final void run() } } - private void stopSynchronousThread(Thread thread, int msWait) - // Note: the thread might not EVER have been started, so may not have any - // SynchronousThreadContext. + private void interruptSynchronousThreads() { - if (thread != null) + for (Thread thread : this.synchronousWorkerThreads) { - // Notify the thread that we wish it to stop what it's doing, clean up, and return. thread.interrupt(); - - // Wait a while until the thread is no longer alive. If he doesn't clear out - // in a reasonable amount of time, then just give up on him. - try - { - thread.join(msWait); - } - catch (InterruptedException e) - { - Util.handleCapturedInterrupt(e); - } + } + if (this.mainThread != null) + { + this.mainThread.interrupt(); } } - private void stopSynchronousWorkerThreads(int msWait) - // Do the shutdown in parallel so we're not serially taking the timeout hits. - // We hope that will be a little faster. + private void waitForSynchronousWorkerThreads(int msWait) throws InterruptedException { - List interruptedThreads = new LinkedList(); - // - for (;;) + for (Thread thread : this.synchronousWorkerThreads) { - Thread thread = this.synchronousWorkerThreads.poll(); - if (null == thread) - break; - thread.interrupt(); - interruptedThreads.add(thread); + thread.join(msWait); } - - for (Thread thread : interruptedThreads) + } + + private void waitForMainThread(int msWait) throws InterruptedException + { + if (this.mainThread != null) { - try - { - thread.join(msWait); - } - catch (InterruptedException e) - { - Util.handleCapturedInterrupt(e); - } + this.mainThread.join(msWait); } } @@ -825,7 +803,13 @@ private void setThreadThunker() @Override public final void init_loop() { this.preInitLoopHook(); - ; + + // Make waitOneFullHardwareCycle work before start is called + synchronized (this.loopLock) + { + this.loopLock.notifyAll(); + } + this.postInitLoopHook(); } @@ -975,11 +959,22 @@ void executeAction(Runnable action) // Next time synchronous threads ask, yes, we do want to stop this.stopRequested = true; - // Clean up any worker threads - this.stopSynchronousWorkerThreads(this.msWaitForSynchronousWorkerThreadTermination); + try { + // Give all of our worker threads a heads up to get out of town + this.interruptSynchronousThreads(); - // Notify the main() thread that we wish it to stop what it's doing, clean up, and return. - this.stopSynchronousThread(this.mainThread, this.msWaitForMainThreadTermination); + // Serially wait for these folk to pack up and leave + this.waitForSynchronousWorkerThreads(this.msWaitForSynchronousWorkerThreadTermination); + this.waitForMainThread(this.msWaitForMainThreadTermination); + } + catch (InterruptedException e) + { + // We were waiting for the threads to shutdown but then *we* got an interrupt + // while doing so. That's an entirely unexpected situation; it shouldn't happen + // architecturally in the robot controller app. + Log.e(LOGGING_TAG, String.format("unexpected interrupt: %s", Util.getStackTrace(e))); + throw SwerveRuntimeException.wrap(e); + } if (this.hardwareFactory != null) { diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/examples/SynchIMUDemo.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/examples/SynchIMUDemo.java index 61406d3b645..59ec3ef393a 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/examples/SynchIMUDemo.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/examples/SynchIMUDemo.java @@ -42,7 +42,7 @@ public class SynchIMUDemo extends SynchronousOpMode // semantically understands this particular kind of sensor. parameters.angleunit = IBNO055IMU.ANGLEUNIT.DEGREES; parameters.accelunit = IBNO055IMU.ACCELUNIT.METERS_PERSEC_PERSEC; - parameters.loggingEnabled = true; + parameters.loggingEnabled = false; parameters.loggingTag = "BNO055"; imu = ClassFactory.createAdaFruitBNO055IMU(hardwareMap.i2cDevice.get("imu"), parameters); diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/examples/SynchMotorLoopPerf.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/examples/SynchMotorLoopPerf.java index 6c9a76673ab..b52272c9b7b 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/examples/SynchMotorLoopPerf.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/examples/SynchMotorLoopPerf.java @@ -9,33 +9,42 @@ * An op mode that investigates how many loop() cycles it takes to do full * mode switching on a motor controller. Each main loop cycle does both * a read and a write to the motor. + * + * This OpMode expects two motors, named 'motorLeft' and 'motorRight'. */ @TeleOp(name="Motor Perf (sync)", group="Swerve Examples") @Disabled public class SynchMotorLoopPerf extends SynchronousOpMode { - DcMotor motor; - int position; + final boolean useBothMotors; + + DcMotor leftMotor, rightMotor; + int leftPosition, rightPosition; long loopCountStart; int spinCount; ElapsedTime elapsed = new ElapsedTime(); public SynchMotorLoopPerf() { + useBothMotors = false; } public @Override void main() throws InterruptedException { - motor = hardwareMap.dcMotor.get("motorLeft"); + leftMotor = hardwareMap.dcMotor.get("motorLeft"); + rightMotor = hardwareMap.dcMotor.get("motorRight"); waitForStart(); + loopCountStart = getLoopCount(); spinCount = 1; elapsed.reset(); - + while (this.opModeIsActive()) { - position = motor.getCurrentPosition(); + leftPosition = leftMotor.getCurrentPosition(); + rightPosition = useBothMotors ? rightMotor.getCurrentPosition() : 0; + long loopCount = getLoopCount() - loopCountStart; double ms = elapsed.time() * 1000; @@ -47,11 +56,11 @@ public SynchMotorLoopPerf() // performance analysis, and we don't want that to be subject to human issues. // Instead, we always set the power exactly once each spin around the loop. if (gamepad1.left_bumper) - motor.setPower(0.5); + powerMotors(0.5); else - motor.setPower(0.25); + powerMotors(0.25); - telemetry.addData("position", position); + telemetry.addData("position", String.format("left=%d right=%d", leftPosition, rightPosition)); telemetry.addData("#loop()", loopCount); telemetry.addData("#spin", spinCount); telemetry.addData("#loop()/#spin", String.format("%.1f", loopCount / (double)spinCount)); @@ -63,4 +72,11 @@ public SynchMotorLoopPerf() spinCount++; } } + + public void powerMotors(double power) + { + leftMotor.setPower(power); + if (useBothMotors) + rightMotor.setPower(power); + } } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/AdaFruitBNO055IMU.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/AdaFruitBNO055IMU.java index 631c74a16ae..1b5dbbf6cfc 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/AdaFruitBNO055IMU.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/AdaFruitBNO055IMU.java @@ -716,7 +716,7 @@ private void delayLore(int ms) } catch (InterruptedException e) { - Util.handleCapturedInterrupt(e); + handleCapturedInterrupt(e); } } @@ -733,7 +733,7 @@ private void delay(int ms) } catch (InterruptedException e) { - Util.handleCapturedInterrupt(e); + handleCapturedInterrupt(e); } } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/AnnotatedOpModeRegistrar.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/AnnotatedOpModeRegistrar.java index bf907301cb3..b611730d45c 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/AnnotatedOpModeRegistrar.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/AnnotatedOpModeRegistrar.java @@ -4,6 +4,8 @@ import android.content.Context; import android.util.Log; import com.qualcomm.robotcore.eventloop.opmode.*; + +import org.swerverobotics.library.SynchronousOpMode; import org.swerverobotics.library.interfaces.*; import java.io.*; import java.lang.reflect.*; @@ -25,7 +27,7 @@ public class AnnotatedOpModeRegistrar // State //---------------------------------------------------------------------------------------------- - private final String TAG = "AnnotatedOpModeReg"; + private final String LOGGING_TAG = SynchronousOpMode.LOGGING_TAG; LinkedList partialClassNamesToIgnore; @@ -155,7 +157,7 @@ else if (lhs.isAnnotationPresent(Autonomous.class) && rhs.isAnnotationPresent(Au { String name = getOpModeName(opMode); this.opModeManager.register(name, opMode); - Log.d(TAG, String.format("registered {%s} as {%s}", opMode.getSimpleName(), name)); + Log.d(LOGGING_TAG, String.format("registered {%s} as {%s}", opMode.getSimpleName(), name)); } } } @@ -284,7 +286,7 @@ public void register(String name, OpMode opModeInstance) // We just go ahead and register this, as there's nothing else to do. // TODO: we could register these AFTER the classes, if we wanted to. opModeManager.register(name, opModeInstance); - Log.d(TAG, String.format("registered instance {%s} as {%s}", opModeInstance.toString(), name)); + Log.d(LOGGING_TAG, String.format("registered instance {%s} as {%s}", opModeInstance.toString(), name)); } } @@ -321,7 +323,7 @@ private List findAllClasses() } catch (NoClassDefFoundError|ClassNotFoundException ex) { - Log.w(TAG, className + " " + ex.toString(), ex); + Log.w(LOGGING_TAG, className + " " + ex.toString(), ex); if (className.contains("$")) { className = className.substring(0, className.indexOf("$") - 1); diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/EasyLegacyMotorController.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/EasyLegacyMotorController.java index 26ac87641af..5bcf8a9ebcf 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/EasyLegacyMotorController.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/EasyLegacyMotorController.java @@ -1,5 +1,7 @@ package org.swerverobotics.library.internal; +import android.util.Log; + import com.qualcomm.robotcore.eventloop.opmode.OpMode; import com.qualcomm.robotcore.hardware.*; import com.qualcomm.robotcore.util.*; @@ -93,8 +95,8 @@ private EasyLegacyMotorController(OpMode context, II2cDeviceClient ii2cDeviceCli this.i2cDeviceClient = ii2cDeviceClient; this.target = target; this.targetName = findTargetName(); - this.legacyModule = ThunkingHardwareFactory.legacyModuleOfLegacyMotorController(target); - this.targetPort = ThunkingHardwareFactory.portOfLegacyMotorController(target); + this.legacyModule = MemberUtil.legacyModuleOfLegacyMotorController(target); + this.targetPort = MemberUtil.portOfLegacyMotorController(target); this.targetCallback = null; this.isArmed = false; this.motor1 = null; @@ -128,10 +130,10 @@ private EasyLegacyMotorController(OpMode context, II2cDeviceClient ii2cDeviceCli public static DcMotorController create(OpMode context, DcMotorController target, DcMotor motor1, DcMotor motor2) { - if (isLegacyMotorController(target)) + if (MemberUtil.isLegacyMotorController(target)) { - LegacyModule legacyModule = legacyModuleOfLegacyMotorController(target); - int port = portOfLegacyMotorController(target); + LegacyModule legacyModule = MemberUtil.legacyModuleOfLegacyMotorController(target); + int port = MemberUtil.portOfLegacyMotorController(target); int i2cAddr8Bit = i2cAddrOfLegacyMotorController(target); // Make a new legacy motor controller @@ -231,7 +233,7 @@ private void arm() if (!this.isArmed) { this.usurpMotors(); - this.targetCallback = ThunkingHardwareFactory.callbacksOfLegacyModule(this.legacyModule)[this.targetPort]; + this.targetCallback = MemberUtil.callbacksOfLegacyModule(this.legacyModule)[this.targetPort]; this.legacyModule.deregisterForPortReadyCallback(this.targetPort); if (this.targetName != null) this.context.hardwareMap.dcMotorController.put(this.targetName, this); this.i2cDeviceClient.arm(); @@ -329,19 +331,25 @@ private synchronized void disarm() @Override synchronized public boolean onUserOpModeStop() { + Log.d(SynchronousOpMode.LOGGING_TAG, "Easy: auto-stopping..."); if (this.isArmed) { this.stopMotors(); // mirror StopRobotOpMode this.disarm(); } + Log.d(SynchronousOpMode.LOGGING_TAG, "Easy: ... done"); return true; // unregister us } @Override synchronized public boolean onRobotShutdown() { + Log.d(SynchronousOpMode.LOGGING_TAG, "Easy: auto-closing..."); + // We actually shouldn't be here by now, having received a onUserOpModeStop() // after which we should have been unregistered. But we close down anyway. this.close(); + + Log.d(SynchronousOpMode.LOGGING_TAG, "Easy: ... done"); return true; // unregister us } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/HandshakeThreadStarter.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/HandshakeThreadStarter.java index a88988e04b8..1856b3ac4f7 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/HandshakeThreadStarter.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/HandshakeThreadStarter.java @@ -1,7 +1,5 @@ package org.swerverobotics.library.internal; -import org.swerverobotics.library.exceptions.*; - /** * A class that helps us start a thread and interlock with its actual starting up. * @@ -74,13 +72,13 @@ public synchronized void start() this.started = true; } - catch (InterruptedException|RuntimeInterruptedException e) + catch (Exception e) { // Clean up if we were interrupted while waiting this.started = true; // so stop() will work stop(); - Util.handleCapturedInterrupt(e); // pass it on + Util.handleCapturedException(e); } } @@ -110,9 +108,9 @@ public synchronized void stop(int msWait) else this.thread.join(msWait); } - catch (InterruptedException|RuntimeInterruptedException e) + catch (Exception e) { - Util.handleCapturedInterrupt(e); + Util.handleCapturedException(e); } finally { diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/I2cDeviceClient.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/I2cDeviceClient.java index f1b1625edd2..a60a5bf8bc0 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/I2cDeviceClient.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/I2cDeviceClient.java @@ -240,7 +240,7 @@ public void disarm() } catch (InterruptedException e) { - Util.handleCapturedInterrupt(e); + handleCapturedInterrupt(e); } } @@ -442,7 +442,7 @@ public void close() } catch (InterruptedException e) { - Util.handleCapturedInterrupt(e); + handleCapturedInterrupt(e); // Can't return (no data to return!) so we must throw throw SwerveRuntimeException.wrap(e); @@ -522,7 +522,7 @@ public void close() } catch (InterruptedException e) { - Util.handleCapturedInterrupt(e); + handleCapturedInterrupt(e); } } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/MemberUtil.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/MemberUtil.java new file mode 100644 index 00000000000..bbff468d023 --- /dev/null +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/MemberUtil.java @@ -0,0 +1,62 @@ +package org.swerverobotics.library.internal; + +import com.qualcomm.ftccommon.*; +import com.qualcomm.robotcore.eventloop.*; +import com.qualcomm.robotcore.hardware.*; +import com.qualcomm.robotcore.robot.*; + +/** + * Skullduggery we wish we didn't have to do. + */ +public class MemberUtil + { + public static Robot robotOfFtcRobotControllerService(FtcRobotControllerService service) + { + return Util.getPrivateObjectField(service, 2+7); + } + + public static EventLoopManager eventLoopManagerOfRobot(Robot robot) + { + return Util.getPrivateObjectField(robot, 0); + } + + public static EventLoopManager.EventLoopMonitor monitorOfEventLoopManager(EventLoopManager manager) + { + return Util.getPrivateObjectField(manager, 8); + } + + public static FtcEventLoopHandler ftcEventLoopHandlerOfFtcEventLoop(FtcEventLoop ftcEventLoop) + { + return Util.getPrivateObjectField(ftcEventLoop, 0); + } + + static boolean isLegacyMotorController(DcMotorController controller) + { + return controller instanceof com.qualcomm.hardware.HiTechnicNxtDcMotorController; + } + + static LegacyModule legacyModuleOfLegacyMotorController(DcMotorController controller) + { + return Util.getPrivateObjectField(controller, 0); + } + + static I2cController.I2cPortReadyCallback[] callbacksOfLegacyModule(LegacyModule module) + { + return Util.getPrivateObjectField(module, 4); + } + + static int portOfLegacyMotorController(DcMotorController controller) + { + return Util.getPrivateIntField(controller, 5); + } + + public static I2cController i2cControllerOfI2cDevice(I2cDevice i2cDevice) + { + return Util.getPrivateObjectField(i2cDevice, 0); + } + + public static int portOfI2cDevice(I2cDevice i2cDevice) + { + return Util.getPrivateIntField(i2cDevice, 1); + } + } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/RobotStateTransitionNotifier.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/RobotStateTransitionNotifier.java index b33aeec4be8..195dbb0399e 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/RobotStateTransitionNotifier.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/RobotStateTransitionNotifier.java @@ -56,6 +56,7 @@ public class RobotStateTransitionNotifier extends DcMotor implements DcMotorCont static Context applicationContext = null; static List onRobotRunningMethods = new LinkedList(); static List onRobotStartupFailureMethods = new LinkedList(); + static List onRobotUpdateActions = new LinkedList(); public final HardwareMap hardwareMap; private final List registrants; @@ -95,7 +96,7 @@ private static RobotStateTransitionNotifier create(HardwareMap map) } //---------------------------------------------------------------------------------------------- - // State transitions + // Registration //---------------------------------------------------------------------------------------------- synchronized void register(IOpModeStateTransitionEvents him) @@ -115,6 +116,27 @@ synchronized boolean unregister(IOpModeStateTransitionEvents him) return false; } + public synchronized static void setStateTransitionCallbacks(Context applicationContext, Collection onRunningMethods, Collection onStartupFailureMethods) + { + RobotStateTransitionNotifier.applicationContext = applicationContext; + RobotStateTransitionNotifier.onRobotRunningMethods = new LinkedList(onRunningMethods); + RobotStateTransitionNotifier.onRobotStartupFailureMethods = new LinkedList(onStartupFailureMethods); + } + + public synchronized static void registerRobotUpdateAction(Runnable runnable) + { + onRobotUpdateActions.add(runnable); + } + + public synchronized static void unregisterRobotUpdateAction(Runnable runnable) + { + onRobotUpdateActions.remove(runnable); + } + + //---------------------------------------------------------------------------------------------- + // Notifications + //---------------------------------------------------------------------------------------------- + synchronized void onUserOpModeStop() { List toRemove = new LinkedList(); @@ -147,13 +169,22 @@ synchronized void onRobotShutdown() } } + public static synchronized void onRobotUpdate(final String status) + { + List copiedList = new LinkedList(onRobotUpdateActions); + for (Runnable runnable : copiedList) + { + runnable.run(); + } + } + /** * Called by the FtcRobotControllerActivity hooking infrastructure when the event loop * changes state. * * @param newState the new state into which the event loop is transitioning. */ - public static synchronized void onEventLoopStateChange(RobotState newState) + public static synchronized void onRobotStateChange(RobotState newState) { Log.d(SynchronousOpMode.LOGGING_TAG, String.format("state xtion: state=%s", newState.toString())); switch (newState) @@ -182,13 +213,6 @@ public static synchronized void onEventLoopStateChange(RobotState newState) } } - public static void setStateTransitionCallbacks(Context applicationContext, Collection onRunningMethods, Collection onStartupFailureMethods) - { - RobotStateTransitionNotifier.applicationContext = applicationContext; - RobotStateTransitionNotifier.onRobotRunningMethods = new LinkedList(onRunningMethods); - RobotStateTransitionNotifier.onRobotStartupFailureMethods = new LinkedList(onStartupFailureMethods); - } - //---------------------------------------------------------------------------------------------- // HardwareDevice //---------------------------------------------------------------------------------------------- diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/SwerveFtcEventLoop.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/SwerveFtcEventLoop.java new file mode 100644 index 00000000000..427b75023d4 --- /dev/null +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/SwerveFtcEventLoop.java @@ -0,0 +1,52 @@ +package org.swerverobotics.library.internal; + +import android.content.Context; +import com.qualcomm.ftccommon.FtcEventLoop; +import com.qualcomm.ftccommon.UpdateUI; +import com.qualcomm.hardware.HardwareFactory; +import com.qualcomm.robotcore.eventloop.EventLoopManager; +import com.qualcomm.robotcore.eventloop.opmode.OpModeRegister; +import com.qualcomm.robotcore.exception.RobotCoreException; +import com.qualcomm.robotcore.robocol.Command; + +import java.util.concurrent.Semaphore; + +/** + * This class is a nefarious hook to try to work around an initialization problem + * that arises when registerOpModes takes too long. We would LOVE not to have to do + * this, and will remove this as soon as the underlying bug is fixed. + * + * See http://ftcforum.usfirst.org/showthread.php?4967 + * + * The essence of the bug is that we're being asked to process commands before we've been + * initialized. So our work around is to simply block the latter on the former. + */ +public class SwerveFtcEventLoop extends FtcEventLoop + { + Semaphore semaphore = new Semaphore(0); + + public SwerveFtcEventLoop(HardwareFactory hardwareFactory, OpModeRegister register, UpdateUI.Callback callback, Context robotControllerContext) + { + super(hardwareFactory, register, callback, robotControllerContext); + } + + @Override + public void init(EventLoopManager eventLoopManager) throws RobotCoreException, InterruptedException + { + super.init(eventLoopManager); + semaphore.release(); + } + + @Override public void processCommand(Command command) + { + try { + semaphore.acquire(); + semaphore.release(); + } + catch (InterruptedException e) + { + Util.handleCapturedInterrupt(e); + } + super.processCommand(command); + } + } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkForReading.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkForReading.java index ba29305962b..9570a496915 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkForReading.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkForReading.java @@ -59,15 +59,12 @@ public T doUntrackedReadOperation(IInterruptableRunnable actionBeforeDispatch) this.dispatch(); } - catch (InterruptedException|RuntimeInterruptedException e) + catch (Exception e) { - // (Re)tell the current thread that he should shut down soon - Util.handleCapturedInterrupt(e); - // Our signature (and that of our caller) doesn't allow us to throw // InterruptedException. But we can't actually return a value to our caller, // as we have nothing to return. So, we do the best we can, and throw SOMETHING. - throw SwerveRuntimeException.wrap(e); + Util.handleCapturedException(e); } return this.result; } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkForWriting.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkForWriting.java index d7bd77875e3..9c085619212 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkForWriting.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkForWriting.java @@ -1,6 +1,5 @@ package org.swerverobotics.library.internal; -import org.swerverobotics.library.exceptions.*; import org.swerverobotics.library.interfaces.*; /** @@ -50,16 +49,9 @@ protected void doUntrackedWriteOperation(IInterruptableRunnable actionBeforeDisp this.dispatch(); } - catch (InterruptedException | RuntimeInterruptedException e) + catch (Exception e) { - // Tell the current thread that he should shut down soon - Util.handleCapturedInterrupt(e); - - // Since callers generally do reads as well as writes, and so - // must deal with the necessity we have in reads of throwing, - // we may as well throw here as well, as that will help shut - // things down sooner. - throw SwerveRuntimeException.wrap(e); + Util.handleCapturedException(e); } } } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkedDCMotorController.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkedDCMotorController.java index 840b052a66a..bdc956db56c 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkedDCMotorController.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkedDCMotorController.java @@ -2,8 +2,6 @@ import com.qualcomm.robotcore.hardware.*; import org.swerverobotics.library.*; -import org.swerverobotics.library.exceptions.*; -import org.swerverobotics.library.interfaces.*; /** * An implementation of DcMotorController that talks to a non-thunking target implementation diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkingHardwareFactory.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkingHardwareFactory.java index dbe57273bf1..4487d41d6fd 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkingHardwareFactory.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/ThunkingHardwareFactory.java @@ -99,7 +99,7 @@ public final HardwareMap createThunkedHardwareMap() // For those controller which are legacy controllers, do a switch-er-roo. for (DcMotorController controller : motors.keySet()) { - if (isLegacyMotorController(controller)) + if (MemberUtil.isLegacyMotorController(controller)) { DcMotor motor1 = motors.get(controller).get(0); DcMotor motor2 = motors.get(controller).size() > 1 ? motors.get(controller).get(1) : null; @@ -499,36 +499,16 @@ static boolean contains(HardwareMap.DeviceMapping map, String name) return false; } - static boolean isLegacyMotorController(DcMotorController controller) - { - return controller instanceof com.qualcomm.hardware.HiTechnicNxtDcMotorController; - } - - static LegacyModule legacyModuleOfLegacyMotorController(DcMotorController controller) - { - return Util.getPrivateObjectField(controller, 0); - } - - static I2cController.I2cPortReadyCallback[] callbacksOfLegacyModule(LegacyModule module) - { - return Util.getPrivateObjectField(module, 4); - } - - static int portOfLegacyMotorController(DcMotorController controller) - { - return Util.getPrivateIntField(controller, 5); - } - static int i2cAddrOfLegacyMotorController(DcMotorController controller) { // From the spec from HiTechnic: // - // "The first motor controller in the daisy chain will use an I2C address of 02/03. Subsequent - // controllers will obtain addresses of 04/05, 06/07 and 08/09. Only four controllers may be + // "The first motor controller in the daisy chain will use an I2C address of 02/03. Subsequent + // controllers will obtain addresses of 04/05, 06/07 and 08/09. Only four controllers may be // daisy chained." // // The legacy module appears not to support daisy chaining; it only supports the first - // address. Note that these are clearly 8-bit addresses, not 7-bit. + // address. Note that these are clearly 8-bit addresses, not 7-bit. // return 0x02; } diff --git a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/Util.java b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/Util.java index 7f6dd1a5054..45ad1261e2a 100644 --- a/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/Util.java +++ b/SwerveRoboticsLibrary/src/main/java/org/swerverobotics/library/internal/Util.java @@ -33,16 +33,6 @@ static public String getStackTrace(Exception e) return result.toString(); } - //---------------------------------------------------------------------------------------------- - // Threading - //---------------------------------------------------------------------------------------------- - - static public void handleCapturedInterrupt(Exception e) - { - // Log.d(SynchronousOpMode.TAG, "caught an thread interrupt, reinterrupting: " + e); - Thread.currentThread().interrupt(); - } - //---------------------------------------------------------------------------------------------- // Private method access // @@ -398,4 +388,30 @@ static public void setPrivateDoubleField(Object target, int iField, double value throw SwerveRuntimeException.wrap(e); } } + + //---------------------------------------------------------------------------------------------- + // Dealing with captured exceptions + // + // That InterruptedException is a non-runtime exception is, I believe, a bug: I could go + // on at great length here about that, but for the moment will refrain and defer until another + // time: the issue is a lengthy discussion. + // + // But we have the issue of what to do. That the fellow has captured the interrupt means that + // he doesn't want an InterruptedException to propagate. Yet somehow we must in effect do so: + // the thread needs to be torn down. Ergo, we seem to have no choice but to throw a runtime + // version of the interrupt. + //---------------------------------------------------------------------------------------------- + + public static void handleCapturedInterrupt(InterruptedException e) + { + handleCapturedException((Exception)e); + } + + public static void handleCapturedException(Exception e) + { + if (e instanceof InterruptedException || e instanceof RuntimeInterruptedException); + Thread.currentThread().interrupt(); + + throw SwerveRuntimeException.wrap(e); + } } diff --git a/YourCodeHere/src/main/java/org/usfirst/ftc/exampleteam/yourcodehere/MyRobotControllerAdministration.java b/YourCodeHere/src/main/java/org/usfirst/ftc/exampleteam/yourcodehere/MyRobotControllerAdministration.java index 0b5c6702945..befc1b96ad5 100644 --- a/YourCodeHere/src/main/java/org/usfirst/ftc/exampleteam/yourcodehere/MyRobotControllerAdministration.java +++ b/YourCodeHere/src/main/java/org/usfirst/ftc/exampleteam/yourcodehere/MyRobotControllerAdministration.java @@ -7,7 +7,7 @@ import com.qualcomm.ftcrobotcontroller.opmodes.*; /** - * MyRobotControllerAdministration is a container for 'administrative' methods that intereract + * MyRobotControllerAdministration is a container for 'administrative' methods that interact * with the Swerve library. You don't have to put your administrative methods in a separate * class as we do here, but it does help keep them neat and tidy. Administrative methods are * each tagged with a Java annotation that connotes and bestows their significance; see the @@ -79,10 +79,7 @@ public static void registerMyOpModes(Context context, IOpModeManager manager) @OnRobotRunning public static void playSoundOnRobotRunning(Context context) { - MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.nxtstartup); - mediaPlayer.start(); - while (mediaPlayer.isPlaying()) - Thread.yield(); + playSound(context, R.raw.nxtstartup); } /** @@ -99,9 +96,16 @@ public static void playSoundOnRobotRunning(Context context) @OnRobotStartupFailure public static void playSoundOnRobotStartupFailure(Context context) { - MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.chord); + playSound(context, R.raw.chord); + } + + /** Plays a sound given the sounds identity as a (raw) resource. */ + static void playSound(Context context, int resource) + { + MediaPlayer mediaPlayer = MediaPlayer.create(context, resource); mediaPlayer.start(); while (mediaPlayer.isPlaying()) Thread.yield(); + mediaPlayer.release(); } }