כתיבה מחדש של תכנות מבוסס פקודות 2020: מה השתנה?
This article provides a summary of changes from the original command-based framework to the 2020 rewrite. This summary is not necessarily comprehensive - for rigorous documentation, as always, refer to the API docs (Java, C++).
Package Location
The new command-based framework is located in the wpilibj2 package for Java, and in the frc2 namespace for C++. The new framework must be installed using the instructions: WPILib Command Libraries.
Major Architectural Changes
The overall structure of the command-based framework has remained largely the same. However, there are some still a few major architectural changes that users should be aware of:
Commands and Subsystems as Interfaces
Command (Java, C++) and Subsystem (Java, C++) are both now interfaces as opposed to abstract classes, allowing advanced users more potential flexibility. CommandBase and SubsystemBase abstract base classes are still provided for convenience, but are not required. For more information, see Commands and מערכות.
Multiple Command Group Classes
The CommandGroup class no longer exists, and has been replaced by a number of narrower classes that can be recursively composed to create more-complicated group structures. For more information see Command Compositions.
Inline Command Definitions
Previously, users were required to write a subclass of Command in almost all cases where a command was needed. Many of the new commands are designed to allow inline definition of command functionality, and so can be used without the need for an explicit subclass. For more information, see Included Command Types.
Injection of Command Dependencies
While not an actual change to the coding of the library, the recommended use pattern for the new command-based framework utilizes injection of subsystem dependencies into commands, so that subsystems are not declared as globals. This is a cleaner, more maintainable, and more reusable pattern than the global subsystem pattern promoted previously. For more information, see Structuring a Command-Based Robot Project.
Command Ownership (C++ Only)
The previous command framework required users to use raw pointers for all commands, resulting in nearly-unavoidable memory leaks in all C++ command-based projects, as well as leaving room for common errors such as double-allocating commands within command-groups.
The new command framework offers ownership management for all commands. Default commands and commands bound to buttons are typically owned by the scheduler, and component commands are owned by their encapsulating command groups. As a result, users should generally never heap-allocate a command with new unless there is a very good reason to do so.
Transfer of ownership is done using perfect forwarding, meaning rvalues will be moved and lvalues will be copied (rvalue/lvalue explanation).
Changes to the Scheduler
Interruptibility of commands is now the responsibility of the scheduler, not the commands, and can be specified during the call to
schedule.Users can now pass actions to the scheduler which are taken whenever a command is scheduled, interrupted, or ends normally. This is highly useful for cases such as event logging.
Changes to Subsystem
הערה
For more information on subsystems, see מערכות.
As noted earlier,
Subsystemis now an interface (Java, C++); the closest equivalent of the oldSubsystemis the newSubsystemBaseclass. Many of the Sendable-related constructor overloads have been removed to reduce clutter; users can call the setters directly from their own constructor, if needed.initDefaultCommandhas been removed; subsystems no longer need to ”know about“ their default commands, which are instead registered directly with theCommandScheduler. The newsetDefaultCommandmethod simply wraps theCommandSchedulercall.Subsystems no longer ”know about“ the commands currently requiring them; this is handled exclusively by the
CommandScheduler. A convenience wrapper on theCommandSchedulermethod is provided, however.
Changes to Command
הערה
For more information on commands, see Commands.
As noted earlier,
Commandis now an interface (Java, C++); the closest equivalent of the oldCommandis the newCommandBaseclass. Many of the Sendable-related constructor overloads have been removed to reduce clutter; users can call the setters directly from their own constructor, if needed.Commands no longer handle their own scheduling state; this is now the responsibility of the scheduler.
The
interrupted()method has been rolled into theend()method, which now takes a parameter specifying whether the command was interrupted (falseif it ended normally).The
requires()method has been renamed toaddRequirement().void setRunsWhenDisabled(boolean disabled)has been replaced by an overridable runsWhenDisabled method.void setInterruptible(boolean interruptible)has been replaced by an overridable getInterruptionBehavior method.Several ”decorator“ methods have been added to allow easy inline modification of commands (e.g. adding a timeout).
(C++ only) In order to allow the decorators to work with the command ownership model, a CRTP is used via the
CommandHelperclass. Any user-defined Command subclassFoomust extendCommandHelper<Foo, Base>whereBaseis the desired base class.
Changes to PIDSubsystem/PIDCommand
הערה
For more information, see PID Control through PIDSubsystems and PIDCommands, and בקרת PID עם WPILib
Following the changes to PIDController, these classes now run synchronously from the main robot loop.
The
PIDControlleris now injected through the constructor, removing many of the forwarding methods. It can be modified after construction withgetController().PIDCommandis intended largely for inline use, as shown in the GyroDriveCommands example (Java, C++).If users wish to use PIDCommand more ”traditionally,“ overriding the protected
returnPIDInput()andusePIDOutput(double output)methods has been replaced by modifying the protectedm_measurementandm_useOutputfields. Similarly, rather than callingsetSetpoint, users can modify the protectedm_setpointfield.