Presentation is loading. Please wait.

Presentation is loading. Please wait.

Team 488 Java Architecture TESTABILITY!. Primary Goals Critical logic can be run without access to a cRIO or Robot ◦Multiple teams (Electrical, Mechanical,

Similar presentations


Presentation on theme: "Team 488 Java Architecture TESTABILITY!. Primary Goals Critical logic can be run without access to a cRIO or Robot ◦Multiple teams (Electrical, Mechanical,"— Presentation transcript:

1 Team 488 Java Architecture TESTABILITY!

2 Primary Goals Critical logic can be run without access to a cRIO or Robot ◦Multiple teams (Electrical, Mechanical, Programming) need access to the robot or a cRIO, so this has often been a bottleneck for us Supports writing and running test cases ◦Now that our programming team has a large pool of programmers, the odds of making a change that causes a regression has increased ◦In addition, we want to be able to write proof-of-concept tests during development Has reusable components for future seasons (and other teams) ◦The progress we make this year should be applicable to future years

3 Running without a Robot To support this idea, the robot’s logic needs to be decoupled from the exact robot implementation. We had multiple possible approaches: ◦Dependency injection/targeting: At compile or runtime, link the core code against either the real robot environment OR some test environment ◦Abstraction: At compile time, choose some point in the dependency stack to abstract out, and create an adapter for the Robot, and an adapter for the Test environment Our team chose Abstraction over Dependency injection. Our reasoning was as follows: When doing dependency injection, we would need to “stub out” all the functions at that particular layer. WPILib and VXWorks both have a large number of function calls, and WPILib in particular changes every year. Fundamentally, we believe this would mean we would need to spend time at the start of each competition season bringing our model up to date.

4 Where to Abstract? We had some choices where in our dependency stack we should put the adapter. Basically, the further away you get from your own code, the more “authoritative” your testing is, since the test environment will more closely resemble the real environment. However, testing can be more complicated, since you need to emulate lower-level details. The closer you put it to your own code: ◦The less authoritative the testing is ◦The test cases and environment are much easier to write.

5 In order of increasing abstraction VxWorks (cRIO) Direct calls into hardware WPI Lib Wrappers for sensors and actuators Command-based Robot Framework Commands, Subsystems, Scheduler “Idea” of the Robot Arm, Drive, State Machines, etc… Where we decided to separate from the stack

6 Our Rationale We decided to decouple the “Idea” of the robot and the Command-based Robot Framework (CBRF). ◦While there will likely be bugs in the CBRF and below, many teams will be exercising the same code path, so there are lots of eyes on this particular problem ◦Most of the bugs we encounter during the season will likely be in our own code & assumptions, and by emulating the CBRF in a test project we should be able to focus almost entirely on those bugs. ◦The high level of abstraction means that we don’t need to write much code to adapt our work onto the framework

7 Architecture Concept Robot Core ◦Contains the “idea” of the robot – systems, commands that operate on those systems, all logic and state machines Actual Robot ◦Instantiates a Robot Core and gives it an environment to work in. For example, the Robot Core could have an “Arm Motor,” and the actual robot will link it to a Speed Controller (Motor). Test Project ◦Instantiates a Robot Core and gives it an environment to work in that is easily modified / viewed. For example, Robot Core could have an “Arm Motor,” and the test project will provide it with a TestMotor – and our test cases can read the value of this motor at any time and compare it against expected values.

8 CommonTools RobotContext Time System Logging System Property Manager WorkersSubsystems Visualized Design (High Level) Robot TimeSource LogWriter SmartDashboard Wrapper Storage Test TestTime TestWriter TestPropertyMap TestStorage ActualRobot RobotCore Test Device Updater Test Cases

9 CommonTools Design Overview

10 Fundamental Design The overall “idea” of the robot is contained in RobotCore in the RobotContext. This is where systems (DriveCore, ArmCore, ShooterCore, etc…) are defined, as well as the Workers that operate on such systems (DriveWithJoysticksWorker, ShootDiscWorker, CollectDiscWorker). CommonTools contains utilities meant to be used across multiple robots, such as Time, Logging, and a PropertyManager (for loading/saving configuration data on the robot, and modifying those values at runtime). CommonTools also contains abstract classes that define work that it needs done. For example, you can ask the Logger in CommonTools to log data, but how is that data actually saved? ◦ActualRobot extends LogWriter which saves to the cRIO’s flash storage ◦Test extends LogWriter which saves to a string variable that test cases can access

11 CommonTools It’s a singleton, meant to be initialized by an entity that can provide the following objects: ◦A class that can determine what time it is (Time) ◦A class that can write logs to disk (LogWriter) ◦A class that can hold a key-value collection in memory (ITableProxy) ◦A class that can load/save a key-value collection to storage (IPermanentStorage) It also contains a LowPriorityTasks object that, much like the watchdog, should be fed (by calling Feed()) at regular intervals. This class currently: ◦Pulls logs off a queue and writes them to disc ◦Periodically saves properties to permanent storage ◦(More to come) Finally, it contains a LoggingQueue.

12 CommonTools: Time This is a small utility class that makes it easy to determine how long it has been since a previous call. The two most commonly called methods are: GetMarker() – this method returns an object representing this moment in time. GetTimeElapsedSinceMarker(marker initial) – this method returns the number of milliseconds that have elapsed since the given marker was created

13 CommonTools: Logger The logging system is actually composed of two classes – LogProducer and LogConsumer. LogProducer exposes useful methods such as Log(string message), LogError(string message), etc. These methods add their message to the CommonTools’ LoggingQueue. LogConsumer exposes a Consume() method. This method pulls a set number of messages off of the LoggingQueue and prepares to send them to the LogWriter. ◦In order to preserve the longevity of the cRIO’s flash memory, the LogConsumer only actually writes to the LogWriter if the aggregate number of messages > 7850 bytes, or if it has been over a minute since the last call to log anything. ◦This is because the cRIO’s flash memory has a limited number of writes, and the VXWorks OS has a 4k minimum write. Given an average log length of 120 bytes, this typically reduces the number of disc writes by ~33x.

14 CommonTools: PropertyManager The PropertyManager handles configuration data for the system, such as PID values, or other “tweakable” constants. It contains a list of Properties, and uses the ITableProxy and IPermanentStorage classes to: ◦Load these properties from a file on the robot at boot time ◦Show properties on the SmartDashboard ◦When properties are changed on the SmartDashboard, allow the robot to begin consuming the new value immediately ◦Save these properties periodically to the robot if the values have changed

15 RobotContext Design Overview

16 RobotContext Is a singleton that will only instantiate itself if CommonTools has already been instantiated. It contains all the Systems and Workers that are used for robot operation.

17 RobotContext: Systems Systems represent things that the Robot is capable of doing. They are analogous to “Subsystems” in WPILib. Some example Systems: OICore, DriveCore, ShooterCore. Subsystems have member variables that represent actuators and sensors that are relevant to that subsystem. Sensor values can be set by external callers, but actuator values can only be set privately. For example, the DriveCore system contains the variables: ◦LeftDrive power (represents power to an actuator, such as a motor) ◦RightDrive power (””) ◦LeftDrive speed (represents value from a sensor, such as an encoder) ◦RightDrive speed (“”) And the method: ◦TankDrive(double left, double right) which assigns power values based on the left/right inputs.

18 RobotContext: Workers Workers are entities that understand how to use Systems to achieve goals. For example, the “DriveWithJoysticks” worker knows how to read values from one system (Joystick values from OICore) and assign them to another (TankDrive in DriveCore) They are analogous to Commands in WPILib, and have several of the same methods (Execute, IsFinished…)

19 ActualRobot Design Overview

20 ActualRobot: Robot.java The ActualRobot has three major responsibilities in order to use the RobotCore appropriately: 1) It must instantiate the CommonTools singleton, and provide it with implementations of it’s utility classes (RobotTime, RobotLogWriter, SmartDashboardProxy, RobotPermanentStorage) 2) In a fairly rapid loop, it must read all the actuator values on the Systems and assign them to the real actuators. It must also read all of its sensor values and assign them to the Systems. ◦For example, it must read the Left/Right power value from the DriveCore and assign those powers to the speed controllers for the left and right drive motors. ◦It must also read the left/right drive encoders and assign those values onto the DriveCore left/right speed sensors. 3) Finally, in a fairly rapid loop, it must call Feed() on the CommonTools.LowPriorityTasks.

