C++\Java Changes and Porting Guide - 2015 to 2016
Our general philosophy with WPILib is to maintain backward compatibility as much as possible in the team-facing APIs. Having said that, sometimes circumstances present a compelling case for breaking that compatibility when making changes and improvements to the library. This document aims to cover the API level changes between the 2015 and 2016 libraries.
In the past, a prestart() function existed that would be called before a robot would appear to the FMS as 'ready' to play. However, this was confusing, and served mostly the same function as robotInit(), and was inconsistent between SampleRobot and IterativeRobot.
As of 2016, the robotInit() function will be called before the FMS is notified that the robot is ready to play. Teams are encouraged to put things that should happen before the match starts (such as gyro initialization) in the robotInit() function.
SD540 and SPARK
Classes have been added for the SD540 and SPARK motor controllers
Both C++ and Java now use a new library called “ntcore” to provide NetworkTables functionality. This library is backwards compatible with NetworkTables 2.0 clients and servers but provides new functionality and more robust operation. Key features of the new protocol and implementation include:
- Entry deletion: Note: if 2.0 clients are used entry deletion may be ineffective.
- Raw data type: When you want truly raw data (like a byte array) rather than a user-readable string.
- Client and server self-identification: Each 3.0 client provides a name to the server. This provides a more reliable method than simply the remote IP address for determining on the server side whether or not a particular client is connected. Note: the server connection information is not provided from the server to the clients, so it is not possible for a client to determine what other clients are connected to the server.
- Entry flags: Each entry now has an 8-bit flags value associated with it. This is only used at present to indicate entry persistence (the next item).
- Entry persistence: The server (the robot) provides a feature that automatically saves entries (once per second) to a file on the robot (/home/lvuser/networktables.ini). By default, no values are automatically saved in this manner, but any client or the server may set the persistent flag on an entry to indicate the server must persist that entry. On startup, the server loads the persistent file to create the initial set of server entries. This functionality is now used by the Preferences class but can be applied to any entry.
From a team perspective, the NetworkTable class (now a thin wrapper class around the ntcore library API) is largely backwards-compatible, although many function signatures have changed, and the exception-raising getter functions have been deprecated in favor of functions that take a default return value (to reduce the risk of exceptions crashing team code when values are unexpectedly not set due to startup order or network communications issues). New functions have been added to support the new data types and functionality in the 3.0 version of the protocol. Array setters and getters now take and return arrays (or in C++, std::vectors) rather than special data types. All setters now return a boolean indicating if the set operation failed (can happen if a value with the same name but different type already exists).
In C++, StringRef’s are used instead of const char*’s for key (name) parameters. The ValueChanged() function in ITableListener has changed to take a StringRef for the key and a std::shared_ptr<nt::Value> for the value. GetSubTable() now returns a std::shared_ptr<ITable> instead of a raw pointer to ITable.
The Preferences class now uses the new Network Tables Persistent Variables to save preference data. This means that it no longer uses the same .ini file on the RoboRIO for storage. Preferences can be backed up and restored using the Save and Load buttons on the Robot Preferences SmartDashboard widget.
In order to better support digital gyros, the Gyro class has been changed to an interface. The old Gyro class has been renamed to AnalogGyro.
Additionally, gyro calibration has been separated from gyro initialization. This allows re-calibration at any time after construction.
The setPIDSourceParameter() and getPIDSourceParameter() methods were renamed to setPIDSourceType() and getPIDSourceType() respectively and added to the PIDSource interface.
The PIDSourceParameter enum was also renamed to PIDSourceType. kPosition in that enum was renamed to kDisplacement.
PID Feed Forward
The feed forward term in PIDController can now be calculated by the end user (without needing to override Calculate()) by overriding CalculateFeedForward().
When PIDSourceType is set to kRate, the PIDController class now uses differentnt calculations to perform PID control with a velocity input. The P and D gains now perform similar actions to what one expects from position PID controllers. The I gain is not used.
GetInverted() methods added to check if a SpeedController is inverted.
SetInverted() added to SpeedController interface. Implementers must now implement this method.
CANTalon now LiveWindowSendable
The CANTalon class now implements LiveWindowSendable and can be displayed on the SmartDashboard. This includes LiveWindow support for PID control.
CANTalon Now Provides Motion Profile Mode
The CANTalon firmware now supports processing motion profiles. To use this functionality, set the control mode to kMotionProfileMode. Trajectory points are passed in as position-velocity pairs with a time duration.
Methods have been added to the Joystick class to get the name of the joystick (same as shown in the DS), whether the Joystick is an XBox controller (technically whether the DS is using XInput for the device) and the HIDtype (see the HIDType enum for options).
PID Controller D term calculation
The PIDController Derivative term calculation has been modified to calculate off of changes in the process variable, not changes in the error (this avoids large D term values on step changes in the setpoint)
Digital Glitch Filter
When enabled on a digital input, the new DigitalGlitchFilter class lets the user configure the time that an input must remain high or low before it is classified as high or low.
Linear Digital Filter
A linear digital filter class (LinearDigitalFilter) that implements linear FIR and IIR filters was added. The current interface provides a single-pole IIR (low-pass) filter, high-pass filter, and moving average filter. This class was based on code from FRC team 341.
I2C Read/Write limits
The 7 byte limit on I2C reads and 6 byte limit on I2C writes has been removed.
64 Bit Time
GetFPGATime() (getFPGATime() in Java) in the Utility class now returns a 64-bit integer, instead of a 32-bit integer. This means time will no longer overflow after 70 minutes. Similarly, the Timer and Notifier classes no longer have issues with time durations longer than 70 minutes or operation across a 70 minute rollover boundary. While unlikely to be an issue in competition, this may avoid issues previously seen in long practice sessions.
A CircularBuffer class was added. It is a double-ended queue which uses an array as its internal storage. When elements are pushed and popped, no memory allocation takes place.
ADXL345 Alternate I2C Addresses
The I2C device address can now be specified when configuring an ADXL345_I2C sensor.
PDP non-zero address
The C++ and Java PowerDistributionPanel classes now have a single parameter constructor which accepts the PDP address for use with PDP addresses other than 0 or multiple PDPs (this is software support only; check the rules for legality of multiple PDPs on a competition robot!).
C++ - Eclipse Dialog on Build Failure
When the Build triggered as part of the "WPILib C++ Deploy" operation fails, you should now see a dialog prompt instead of silently deploying your last successful build.
C++ - DriverStation GetInstance type
The return type of DriverStation::GetInstance() has changed from a pointer to a reference
C++ - Smart Pointers
WPILib now makes use of shared pointers (std::shared_ptr), and it is recommended that your code follow this convention. Only use the raw pointer interface if you know what you are doing.
Smart pointers provide automatic memory management by enforcing object ownership concepts in one's code (i.e. whose responsibility it is to clean up the object when it is no longer being used). There are two types: std::unique_ptr and std::shared_ptr. The former only allows one owner at a time, but ownership can be transferred via std::move(). The latter shares ownership with several users. When the last owner stops using the object when it leaves scope or is destructed, the object is automatically destroyed by the smart pointer.
Keep in mind this is not non-deterministic garbage collection. The object is constructed and destructed at the same places you would manually, but the destructor is called at the appropriate time for you. std::shared_ptr has the added overhead of a reference count, but std::unique_ptr is simply a pointer with some compile-time machinery to handle automatic destruction.
C++ - const char* changed to std::string in many interfaces
Many interfaces (mostly in Command Based programs) migrated from using const char* parameters to using std::string for the parameter.
C++ - "const" Correctness
WPILib functions that don't mutate any state, like the Get() function of motor controllers, are now const qualified. If some of your classes override these functions, they should be updated to the new function signature. For example, bool IsTimedOut() should be changed to bool IsTimedOut() const.
C++ - Notifier std::function callbacks
The Notifier class now accepts std::function and arbitrary functions and parameters (in the style of the std::thread constructor) rather than just void (*callback) (void *param). This is backwards compatible.
C++ - Task class restructured
Unimplemented C interface functions in the HAL related to task management and spawnTask() were removed. The Task class was also rewritten to provide the same interface and construction semantics as std::thread. However, the first argument is still a string containing the name of the task.
The Task class’s constructor has been changed to accept arbitrary functions and parameters (similar to the notes on Notifier, above, this is in the style of the std::thread constructor).
Additionally, the Task is now started during construction; there is no longer a separate Start() method. Tasks can no longer be suspended, stopped, or resumed. They are, however, now joinable providing a controlling thread the ability to block waiting until a Task has completed.
For now, setting the real-time task priority doesn't work, although it was never implemented on the RoboRIO anyway.
C++ - Synchronization Primitives
The Synchronized class and the CRITICAL_REGION, END_REGION, and SYNCHRONIZE macros were replaced with std::lock_guard and other standard library constructs. SEM_ID was replaced with a nearly platform independent Semaphore class (priority_mutex is platform-specific, but could be replaced with std::mutex).
MUTEX_ID was replaced with the priority_mutex and priority_recursive_mutex classes. They have the same interface as std::mutex and std::recursive_mutex respectively, but also provide protection from priority inversion.
If in need of a condition variable when using a priority mutex, use std::condition_variable_any instead of priority_condition_variable. priority_condition_variable is simply std::condition_variable_any with the ability to return the internal handle like std::condition_variable, and WPILib has a very specific use-case for this internally.
C++ - LiveWindow Call Deprecation
Here the pointer argument for component should be replaced with an STL smart pointer or a reference.
void LiveWindow::AddSensor(const std::string &subsystem,
const std::string &name,
void LiveWindow::AddActuator(const std::string &subsystem,
const std::string &name,
Java - Notifier Added
The Notifier class has been added to the Java library. This class utilizes the FPGA timer to call methods on a periodic basis. It can be used for running tasks which require consistent time bases such as control loops.
Java - I2C and SPI Support ByteBuffer
The I2C and SPI classes now have ByteBuffer interfaces (in addition to byte) for easier, lower-overhead encoding and decoding of multi-byte transactions.
Java - CanTalonSRX SWIG Class Removed
Note: the CANTalon class still exists! CanTalonSRX was a SWIG-generated helper class used by CANTalon to call across the JNI boundary. The CANTalon JNI layer has been completely rewritten and the CANTalon class now directly uses the JNI interface functions. Similarly, the various SWIGTYPE classes previously required by CANTalon have been removed.
Java - JNI Changes
The JNI now uses longs to pass pointers to the HAL rather than ByteBuffers. Status handling is now handled at the C level rather than the Java level. Some function signatures changed (e.g. boolean used instead of int for boolean parameters / return values). Your team is unlikely to be affected by this unless you call directly into JNI functions.