Configuration ============= Devices support persistent settings through the use of "configs". .. tip:: Configs can also be configured using Phoenix Tuner X. See :ref:`docs/tuner/configs:tuner configs` for more information. Configuration Objects --------------------- There are device-specific ``Configuration`` classes that group configuration data of devices in a meaningful way. These classes are `Passive Data Structures `__. One example is ``TalonFXConfiguration``, which has subgroups of configs such as ``MotorOutputConfigs``. The configs can be modified through public member variables of the ``Configuration`` object. The complete list of configuration objects can be found in the API documentation (`Java `__, `C++ `__, `Python `__). .. note:: Phoenix 6 utilizes the `C++ units library `__ and, optionally, the `Java units library `__ when applicable. Using the Java units library may increase GC overhead. .. tab-set:: .. tab-item:: Java :sync: Java .. code-block:: Java var talonFXConfigs = new TalonFXConfiguration(); .. tab-item:: C++ :sync: C++ .. code-block:: c++ configs::TalonFXConfiguration talonFXConfigs{}; .. tab-item:: Python :sync: python .. code-block:: python talonfx_configs = configs.TalonFXConfiguration() .. tip:: The device ``Configuration`` objects document all available configuration parameters in the documentation of each public member variable, making it easy to find specific configs (`example `__). Modifying Configurations ^^^^^^^^^^^^^^^^^^^^^^^^ Configuration objects are mutable, so they can be saved in a member variable and reused. Additionally, configuration objects support modification using method chaining. This can be useful for constructing them as a class member variable or at compile time. In Java, this can also be used to provide unit types. Configuration and config group objects can also be cloned, making it easy to share common configs across device configurations. .. tab-set:: .. tab-item:: Java :sync: Java .. code-block:: Java final TalonFXConfiguration commonConfigs = new TalonFXConfiguration() .withMotorOutput( new MotorOutputConfigs() .withNeutralMode(NeutralModeValue.Brake) ) .withCurrentLimits( new CurrentLimitsConfigs() .withStatorCurrentLimit(Amps.of(120)) .withStatorCurrentLimitEnable(true) ); /* create a copy with a different invert */ final TalonFXConfiguration leaderConfigs = commonConfigs.clone() .withMotorOutput( commonConfigs.MotorOutput.clone() .withInverted(InvertedValue.Clockwise_Positive) ); .. tab-item:: C++ :sync: C++ .. code-block:: c++ static constexpr configs::TalonFXConfiguration commonConfigs = configs::TalonFXConfiguration{} .WithMotorOutput( configs::MotorOutputConfigs{} .WithNeutralMode(signals::NeutralModeValue::Brake) ) .WithCurrentLimits( configs::CurrentLimitsConfigs{} .WithStatorCurrentLimit(120_A) .WithStatorCurrentLimitEnable(true) ); /* create a copy with a different invert */ configs::TalonFXConfiguration leaderConfigs = configs::TalonFXConfiguration{commonConfigs} .WithMotorOutput( configs::MotorOutputConfigs{commonConfigs.MotorOutput} .WithInverted(signals::InvertedValue::Clockwise_Positive) ); .. tab-item:: Python :sync: python .. code-block:: python self._common_configs = ( configs.TalonFXConfiguration() .with_motor_output( configs.MotorOutputConfigs() .with_neutral_mode(signals.NeutralModeValue.BRAKE) ) .with_current_limits( configs.CurrentLimitsConfigs() .with_stator_current_limit(120.0) .with_stator_current_limit_enable(True) ) ) # create a copy with a different invert self._leader_configs = ( copy.deepcopy(self._common_configs) .with_motor_output( copy.deepcopy(self._common_configs.motor_output) .with_inverted(signals.InvertedValue.CLOCKWISE_POSITIVE) ) ) Future Proofing Configs ^^^^^^^^^^^^^^^^^^^^^^^ There is a corner case with configs where the device may have firmware with newer configs that didn't exist when the version of the API was built. To account for this problem, device ``Configuration`` objects have a ``FutureProofConfigs`` (`Java `__, `C++ `__, `Python `__) field. Configurator API ---------------- Device objects have a ``getConfigurator()`` method that returns a device-specific ``Configurator`` object. The ``Configurator`` is used to retrieve, apply, and factory default the configs of a device. .. note:: The ``getConfigurator()`` routine can be called frequently without any performance implications. The device-specific configurators have type-specific overloads that allow for the widest variety of device-compatible configs. As a result, the caller can pass the entire device ``Configuration`` object or just the relevant subgroup of configs to the ``Configurator`` API. .. tab-set:: .. tab-item:: Java :sync: Java .. code-block:: Java var talonFXConfigurator = m_talonFX.getConfigurator(); .. tab-item:: C++ :sync: C++ .. code-block:: c++ auto& talonFXConfigurator = m_talonFX.GetConfigurator(); .. tab-item:: Python :sync: python .. code-block:: python talonfx_configurator = self.talonfx.configurator Reading Configs ^^^^^^^^^^^^^^^ To read configs stored in a device, use the ``refresh()`` method to update a ``Configuration`` object. The example below demonstrates retrieving a full ``TalonFXConfiguration`` (`Java `__, `C++ `__, `Python `__) object from a ``TalonFX`` device. .. warning:: ``refresh()`` is a blocking API call that waits on the device to respond. Calling ``refresh()`` periodically may slow down the execution time of the periodic function, as it will always wait up to ``DefaultTimeoutSeconds`` (`Java `__, `C++ `__, `Python `__) for the response when no timeout parameter is specified. .. tab-set:: .. tab-item:: Java :sync: Java .. code-block:: Java var talonFXConfigurator = m_talonFX.getConfigurator(); var talonFXConfigs = new TalonFXConfiguration(); // optional timeout (in seconds) as a second optional parameter talonFXConfigurator.refresh(talonFXConfigs); .. tab-item:: C++ :sync: C++ .. code-block:: c++ auto& talonFXConfigurator = m_talonFX.GetConfigurator(); configs::TalonFXConfiguration talonFXConfigs{}; // optional timeout (in seconds) as a second optional parameter talonFXConfigurator.Refresh(talonFXConfigs); .. tab-item:: Python :sync: python .. code-block:: python talonfx_configurator = self.talonfx.configurator talonfx_configs = configs.TalonFXConfiguration() # optional timeout (in seconds) as a second optional parameter talonfx_configurator.refresh(talonfx_configs) Applying Configs ^^^^^^^^^^^^^^^^ Configs can be applied to a device by calling ``apply()`` on the ``Configurator`` with a ``Configuration`` object. During initialization, it is recommended that a full device ``Configuration`` object is applied instead of separate config groups to ensure that configs left unmodified by code are factory defaulted. .. warning:: ``apply()`` is a blocking API call that waits on the device to respond. Calling ``apply()`` periodically may slow down the execution time of the periodic function, as it will always wait up to ``DefaultTimeoutSeconds`` (`Java `__, `C++ `__, `Python `__) for the response when no timeout parameter is specified. .. tab-set:: .. tab-item:: Java :sync: Java .. code-block:: Java var talonFXConfigurator = m_talonFX.getConfigurator(); var motorConfigs = new TalonFXConfiguration(); // set invert to CW+ motorConfigs.MotorOutput.Inverted = InvertedValue.Clockwise_Positive; // apply all configs to the motor talonFXConfigurator.apply(motorConfigs); // later in the match, change the neutral mode to Brake motorConfigs.MotorOutput.NeutralMode = NeutralModeValue.Brake; // apply only the MotorOutput configs, leaving all other configs untouched talonFXConfigurator.apply(motorConfigs.MotorOutput); .. tab-item:: C++ :sync: C++ .. code-block:: c++ auto& talonFXConfigurator = m_talonFX.GetConfigurator(); configs::TalonFXConfiguration motorConfigs{}; // set invert to CW+ motorConfigs.MotorOutput.Inverted = signals::InvertedValue::Clockwise_Positive; // apply all configs to the motor talonFXConfigurator.Apply(motorConfigs); // later in the match, change the neutral mode to Brake motorConfigs.MotorOutput.NeutralMode = signals::NeutralModeValue::Brake; // apply only the MotorOutput configs, leaving all other configs untouched talonFXConfigurator.Apply(motorConfigs.MotorOutput); .. tab-item:: Python :sync: python .. code-block:: python talonfx_configurator = self.talonfx.configurator motor_configs = configs.TalonFXConfiguration() # set invert to CW+ motor_configs.motor_output.inverted = signals.InvertValue.CLOCKWISE_POSITIVE # apply all configs to the motor talonfx_configurator.apply(motor_configs) # later in the match, change the neutral mode to Brake motor_configs.motor_output.neutral_mode = signals.NeutralModeValue.BRAKE # apply only the motor_output configs, leaving all other configs untouched talonfx_configurator.apply(motor_configs.motor_output) .. tip:: To modify a single configuration value without affecting the other configs, users can call ``refresh()`` after constructing the config object, or users can cache the config object and reuse it for future calls to ``apply()``. Applying and refreshing a single config group can be faster in this scenario. Factory Default ~~~~~~~~~~~~~~~ A newly-created ``Configuration`` object contains the default configuration values of a device. As a result, it is **unnecessary** to factory default a device before applying a modified device ``Configuration`` object. A device's configs can be explicitly restored to the factory defaults by passing a newly-created ``Configuration`` object to the device ``Configurator``. .. tab-set:: .. tab-item:: Java :sync: Java .. code-block:: Java m_talonFX.getConfigurator().apply(new TalonFXConfiguration()); .. tab-item:: C++ :sync: C++ .. code-block:: c++ m_talonFX.GetConfigurator().Apply(configs::TalonFXConfiguration{}); .. tab-item:: Python :sync: python .. code-block:: Python self.talonfx.configurator.apply(configs.TalonFXConfiguration())