21 ActualRobot: Commands So far, we still depend on WPILib’s scheduler to actually do work on the robot. As such, we create Commands that are just proxies for workers. ◦There will be a DriveWithJoysticks command, but internally it just calls DriveWithJoysticksWorker We are currently working on a state-machine evaluator system that will run alongside the WPILib scheduler and allow for powerful decision-making logic, as a replacement for the fairly static CommandGroups.

22 Tests Design Overview

23 Architecture Our team uses JUnit to write unit tests for the Workers/Subsystems of RobotContext as well as for the utility classes of CommonTools. Our tests all follow the overall pattern of ◦Initializing CommonTools with test utilities that are easily read/manipulated by test cases ◦Taking an object under test and providing it some stimulus (such as setting a Joystick value and then calling DriveWithJoysticksWorker.Execute) ◦Reading the response of the stimulus and comparing it to an expected value (such as reading the values of the drive motors and making sure they correspond to the joystick values we set earlier) ◦Reporting success or failure

24 Questions? Feel free to email: John Gilbert (johngilb@gmail.com)johngilb@gmail.com Alex Schokking (aschokking@outlook.com)aschokking@outlook.com Sterling Dorminey (sterling.dorminey@gmail.com)sterling.dorminey@gmail.com

25 Link to Repository https://github.com/Team488/Shared


Download ppt "Team 488 Java Architecture TESTABILITY!. Primary Goals Critical logic can be run without access to a cRIO or Robot ◦Multiple teams (Electrical, Mechanical,"

Similar presentations


Ads by Google