001/*
002 * Copyright (c) 2021 REV Robotics
003 *
004 * Redistribution and use in source and binary forms, with or without
005 * modification, are permitted provided that the following conditions are met:
006 *
007 * 1. Redistributions of source code must retain the above copyright notice,
008 *    this list of conditions and the following disclaimer.
009 * 2. Redistributions in binary form must reproduce the above copyright
010 *    notice, this list of conditions and the following disclaimer in the
011 *    documentation and/or other materials provided with the distribution.
012 * 3. Neither the name of REV Robotics nor the names of its
013 *    contributors may be used to endorse or promote products derived from
014 *    this software without specific prior written permission.
015 *
016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
020 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
021 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
022 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
023 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
025 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
026 * POSSIBILITY OF SUCH DAMAGE.
027 */
028
029package com.revrobotics;
030
031import com.revrobotics.jni.CANSparkMaxJNI;
032
033/** Get an instance of this class by using {@link CANSparkMax#getPIDController()}. */
034public class SparkMaxPIDController implements CANPIDController {
035  private final CANSparkMax sparkMax;
036
037  public enum AccelStrategy {
038    kTrapezoidal(0),
039    kSCurve(1);
040
041    @SuppressWarnings("MemberName")
042    public final int value;
043
044    AccelStrategy(int value) {
045      this.value = value;
046    }
047
048    public static AccelStrategy fromInt(int value) {
049      switch (value) {
050        case 0:
051          return kTrapezoidal;
052        case 1:
053          return kSCurve;
054        default:
055          return kTrapezoidal;
056      }
057    }
058  }
059
060  public enum ArbFFUnits {
061    kVoltage(0),
062    kPercentOut(1);
063
064    @SuppressWarnings("MemberName")
065    public final int value;
066
067    ArbFFUnits(int value) {
068      this.value = value;
069    }
070  }
071
072  // package-private (can only be used by other classes in this package)
073  SparkMaxPIDController(CANSparkMax device) {
074    sparkMax = device;
075  }
076
077  /**
078   * Set the controller reference value based on the selected control mode.
079   *
080   * @param value The value to set depending on the control mode. For basic duty cycle control this
081   *     should be a value between -1 and 1 Otherwise: Voltage Control: Voltage (volts) Velocity
082   *     Control: Velocity (RPM) Position Control: Position (Rotations) Current Control: Current
083   *     (Amps). Native units can be changed using the setPositionConversionFactor() or
084   *     setVelocityConversionFactor() methods of the CANEncoder class
085   * @param ctrl the control type
086   * @return {@link REVLibError#kOk} if successful
087   */
088  public REVLibError setReference(double value, CANSparkMax.ControlType ctrl) {
089    sparkMax.throwIfClosed();
090    return setReference(value, ctrl, 0);
091  }
092
093  /**
094   * Set the controller reference value based on the selected control mode.
095   *
096   * @param value The value to set depending on the control mode. For basic duty cycle control this
097   *     should be a value between -1 and 1 Otherwise: Voltage Control: Voltage (volts) Velocity
098   *     Control: Velocity (RPM) Position Control: Position (Rotations) Current Control: Current
099   *     (Amps). Native units can be changed using the setPositionConversionFactor() or
100   *     setVelocityConversionFactor() methods of the CANEncoder class
101   * @param ctrl the control type
102   * @return {@link REVLibError#kOk} if successful
103   * @deprecated Use {@link #setReference(double, CANSparkMax.ControlType)} instead.
104   */
105  @Deprecated(forRemoval = true)
106  @SuppressWarnings("removal")
107  public REVLibError setReference(double value, ControlType ctrl) {
108    sparkMax.throwIfClosed();
109    return setReference(value, ctrl, 0);
110  }
111
112  /**
113   * Set the controller reference value based on the selected control mode. This will override the
114   * pre-programmed control mode but not change what is programmed to the controller.
115   *
116   * @param value The value to set depending on the control mode. For basic duty cycle control this
117   *     should be a value between -1 and 1 Otherwise: Voltage Control: Voltage (volts) Velocity
118   *     Control: Velocity (RPM) Position Control: Position (Rotations) Current Control: Current
119   *     (Amps). Native units can be changed using the setPositionConversionFactor() or
120   *     setVelocityConversionFactor() methods of the CANEncoder class
121   * @param ctrl Is the control type to override with
122   * @param pidSlot for this command
123   * @return {@link REVLibError#kOk} if successful
124   */
125  public REVLibError setReference(double value, CANSparkMax.ControlType ctrl, int pidSlot) {
126    sparkMax.throwIfClosed();
127    return setReference(value, ctrl, pidSlot, 0);
128  }
129
130  /**
131   * Set the controller reference value based on the selected control mode. This will override the
132   * pre-programmed control mode but not change what is programmed to the controller.
133   *
134   * @param value The value to set depending on the control mode. For basic duty cycle control this
135   *     should be a value between -1 and 1 Otherwise: Voltage Control: Voltage (volts) Velocity
136   *     Control: Velocity (RPM) Position Control: Position (Rotations) Current Control: Current
137   *     (Amps). Native units can be changed using the setPositionConversionFactor() or
138   *     setVelocityConversionFactor() methods of the CANEncoder class
139   * @param ctrl Is the control type to override with
140   * @param pidSlot for this command
141   * @return {@link REVLibError#kOk} if successful
142   * @deprecated Use {@link #setReference(double, CANSparkMax.ControlType, int)} instead.
143   */
144  @Deprecated(forRemoval = true)
145  @SuppressWarnings("removal")
146  public REVLibError setReference(double value, ControlType ctrl, int pidSlot) {
147    sparkMax.throwIfClosed();
148    return setReference(value, ctrl, pidSlot, 0);
149  }
150
151  /**
152   * Set the controller reference value based on the selected control mode. This will override the
153   * pre-programmed control mode but not change what is programmed to the controller.
154   *
155   * @param value The value to set depending on the control mode. For basic duty cycle control this
156   *     should be a value between -1 and 1 Otherwise: Voltage Control: Voltage (volts) Velocity
157   *     Control: Velocity (RPM) Position Control: Position (Rotations) Current Control: Current
158   *     (Amps). Native units can be changed using the setPositionConversionFactor() or
159   *     setVelocityConversionFactor() methods of the CANEncoder class
160   * @param ctrl Is the control type to override with
161   * @param pidSlot for this command
162   * @param arbFeedforward A value from which is represented in voltage applied to the motor after
163   *     the result of the specified control mode. The units for the parameter is Volts. This value
164   *     is set after the control mode, but before any current limits or ramp rates.
165   * @return {@link REVLibError#kOk} if successful
166   */
167  public REVLibError setReference(
168      double value, CANSparkMax.ControlType ctrl, int pidSlot, double arbFeedforward) {
169    sparkMax.throwIfClosed();
170    return sparkMax.setpointCommand(value, ctrl, pidSlot, arbFeedforward);
171  }
172
173  /**
174   * Set the controller reference value based on the selected control mode. This will override the
175   * pre-programmed control mode but not change what is programmed to the controller.
176   *
177   * @param value The value to set depending on the control mode. For basic duty cycle control this
178   *     should be a value between -1 and 1 Otherwise: Voltage Control: Voltage (volts) Velocity
179   *     Control: Velocity (RPM) Position Control: Position (Rotations) Current Control: Current
180   *     (Amps). Native units can be changed using the setPositionConversionFactor() or
181   *     setVelocityConversionFactor() methods of the CANEncoder class
182   * @param ctrl Is the control type to override with
183   * @param pidSlot for this command
184   * @param arbFeedforward A value from which is represented in voltage applied to the motor after
185   *     the result of the specified control mode. The units for the parameter is Volts. This value
186   *     is set after the control mode, but before any current limits or ramp rates.
187   * @return {@link REVLibError#kOk} if successful
188   * @deprecated Use {@link #setReference(double, CANSparkMax.ControlType, int, double)} instead.
189   */
190  @Deprecated(forRemoval = true)
191  @SuppressWarnings("removal")
192  public REVLibError setReference(
193      double value, ControlType ctrl, int pidSlot, double arbFeedforward) {
194    sparkMax.throwIfClosed();
195    return sparkMax.setpointCommand(value, ctrl, pidSlot, arbFeedforward);
196  }
197
198  /**
199   * Set the controller reference value based on the selected control mode. This will override the
200   * pre-programmed control mode but not change what is programmed to the controller.
201   *
202   * @param value The value to set depending on the control mode. For basic duty cycle control this
203   *     should be a value between -1 and 1 Otherwise: Voltage Control: Voltage (volts) Velocity
204   *     Control: Velocity (RPM) Position Control: Position (Rotations) Current Control: Current
205   *     (Amps). Native units can be changed using the setPositionConversionFactor() or
206   *     setVelocityConversionFactor() methods of the CANEncoder class
207   * @param ctrl Is the control type to override with
208   * @param pidSlot for this command
209   * @param arbFeedforward A value from which is represented in voltage applied to the motor after
210   *     the result of the specified control mode. The units for the parameter is Volts. This value
211   *     is set after the control mode, but before any current limits or ramp rates.
212   * @param arbFFUnits The units the arbitrary feed forward term is in
213   * @return {@link REVLibError#kOk} if successful
214   */
215  public REVLibError setReference(
216      double value,
217      CANSparkMax.ControlType ctrl,
218      int pidSlot,
219      double arbFeedforward,
220      ArbFFUnits arbFFUnits) {
221    sparkMax.throwIfClosed();
222    return sparkMax.setpointCommand(value, ctrl, pidSlot, arbFeedforward, arbFFUnits.value);
223  }
224
225  /**
226   * Set the controller reference value based on the selected control mode. This will override the
227   * pre-programmed control mode but not change what is programmed to the controller.
228   *
229   * @param value The value to set depending on the control mode. For basic duty cycle control this
230   *     should be a value between -1 and 1 Otherwise: Voltage Control: Voltage (volts) Velocity
231   *     Control: Velocity (RPM) Position Control: Position (Rotations) Current Control: Current
232   *     (Amps). Native units can be changed using the setPositionConversionFactor() or
233   *     setVelocityConversionFactor() methods of the CANEncoder class
234   * @param ctrl Is the control type to override with
235   * @param pidSlot for this command
236   * @param arbFeedforward A value from which is represented in voltage applied to the motor after
237   *     the result of the specified control mode. The units for the parameter is Volts. This value
238   *     is set after the control mode, but before any current limits or ramp rates.
239   * @param arbFFUnits The units the arbitrary feed forward term is in
240   * @return {@link REVLibError#kOk} if successful
241   * @deprecated Use {@link #setReference(double, CANSparkMax.ControlType, int, double,
242   *     SparkMaxPIDController.ArbFFUnits)} instead.
243   */
244  @SuppressWarnings("removal")
245  @Deprecated(forRemoval = true)
246  public REVLibError setReference(
247      double value,
248      ControlType ctrl,
249      int pidSlot,
250      double arbFeedforward,
251      CANPIDController.ArbFFUnits arbFFUnits) {
252    sparkMax.throwIfClosed();
253    return sparkMax.setpointCommand(value, ctrl, pidSlot, arbFeedforward, arbFFUnits.value);
254  }
255
256  /**
257   * Set the Proportional Gain constant of the PIDF controller on the SPARK MAX. This uses the Set
258   * Parameter API and should be used infrequently. The parameter does not presist unless
259   * burnFlash() is called. The recommended method to configure this parameter is use to SPARK MAX
260   * GUI to tune and save parameters.
261   *
262   * @param gain The proportional gain value, must be positive
263   * @return {@link REVLibError#kOk} if successful
264   */
265  public REVLibError setP(double gain) {
266    sparkMax.throwIfClosed();
267    return setP(gain, 0);
268  }
269
270  /**
271   * Set the Proportional Gain constant of the PIDF controller on the SPARK MAX. This uses the Set
272   * Parameter API and should be used infrequently. The parameter does not presist unless
273   * burnFlash() is called. The recommended method to configure this parameter is use to SPARK MAX
274   * GUI to tune and save parameters.
275   *
276   * @param gain The proportional gain value, must be positive
277   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
278   *     its own set of gain values and can be changed in each control frame using SetReference().
279   * @return {@link REVLibError#kOk} if successful
280   */
281  public REVLibError setP(double gain, int slotID) {
282    sparkMax.throwIfClosed();
283    return REVLibError.fromInt(
284        CANSparkMaxJNI.c_SparkMax_SetP(sparkMax.sparkMaxHandle, slotID, (float) gain));
285  }
286
287  /**
288   * Set the Integral Gain constant of the PIDF controller on the SPARK MAX. This uses the Set
289   * Parameter API and should be used infrequently. The parameter does not presist unless
290   * burnFlash() is called. The recommended method to configure this parameter is use to SPARK MAX
291   * GUI to tune and save parameters.
292   *
293   * @param gain The integral gain value, must be positive
294   * @return {@link REVLibError#kOk} if successful
295   */
296  public REVLibError setI(double gain) {
297    sparkMax.throwIfClosed();
298    return setI(gain, 0);
299  }
300
301  /**
302   * Set the Integral Gain constant of the PIDF controller on the SPARK MAX. This uses the Set
303   * Parameter API and should be used infrequently. The parameter does not presist unless
304   * burnFlash() is called. The recommended method to configure this parameter is use to SPARK MAX
305   * GUI to tune and save parameters.
306   *
307   * @param gain The integral gain value, must be positive
308   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
309   *     its own set of gain values and can be changed in each control frame using SetReference().
310   * @return {@link REVLibError#kOk} if successful
311   */
312  public REVLibError setI(double gain, int slotID) {
313    sparkMax.throwIfClosed();
314    return REVLibError.fromInt(
315        CANSparkMaxJNI.c_SparkMax_SetI(sparkMax.sparkMaxHandle, slotID, (float) gain));
316  }
317
318  /**
319   * Set the Derivative Gain constant of the PIDF controller on the SPARK MAX. This uses the Set
320   * Parameter API and should be used infrequently. The parameter does not presist unless
321   * burnFlash() is called. The recommended method to configure this parameter is use to SPARK MAX
322   * GUI to tune and save parameters.
323   *
324   * @param gain The derivative gain value, must be positive
325   * @return {@link REVLibError#kOk} if successful
326   */
327  public REVLibError setD(double gain) {
328    sparkMax.throwIfClosed();
329    return setD(gain, 0);
330  }
331
332  /**
333   * Set the Derivative Gain constant of the PIDF controller on the SPARK MAX. This uses the Set
334   * Parameter API and should be used infrequently. The parameter does not presist unless
335   * burnFlash() is called. The recommended method to configure this parameter is use to SPARK MAX
336   * GUI to tune and save parameters.
337   *
338   * @param gain The derivative gain value, must be positive
339   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
340   *     its own set of gain values and can be changed in each control frame using SetReference().
341   * @return {@link REVLibError#kOk} if successful
342   */
343  public REVLibError setD(double gain, int slotID) {
344    sparkMax.throwIfClosed();
345    return REVLibError.fromInt(
346        CANSparkMaxJNI.c_SparkMax_SetD(sparkMax.sparkMaxHandle, slotID, (float) gain));
347  }
348
349  /**
350   * Set the Derivative Filter constant of the PIDF controller on the SPARK MAX. This uses the Set
351   * Parameter API and should be used infrequently. The parameter does not presist unless
352   * burnFlash() is called.
353   *
354   * @param gain The derivative filter value, must be a positive number between 0 and 1
355   * @return {@link REVLibError#kOk} if successful
356   */
357  public REVLibError setDFilter(double gain) {
358    sparkMax.throwIfClosed();
359    return setDFilter(gain, 0);
360  }
361
362  /**
363   * Set the Derivative Filter constant of the PIDF controller on the SPARK MAX. This uses the Set
364   * Parameter API and should be used infrequently. The parameter does not presist unless
365   * burnFlash() is called.
366   *
367   * @param gain The derivative filter value, must be a positive number between 0 and 1
368   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
369   *     its own set of gain values and can be changed in each control frame using SetReference().
370   * @return {@link REVLibError#kOk} if successful
371   */
372  public REVLibError setDFilter(double gain, int slotID) {
373    sparkMax.throwIfClosed();
374    return REVLibError.fromInt(
375        CANSparkMaxJNI.c_SparkMax_SetDFilter(sparkMax.sparkMaxHandle, slotID, (float) gain));
376  }
377
378  /**
379   * Set the Feed-froward Gain constant of the PIDF controller on the SPARK MAX. This uses the Set
380   * Parameter API and should be used infrequently. The parameter does not presist unless
381   * burnFlash() is called. The recommended method to configure this parameter is use to SPARK MAX
382   * GUI to tune and save parameters.
383   *
384   * @param gain The feed-forward gain value
385   * @return {@link REVLibError#kOk} if successful
386   */
387  public REVLibError setFF(double gain) {
388    sparkMax.throwIfClosed();
389    return setFF(gain, 0);
390  }
391
392  /**
393   * Set the Feed-froward Gain constant of the PIDF controller on the SPARK MAX. This uses the Set
394   * Parameter API and should be used infrequently. The parameter does not presist unless
395   * burnFlash() is called. The recommended method to configure this parameter is use to SPARK MAX
396   * GUI to tune and save parameters.
397   *
398   * @param gain The feed-forward gain value
399   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
400   *     its own set of gain values and can be changed in each control frame using SetReference().
401   * @return {@link REVLibError#kOk} if successful
402   */
403  public REVLibError setFF(double gain, int slotID) {
404    sparkMax.throwIfClosed();
405    return REVLibError.fromInt(
406        CANSparkMaxJNI.c_SparkMax_SetFF(sparkMax.sparkMaxHandle, slotID, (float) gain));
407  }
408
409  /**
410   * Set the IZone range of the PIDF controller on the SPARK MAX. This value specifies the range the
411   * |error| must be within for the integral constant to take effect.
412   *
413   * <p>This uses the Set Parameter API and should be used infrequently. The parameter does not
414   * presist unless burnFlash() is called. The recommended method to configure this parameter is to
415   * use the SPARK MAX GUI to tune and save parameters.
416   *
417   * @param IZone The IZone value, must be positive. Set to 0 to disable
418   * @return {@link REVLibError#kOk} if successful
419   */
420  public REVLibError setIZone(double IZone) {
421    sparkMax.throwIfClosed();
422    return setIZone(IZone, 0);
423  }
424
425  /**
426   * Set the IZone range of the PIDF controller on the SPARK MAX. This value specifies the range the
427   * |error| must be within for the integral constant to take effect.
428   *
429   * <p>This uses the Set Parameter API and should be used infrequently. The parameter does not
430   * presist unless burnFlash() is called. The recommended method to configure this parameter is to
431   * use the SPARK MAX GUI to tune and save parameters.
432   *
433   * @param IZone The IZone value, must be positive. Set to 0 to disable
434   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
435   *     its own set of gain values and can be changed in each control frame using SetReference().
436   * @return {@link REVLibError#kOk} if successful
437   */
438  public REVLibError setIZone(double IZone, int slotID) {
439    sparkMax.throwIfClosed();
440    return REVLibError.fromInt(
441        CANSparkMaxJNI.c_SparkMax_SetIZone(sparkMax.sparkMaxHandle, slotID, (float) IZone));
442  }
443
444  /**
445   * Set the min amd max output for the closed loop mode.
446   *
447   * <p>This uses the Set Parameter API and should be used infrequently. The parameter does not
448   * presist unless burnFlash() is called. The recommended method to configure this parameter is to
449   * use the SPARK MAX GUI to tune and save parameters.
450   *
451   * @param min Reverse power minimum to allow the controller to output
452   * @param max Forward power maximum to allow the controller to output
453   * @return {@link REVLibError#kOk} if successful
454   */
455  public REVLibError setOutputRange(double min, double max) {
456    sparkMax.throwIfClosed();
457    return setOutputRange(min, max, 0);
458  }
459
460  /**
461   * Set the min amd max output for the closed loop mode.
462   *
463   * <p>This uses the Set Parameter API and should be used infrequently. The parameter does not
464   * presist unless burnFlash() is called. The recommended method to configure this parameter is to
465   * use the SPARK MAX GUI to tune and save parameters.
466   *
467   * @param min Reverse power minimum to allow the controller to output
468   * @param max Forward power maximum to allow the controller to output
469   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
470   *     its own set of gain values and can be changed in each control frame using SetReference().
471   * @return {@link REVLibError#kOk} if successful
472   */
473  public REVLibError setOutputRange(double min, double max, int slotID) {
474    sparkMax.throwIfClosed();
475    return REVLibError.fromInt(
476        CANSparkMaxJNI.c_SparkMax_SetOutputRange(
477            sparkMax.sparkMaxHandle, slotID, (float) min, (float) max));
478  }
479
480  /**
481   * Get the Proportional Gain constant of the PIDF controller on the SPARK MAX.
482   *
483   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
484   * non-blocking call and will return a cached value if the parameter is not returned by the
485   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
486   *
487   * @return double P Gain value
488   */
489  public double getP() {
490    sparkMax.throwIfClosed();
491    return getP(0);
492  }
493
494  /**
495   * Get the Proportional Gain constant of the PIDF controller on the SPARK MAX.
496   *
497   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
498   * non-blocking call and will return a cached value if the parameter is not returned by the
499   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
500   *
501   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
502   *     its own set of gain values and can be changed in each control frame using SetReference().
503   * @return double P Gain value
504   */
505  public double getP(int slotID) {
506    sparkMax.throwIfClosed();
507    return CANSparkMaxJNI.c_SparkMax_GetP(sparkMax.sparkMaxHandle, slotID);
508  }
509
510  /**
511   * Get the Integral Gain constant of the PIDF controller on the SPARK MAX.
512   *
513   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
514   * non-blocking call and will return a cached value if the parameter is not returned by the
515   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
516   *
517   * @return double I Gain value
518   */
519  public double getI() {
520    sparkMax.throwIfClosed();
521    return getI(0);
522  }
523
524  /**
525   * Get the Integral Gain constant of the PIDF controller on the SPARK MAX.
526   *
527   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
528   * non-blocking call and will return a cached value if the parameter is not returned by the
529   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
530   *
531   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
532   *     its own set of gain values and can be changed in each control frame using SetReference().
533   * @return double I Gain value
534   */
535  public double getI(int slotID) {
536    sparkMax.throwIfClosed();
537    return CANSparkMaxJNI.c_SparkMax_GetI(sparkMax.sparkMaxHandle, slotID);
538  }
539
540  /**
541   * Get the Derivative Gain constant of the PIDF controller on the SPARK MAX.
542   *
543   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
544   * non-blocking call and will return a cached value if the parameter is not returned by the
545   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
546   *
547   * @return double D Gain value
548   */
549  public double getD() {
550    sparkMax.throwIfClosed();
551    return getD(0);
552  }
553
554  /**
555   * Get the Derivative Gain constant of the PIDF controller on the SPARK MAX.
556   *
557   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
558   * non-blocking call and will return a cached value if the parameter is not returned by the
559   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
560   *
561   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
562   *     its own set of gain values and can be changed in each control frame using SetReference().
563   * @return double D Gain value
564   */
565  public double getD(int slotID) {
566    sparkMax.throwIfClosed();
567    return CANSparkMaxJNI.c_SparkMax_GetD(sparkMax.sparkMaxHandle, slotID);
568  }
569
570  /**
571   * Get the Derivative Filter constant of the PIDF controller on the SPARK MAX.
572   *
573   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
574   * non-blocking call and will return a cached value if the parameter is not returned by the
575   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
576   *
577   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
578   *     its own set of gain values and can be changed in each control frame using SetReference().
579   * @return double D Filter value
580   */
581  public double getDFilter(int slotID) {
582    sparkMax.throwIfClosed();
583    return CANSparkMaxJNI.c_SparkMax_GetDFilter(sparkMax.sparkMaxHandle, slotID);
584  }
585
586  /**
587   * Get the Feed-forward Gain constant of the PIDF controller on the SPARK MAX.
588   *
589   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
590   * non-blocking call and will return a cached value if the parameter is not returned by the
591   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
592   *
593   * @return double F Gain value
594   */
595  public double getFF() {
596    sparkMax.throwIfClosed();
597    return getFF(0);
598  }
599
600  /**
601   * Get the Feed-forward Gain constant of the PIDF controller on the SPARK MAX.
602   *
603   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
604   * non-blocking call and will return a cached value if the parameter is not returned by the
605   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
606   *
607   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
608   *     its own set of gain values and can be changed in each control frame using SetReference().
609   * @return double F Gain value
610   */
611  public double getFF(int slotID) {
612    sparkMax.throwIfClosed();
613    return CANSparkMaxJNI.c_SparkMax_GetFF(sparkMax.sparkMaxHandle, slotID);
614  }
615
616  /**
617   * Get the IZone constant of the PIDF controller on the SPARK MAX.
618   *
619   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
620   * non-blocking call and will return a cached value if the parameter is not returned by the
621   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
622   *
623   * @return double IZone value
624   */
625  public double getIZone() {
626    sparkMax.throwIfClosed();
627    return getIZone(0);
628  }
629
630  /**
631   * Get the IZone constant of the PIDF controller on the SPARK MAX.
632   *
633   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
634   * non-blocking call and will return a cached value if the parameter is not returned by the
635   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
636   *
637   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
638   *     its own set of gain values and can be changed in each control frame using SetReference().
639   * @return double IZone value
640   */
641  public double getIZone(int slotID) {
642    sparkMax.throwIfClosed();
643    return CANSparkMaxJNI.c_SparkMax_GetIZone(sparkMax.sparkMaxHandle, slotID);
644  }
645
646  /**
647   * Get the derivative filter constant of the PIDF controller on the SPARK MAX.
648   *
649   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
650   * non-blocking call and will return a cached value if the parameter is not returned by the
651   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
652   *
653   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
654   *     its own set of gain values and can be changed in each control frame using SetReference().
655   * @return double D Filter
656   */
657  // public double getDFilter(int slotID = 0);
658
659  /**
660   * Get the min output of the PIDF controller on the SPARK MAX.
661   *
662   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
663   * non-blocking call and will return a cached value if the parameter is not returned by the
664   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
665   *
666   * @return double min value
667   */
668  public double getOutputMin() {
669    sparkMax.throwIfClosed();
670    return getOutputMin(0);
671  }
672
673  /**
674   * Get the min output of the PIDF controller on the SPARK MAX.
675   *
676   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
677   * non-blocking call and will return a cached value if the parameter is not returned by the
678   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
679   *
680   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
681   *     its own set of gain values and can be changed in each control frame using SetReference().
682   * @return double min value
683   */
684  public double getOutputMin(int slotID) {
685    sparkMax.throwIfClosed();
686    return CANSparkMaxJNI.c_SparkMax_GetOutputMin(sparkMax.sparkMaxHandle, slotID);
687  }
688
689  /**
690   * Get the max output of the PIDF controller on the SPARK MAX.
691   *
692   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
693   * non-blocking call and will return a cached value if the parameter is not returned by the
694   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
695   *
696   * @return double max value
697   */
698  public double getOutputMax() {
699    sparkMax.throwIfClosed();
700    return getOutputMax(0);
701  }
702
703  /**
704   * Get the max output of the PIDF controller on the SPARK MAX.
705   *
706   * <p>This uses the Get Parameter API and should be used infrequently. This function uses a
707   * non-blocking call and will return a cached value if the parameter is not returned by the
708   * timeout. The timeout can be changed by calling SetCANTimeout(int milliseconds)
709   *
710   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
711   *     its own set of gain values and can be changed in each control frame using SetReference().
712   * @return double max value
713   */
714  public double getOutputMax(int slotID) {
715    sparkMax.throwIfClosed();
716    return CANSparkMaxJNI.c_SparkMax_GetOutputMax(sparkMax.sparkMaxHandle, slotID);
717  }
718
719  /**
720   * Configure the maximum velocity of the SmartMotion mode. This is the velocity that is reached in
721   * the middle of the profile and is what the motor should spend most of its time at
722   *
723   * @param maxVel The maxmimum cruise velocity for the motion profile in RPM
724   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
725   *     its own set of gain values and can be changed in each control frame using SetReference().
726   * @return {@link REVLibError#kOk} if successful
727   */
728  public REVLibError setSmartMotionMaxVelocity(double maxVel, int slotID) {
729    sparkMax.throwIfClosed();
730    return REVLibError.fromInt(
731        CANSparkMaxJNI.c_SparkMax_SetSmartMotionMaxVelocity(
732            sparkMax.sparkMaxHandle, slotID, (float) maxVel));
733  }
734
735  /**
736   * Configure the maximum acceleration of the SmartMotion mode. This is the accleration that the
737   * motor velocity will increase at until the max velocity is reached
738   *
739   * @param maxAccel The maxmimum acceleration for the motion profile in RPM per second
740   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
741   *     its own set of gain values and can be changed in each control frame using SetReference().
742   * @return {@link REVLibError#kOk} if successful
743   */
744  public REVLibError setSmartMotionMaxAccel(double maxAccel, int slotID) {
745    sparkMax.throwIfClosed();
746    return REVLibError.fromInt(
747        CANSparkMaxJNI.c_SparkMax_SetSmartMotionMaxAccel(
748            sparkMax.sparkMaxHandle, slotID, (float) maxAccel));
749  }
750
751  /**
752   * Configure the mimimum velocity of the SmartMotion mode. Any requested velocities below this
753   * value will be set to 0.
754   *
755   * @param minVel The minimum velocity for the motion profile in RPM
756   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
757   *     its own set of gain values and can be changed in each control frame using SetReference().
758   * @return {@link REVLibError#kOk} if successful
759   */
760  public REVLibError setSmartMotionMinOutputVelocity(double minVel, int slotID) {
761    sparkMax.throwIfClosed();
762    return REVLibError.fromInt(
763        CANSparkMaxJNI.c_SparkMax_SetSmartMotionMinOutputVelocity(
764            sparkMax.sparkMaxHandle, slotID, (float) minVel));
765  }
766
767  /**
768   * Configure the allowed closed loop error of SmartMotion mode. This value is how much deviation
769   * from your setpoint is tolerated and is useful in preventing oscillation around your setpoint.
770   *
771   * @param allowedErr The allowed deviation for your setpoint vs actual position in rotations
772   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
773   *     its own set of gain values and can be changed in each control frame using SetReference().
774   * @return {@link REVLibError#kOk} if successful
775   */
776  public REVLibError setSmartMotionAllowedClosedLoopError(double allowedErr, int slotID) {
777    sparkMax.throwIfClosed();
778    return REVLibError.fromInt(
779        CANSparkMaxJNI.c_SparkMax_SetSmartMotionAllowedClosedLoopError(
780            sparkMax.sparkMaxHandle, slotID, (float) allowedErr));
781  }
782
783  /**
784   * NOTE: As of the 2022 FRC season, the firmware only supports the trapezoidal motion profiling
785   * acceleration strategy.
786   *
787   * <p>Configure the acceleration strategy used to control acceleration on the motor.
788   *
789   * @param accelStrategy The acceleration strategy to use for the automatically generated motion
790   *     profile
791   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
792   *     its own set of gain values and can be changed in each control frame using SetReference().
793   * @return {@link REVLibError#kOk} if successful
794   */
795  public REVLibError setSmartMotionAccelStrategy(AccelStrategy accelStrategy, int slotID) {
796    sparkMax.throwIfClosed();
797    return REVLibError.fromInt(
798        CANSparkMaxJNI.c_SparkMax_SetSmartMotionAccelStrategy(
799            sparkMax.sparkMaxHandle, slotID, accelStrategy.value));
800  }
801
802  /**
803   * NOTE: As of the 2022 FRC season, the firmware only supports the trapezoidal motion profiling
804   * acceleration strategy.
805   *
806   * <p>Configure the acceleration strategy used to control acceleration on the motor. The current
807   * strategy is trapezoidal motion profiling.
808   *
809   * @param accelStrategy The acceleration strategy to use for the automatically generated motion
810   *     profile
811   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
812   *     its own set of gain values and can be changed in each control frame using SetReference().
813   * @return {@link REVLibError#kOk} if successful
814   * @deprecated Use {@link #setSmartMotionAccelStrategy(AccelStrategy, int)} instead.
815   */
816  @SuppressWarnings("removal")
817  @Deprecated(forRemoval = true)
818  public REVLibError setSmartMotionAccelStrategy(
819      CANPIDController.AccelStrategy accelStrategy, int slotID) {
820    sparkMax.throwIfClosed();
821    return REVLibError.fromInt(
822        CANSparkMaxJNI.c_SparkMax_SetSmartMotionAccelStrategy(
823            sparkMax.sparkMaxHandle, slotID, accelStrategy.value));
824  }
825
826  /**
827   * Get the maximum velocity of the SmartMotion mode. This is the velocity that is reached in the
828   * middle of the profile and is what the motor should spend most of its time at
829   *
830   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
831   *     its own set of gain values and can be changed in each control frame using SetReference().
832   * @return The maxmimum cruise velocity for the motion profile in RPM
833   */
834  public double getSmartMotionMaxVelocity(int slotID) {
835    sparkMax.throwIfClosed();
836    return CANSparkMaxJNI.c_SparkMax_GetSmartMotionMaxVelocity(sparkMax.sparkMaxHandle, slotID);
837  }
838
839  /**
840   * Get the maximum acceleration of the SmartMotion mode. This is the accleration that the motor
841   * velocity will increase at until the max velocity is reached
842   *
843   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
844   *     its own set of gain values and can be changed in each control frame using SetReference().
845   * @return The maxmimum acceleration for the motion profile in RPM per second
846   */
847  public double getSmartMotionMaxAccel(int slotID) {
848    sparkMax.throwIfClosed();
849    return CANSparkMaxJNI.c_SparkMax_GetSmartMotionMaxAccel(sparkMax.sparkMaxHandle, slotID);
850  }
851
852  /**
853   * Get the mimimum velocity of the SmartMotion mode. Any requested velocities below this value
854   * will be set to 0.
855   *
856   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
857   *     its own set of gain values and can be changed in each control frame using SetReference().
858   * @return The minimum velocity for the motion profile in RPM
859   */
860  public double getSmartMotionMinOutputVelocity(int slotID) {
861    sparkMax.throwIfClosed();
862    return CANSparkMaxJNI.c_SparkMax_GetSmartMotionMinOutputVelocity(
863        sparkMax.sparkMaxHandle, slotID);
864  }
865
866  /**
867   * Get the allowed closed loop error of SmartMotion mode. This value is how much deviation from
868   * your setpoint is tolerated and is useful in preventing oscillation around your setpoint.
869   *
870   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
871   *     its own set of gain values and can be changed in each control frame using SetReference().
872   * @return The allowed deviation for your setpoint vs actual position in rotations
873   */
874  public double getSmartMotionAllowedClosedLoopError(int slotID) {
875    sparkMax.throwIfClosed();
876    return CANSparkMaxJNI.c_SparkMax_GetSmartMotionAllowedClosedLoopError(
877        sparkMax.sparkMaxHandle, slotID);
878  }
879
880  /**
881   * Get the acceleration strategy used to control acceleration on the motor. As of the 2022 FRC
882   * season, the strategy is always trapezoidal motion profiling, regardless of what the device may
883   * report.
884   *
885   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
886   *     its own set of gain values and can be changed in each control frame using SetReference().
887   * @return The acceleration strategy to use for the automatically generated motion profile.
888   */
889  public AccelStrategy getSmartMotionAccelStrategy(int slotID) {
890    sparkMax.throwIfClosed();
891    return AccelStrategy.fromInt(
892        CANSparkMaxJNI.c_SparkMax_GetSmartMotionAccelStrategy(sparkMax.sparkMaxHandle, slotID));
893  }
894
895  /**
896   * Configure the maximum I accumulator of the PID controller. This value is used to constrain the
897   * I accumulator to help manage integral wind-up
898   *
899   * @param iMaxAccum The max value to contrain the I accumulator to
900   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
901   *     its own set of gain values and can be changed in each control frame using SetReference().
902   * @return {@link REVLibError#kOk} if successful
903   */
904  public REVLibError setIMaxAccum(double iMaxAccum, int slotID) {
905    sparkMax.throwIfClosed();
906    return REVLibError.fromInt(
907        CANSparkMaxJNI.c_SparkMax_SetIMaxAccum(sparkMax.sparkMaxHandle, slotID, (float) iMaxAccum));
908  }
909
910  /**
911   * Get the maximum I accumulator of the PID controller. This value is used to constrain the I
912   * accumulator to help manage integral wind-up
913   *
914   * @param slotID Is the gain schedule slot, the value is a number between 0 and 3. Each slot has
915   *     its own set of gain values and can be changed in each control frame using SetReference().
916   * @return The max value to contrain the I accumulator to
917   */
918  public double getIMaxAccum(int slotID) {
919    sparkMax.throwIfClosed();
920    return (double) CANSparkMaxJNI.c_SparkMax_GetIMaxAccum(sparkMax.sparkMaxHandle, slotID);
921  }
922
923  /**
924   * Set the I accumulator of the PID controller. This is useful when wishing to force a reset on
925   * the I accumulator of the PID controller. You can also preset values to see how it will respond
926   * to certain I characteristics
927   *
928   * <p>To use this function, the controller must be in a closed loop control mode by calling
929   * setReference()
930   *
931   * @param iAccum The value to set the I accumulator to
932   * @return {@link REVLibError#kOk} if successful
933   */
934  public REVLibError setIAccum(double iAccum) {
935    sparkMax.throwIfClosed();
936    return REVLibError.fromInt(
937        CANSparkMaxJNI.c_SparkMax_SetIAccum(sparkMax.sparkMaxHandle, (float) iAccum));
938  }
939
940  /**
941   * Get the I accumulator of the PID controller. This is useful when wishing to see what the I
942   * accumulator value is to help with PID tuning
943   *
944   * @return The value of the I accumulator
945   */
946  public double getIAccum() {
947    sparkMax.throwIfClosed();
948    return CANSparkMaxJNI.c_SparkMax_GetIAccum(sparkMax.sparkMaxHandle);
949  }
950
951  /**
952   * Set the controller's feedback device
953   *
954   * <p>The default feedback device in brushless mode is assumed to be the integrated encoder and
955   * the default feedback device in brushed mode is assumed to be a quadrature encoder. This is used
956   * to changed to another feedback device for the controller, such as an analog sensor.
957   *
958   * <p>If there is a limited range on the feedback sensor that should be observed by the
959   * PIDController, it can be set by calling SetFeedbackSensorRange() on the sensor object.
960   *
961   * @param sensor The sensor to use as a feedback device
962   * @return {@link REVLibError#kOk} if successful
963   */
964  // TODO(Noah): Figure out what's going on with setFeedbackSensorRange()
965  public REVLibError setFeedbackDevice(final MotorFeedbackSensor sensor) {
966    //noinspection removal
967    return setFeedbackDevice((CANSensor) sensor);
968  }
969
970  /** @deprecated Use {@link #setFeedbackDevice(MotorFeedbackSensor)} instead */
971  @SuppressWarnings("removal")
972  @Deprecated(forRemoval = true)
973  public REVLibError setFeedbackDevice(final CANSensor sensor) {
974    sparkMax.throwIfClosed();
975
976    if (sensor instanceof SparkMaxRelativeEncoder
977        || sensor instanceof SparkMaxAlternateEncoder
978        || sensor instanceof SparkMaxAnalogSensor) {
979      // Handle sensors that are directly connected to the SPARK Max
980      CANSparkMax sparkMaxSensorIsConnectedTo;
981      int feedbackDeviceId;
982
983      if (sensor instanceof SparkMaxRelativeEncoder) {
984        sparkMaxSensorIsConnectedTo = ((SparkMaxRelativeEncoder) sensor).sparkMax;
985        feedbackDeviceId = ((SparkMaxRelativeEncoder) sensor).getSparkMaxFeedbackDeviceId();
986      } else if (sensor instanceof SparkMaxAlternateEncoder) {
987        sparkMaxSensorIsConnectedTo = ((SparkMaxAlternateEncoder) sensor).sparkMax;
988        feedbackDeviceId = ((SparkMaxAlternateEncoder) sensor).getSparkMaxFeedbackDeviceId();
989      } else {
990        sparkMaxSensorIsConnectedTo = ((SparkMaxAnalogSensor) sensor).sparkMax;
991        feedbackDeviceId = ((SparkMaxAnalogSensor) sensor).getSparkMaxFeedbackDeviceId();
992      }
993
994      if (sparkMaxSensorIsConnectedTo != this.sparkMax) {
995        throw new IllegalArgumentException(
996            "A sensor attached to one SPARK MAX cannot be used as a feedback device for a different SPARK MAX");
997      }
998
999      return REVLibError.fromInt(
1000          CANSparkMaxJNI.c_SparkMax_SetFeedbackDevice(sparkMax.sparkMaxHandle, feedbackDeviceId));
1001
1002    } else {
1003      // Right now, the SPARK MAX does not support sensors that are not directly connected to itself
1004      throw new IllegalArgumentException(
1005          sensor.getClass().getSimpleName()
1006              + " cannot be used as a feedback device for a SPARK MAX at this time");
1007    }
1008  }
1009}