Skip to content

cLarson-asbHum/hardware-faker

Repository files navigation

Tests

HardwareFaker

This project contains several test fakes of hardware devices common to FTC projects. Currently implemented test doubles include the following:

  • ColorRangeSensor
  • CRServoImplEx
  • DcMotorControllerEx
  • DcMotorImplEx
  • DigitalChannelController
  • DigitalChannelImpl
  • DistanceSensor
  • LED
  • LynxModule
  • LynxModule (partial, see "Known Issues")
  • LynxUsbDeviceImpl
  • ServoControllerEx
  • ServoImplEx

Along with these, the following classes are planned to be implemented:

  • AnalogInput
  • Gamepad
  • HardwareMap
  • IMU
  • Telemetry
  • TouchSensor

The goal of these fakes is to ultimately be able to test and entire opmode completely offline (separate from a robot) and so have faster, more reliable development.

Intallation

  1. Copy the HardwareFaker directory into your project, and add the following line to the top-level settings.gradle:
include ":HardwareFaker" // Or whatever you rename the directory to.
  1. Include the following code in your project's build.gradle (most commonly in the :TeamCode subproject):
dependencies {
    // ... other statements

    implementation project(':HardwareFaker') // Or whatever you rename the directory to.

    // ...
}
  1. Import the desired test double. All fakes are located in the clarson.ftc.faker package and have the class name formula of ${HardwareDevice}Fake. For example, to import the test double for DcMotorEx, place the following import statement at the beginning of your code:
import clarson.ftc.faker.DcMotorExFake;
  1. Construct the fake. DcMotorExFake is constructed with an RPM and number of ticks per revolution, in that order. Specifics for other constructors vary.

  2. Use the fake wherever you would use the ordinary object! All fakes implement the corresponding interfaces; DcMotorExFake implements the DcMotorEx interface from com.qualcomm.robotcore.hardware, and so the following code is completely valid:

DcMotorImplEx motor = new DcMotorImplExFake(312, 576.6);
motor.setMode(DcMotorEx.RunMode.RUN_USING_ENCODER);
motor.getMode(); // Returns DcMotorEx.RunMode.RUN_USING_ENCODER

motor.setPower(0.33); // The motor's current position will begin to move
motor.setMode(DcMotorEx.RunMode.STOP_AND_RESET_ENCODER); // Resets getCurrentPosition()

NOTE: If you wanted the test double of DcMotor, you would simply use the DcMotorImplExFake constructor as normal but use the field/variable as DcMotor. In other words, line 1 in the previous example becomes

DcMotor motor = new DcMotorImplExFake(312, 576.6);
// Previously was "DcMotorEx motor = new DcMotorImplExFake(312, 576.6)" 

This also applies for other interfaces, such as Servo and CRServo; use the fake with "Ex" in its name. For Servo, use ServoImplExFake, and with CRServo use CRServoImplExFake.

Using ModularUpdater

In real opmodes with real hardware, methods that send commands to the Lynx board (a part of Control and Expansion Hubs) take time, upwards of 2 milliseconds. As a result, such hardware calls are responsible for both latency but also- and more importantly- the movement of actuators and updating of sensors after each hardware call. This effect can be simulated by manually calling update() on fakes implementing the Updateable interface, or by registering such Updateables with a ModularUpdater object.

Updateables can be registered using the ModularUpdater.register method.

All ModularUpdater-related classes are in the clarson.ftc.faker.updater package. This includes ModularUpdater and other classes, listed below:

Symbol Name Synopsis
ModularUpdater Concrete subclass of the Updater interface
Rotateable Interface for fakes able to have external rotation applied
SimulateDelay Annotates a method as causing automatic updates
Updateable Interface for fakes able to be registered with Updaters
Updater Interface for ModularUpdater
UpdatesWhen Argument of SimulateDelay
TwoWayUpdateable An Updateable that can remember registered Updaters

Automatic update() Calls

Automatic updating is done whenever a registered Updateable calls a method whose real life counterpart would send a command to the Lynx board. Such methods include setPower(), getMode(), setPositon(), getDistance(), etc. This calls the update() method on all Updateables registered on the same Updater.

@SimulateDelay is used to document a method as causing an automatic update to all Updaters it is registered in. The meaning of each enum constant is listed below:

UpdatesWhen Constant Description
ALWAYS Always automatically updates all reigstered Updaters.
CONDITIONAL Depends on a conditon that is not related to the BulkData cache
NEVER Never causes an automatic update
ON_BULK_READ Automatically updates whenever a reigstered ModularUpdater has an outdated LynxModule cache

Although methods without this annotation can be assumed to never cause an automatic update, some methods may be explicitly annotated with @SimulateDelay(UpdatesWhen.NEVER).

NOTE: @SimulateDelay annotation is only retained in source, and cannot be used at runtime to verify a method causes automatic updates.

Each automatic update has a delay length of 2.5 milliseconds unless the source is an I2C command, in which the delay is 7.5 ms. Manual updates must specify delay length in seconds using the update() method's parameter deltaSec.

NOTE: "Delay length" refers to the length of time simulated, not actually elapsing. In other words, update() does not block the calling thread for 2.5 milliseconds; instead, the real-life hardware the updated fake represents would block all threads for such time, causing all actuators to move and sensors to update.

All classes capable of automatic updates implement the TwoWayUpdateable interface.

Known Issues

  • ServoControllerExFake.setServoType() is provided for compatability and does not affect how the servo interacts

  • DcMotorControllerExFake.setMotorType() only respects the orientation field on the given configuration, but changing this from CW to CCW is untested and expected to cause unintended behavior.

  • All actuators assume acceleration and friction are negligible. This is most notable with DcMotorExFake.setVelocity(), which instantly changes the velocity.

  • All PID(F) coefficient-setting methods in DcMotorExFake do nothing as neither RUN_TO_POSITION nor RUN_USING_ENCODER are implemented with PID(F) controllers.

  • Framing length in PwmController.PwmRange objects is ignored for all servo fakes, continuous or positional.

  • Not all LynxMessages are supported to be transmitted to LynxUsbDeviceImplFake. For maintainers and advanced users, unsupported messages are sent LynxNacks with error code 254 (COMMAND_ROUTING_ERROR). Nothing is done except have an error be recieved. The following codes are supported:

    • LynxGetBulkInputDataCommand,
    • LynxGetMotorEncoderPositionCommand (for bulk data retrieval),
    • LynxIsMotorAtTargetCommand (for bulk data retrieval),
    • LynxKeepAliveCommand,
    • LynxQueryInterfaceCommand.

About

Test fakes that can be used within any FTC opmode, designed to be as easy as possible to utilize and as accurate to the behvaior real hardware.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages