001/*
002 * Copyright (C) Cross The Road Electronics.  All rights reserved.
003 * License information can be found in CTRE_LICENSE.txt
004 * For support and suggestions contact support@ctr-electronics.com or file
005 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
006 */
007package com.ctre.phoenix6.controls;
008
009import com.ctre.phoenix6.StatusCode;
010import com.ctre.phoenix6.controls.jni.ControlJNI;
011import com.ctre.phoenix6.hardware.traits.*;
012
013import edu.wpi.first.units.*;
014import edu.wpi.first.units.measure.*;
015import static edu.wpi.first.units.Units.*;
016
017import java.util.HashMap;
018import java.util.Map;
019
020/**
021 * Request PID to target position with voltage feedforward
022 * <p>
023 * This control mode will set the motor's position setpoint to the position specified by the user. In
024 * addition, it will apply an additional voltage as an arbitrary feedforward value.
025 */
026public class PositionVoltage extends ControlRequest implements Cloneable
027{
028    /**
029     * Position to drive toward in rotations.
030     * 
031     * <ul>
032     *   <li> Units: rotations
033     * </ul>
034     * 
035     */
036    public double Position;
037    /**
038     * Velocity to drive toward in rotations per second. This is typically used for
039     * motion profiles generated by the robot program.
040     * 
041     * <ul>
042     *   <li> Units: rotations per second
043     * </ul>
044     * 
045     */
046    public double Velocity = 0.0;
047    /**
048     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
049     * peak power by ~15% on supported devices (see {@link SupportsFOC}). Set to
050     * false to use trapezoidal commutation.
051     * <p>
052     * FOC improves motor performance by leveraging torque (current) control. 
053     * However, this may be inconvenient for applications that require specifying
054     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
055     * combines the performances gains of FOC while still allowing applications to
056     * provide duty cycle or voltage demand.  This not to be confused with simple
057     * sinusoidal control or phase voltage control which lacks the performance
058     * gains.
059     */
060    public boolean EnableFOC = true;
061    /**
062     * Feedforward to apply in volts
063     * 
064     * <ul>
065     *   <li> Units: Volts
066     * </ul>
067     * 
068     */
069    public double FeedForward = 0.0;
070    /**
071     * Select which gains are applied by selecting the slot.  Use the configuration
072     * api to set the gain values for the selected slot before enabling this
073     * feature. Slot must be within [0,2].
074     */
075    public int Slot = 0;
076    /**
077     * Set to true to static-brake the rotor when output is zero (or within
078     * deadband).  Set to false to use the NeutralMode configuration setting
079     * (default). This flag exists to provide the fundamental behavior of this
080     * control when output is zero, which is to provide 0V to the motor.
081     */
082    public boolean OverrideBrakeDurNeutral = false;
083    /**
084     * Set to true to force forward limiting.  This allows users to use other limit
085     * switch sensors connected to robot controller.  This also allows use of active
086     * sensors that require external power.
087     */
088    public boolean LimitForwardMotion = false;
089    /**
090     * Set to true to force reverse limiting.  This allows users to use other limit
091     * switch sensors connected to robot controller.  This also allows use of active
092     * sensors that require external power.
093     */
094    public boolean LimitReverseMotion = false;
095    /**
096     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
097     * LimitReverseMotion parameters, instead allowing motion.
098     * <p>
099     * This can be useful on mechanisms such as an intake/feeder, where a limit
100     * switch stops motion while intaking but should be ignored when feeding to a
101     * shooter.
102     * <p>
103     * The hardware limit faults and Forward/ReverseLimit signals will still report
104     * the values of the limit switches regardless of this parameter.
105     */
106    public boolean IgnoreHardwareLimits = false;
107    /**
108     * Set to true to delay applying this control request until a timesync boundary
109     * (requires Phoenix Pro and CANivore). This eliminates the impact of
110     * nondeterministic network delays in exchange for a larger but deterministic
111     * control latency.
112     * <p>
113     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
114     * Additionally, when this is enabled, the UpdateFreqHz of this request should
115     * be set to 0 Hz.
116     */
117    public boolean UseTimesync = false;
118
119    /**
120     * The period at which this control will update at.
121     * This is designated in Hertz, with a minimum of 20 Hz
122     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
123     * <p>
124     * If this field is set to 0 Hz, the control request will
125     * be sent immediately as a one-shot frame. This may be useful
126     * for advanced applications that require outputs to be
127     * synchronized with data acquisition. In this case, we
128     * recommend not exceeding 50 ms between control calls.
129     */
130    public double UpdateFreqHz = 100;
131
132    /**
133     * Request PID to target position with voltage feedforward
134     * <p>
135     * This control mode will set the motor's position setpoint to the position
136     * specified by the user. In addition, it will apply an additional voltage as an
137     * arbitrary feedforward value.
138     * 
139     * @param Position    Position to drive toward in rotations.
140     */
141    public PositionVoltage(double Position)
142    {
143        super("PositionVoltage");
144        this.Position = Position;
145    }
146
147    /**
148     * Request PID to target position with voltage feedforward
149     * <p>
150     * This control mode will set the motor's position setpoint to the position
151     * specified by the user. In addition, it will apply an additional voltage as an
152     * arbitrary feedforward value.
153     * 
154     * @param Position    Position to drive toward in rotations.
155     */
156    public PositionVoltage(Angle Position)
157    {
158        this(Position.in(Rotations));
159    }
160
161    @Override
162    public String toString()
163    {
164        String ss = "Control: PositionVoltage\n";
165        ss += "    Position: " + Position + " rotations" + "\n";
166        ss += "    Velocity: " + Velocity + " rotations per second" + "\n";
167        ss += "    EnableFOC: " + EnableFOC + "\n";
168        ss += "    FeedForward: " + FeedForward + " Volts" + "\n";
169        ss += "    Slot: " + Slot + "\n";
170        ss += "    OverrideBrakeDurNeutral: " + OverrideBrakeDurNeutral + "\n";
171        ss += "    LimitForwardMotion: " + LimitForwardMotion + "\n";
172        ss += "    LimitReverseMotion: " + LimitReverseMotion + "\n";
173        ss += "    IgnoreHardwareLimits: " + IgnoreHardwareLimits + "\n";
174        ss += "    UseTimesync: " + UseTimesync + "\n";
175        return ss;
176    }
177
178    @Override
179    public StatusCode sendRequest(String network, int deviceHash)
180    {
181        return StatusCode.valueOf(ControlJNI.JNI_RequestControlPositionVoltage(
182                network, deviceHash, UpdateFreqHz, Position, Velocity, EnableFOC, FeedForward, Slot, OverrideBrakeDurNeutral, LimitForwardMotion, LimitReverseMotion, IgnoreHardwareLimits, UseTimesync));
183    }
184
185    /**
186     * Gets information about this control request.
187     *
188     * @return Map of control parameter names and corresponding applied values
189     */
190    @Override
191    public Map<String, String> getControlInfo()
192    {
193        var controlInfo = new HashMap<String, String>();
194        controlInfo.put("Name", getName());
195        controlInfo.put("Position", String.valueOf(this.Position));
196        controlInfo.put("Velocity", String.valueOf(this.Velocity));
197        controlInfo.put("EnableFOC", String.valueOf(this.EnableFOC));
198        controlInfo.put("FeedForward", String.valueOf(this.FeedForward));
199        controlInfo.put("Slot", String.valueOf(this.Slot));
200        controlInfo.put("OverrideBrakeDurNeutral", String.valueOf(this.OverrideBrakeDurNeutral));
201        controlInfo.put("LimitForwardMotion", String.valueOf(this.LimitForwardMotion));
202        controlInfo.put("LimitReverseMotion", String.valueOf(this.LimitReverseMotion));
203        controlInfo.put("IgnoreHardwareLimits", String.valueOf(this.IgnoreHardwareLimits));
204        controlInfo.put("UseTimesync", String.valueOf(this.UseTimesync));
205        return controlInfo;
206    }
207    
208    /**
209     * Modifies this Control Request's Position parameter and returns itself for
210     * method-chaining and easier to use request API.
211     * <p>
212     * Position to drive toward in rotations.
213     * 
214     * <ul>
215     *   <li> Units: rotations
216     * </ul>
217     * 
218     *
219     * @param newPosition Parameter to modify
220     * @return Itself
221     */
222    public PositionVoltage withPosition(double newPosition)
223    {
224        Position = newPosition;
225        return this;
226    }
227    
228    /**
229     * Modifies this Control Request's Position parameter and returns itself for
230     * method-chaining and easier to use request API.
231     * <p>
232     * Position to drive toward in rotations.
233     * 
234     * <ul>
235     *   <li> Units: rotations
236     * </ul>
237     * 
238     *
239     * @param newPosition Parameter to modify
240     * @return Itself
241     */
242    public PositionVoltage withPosition(Angle newPosition)
243    {
244        Position = newPosition.in(Rotations);
245        return this;
246    }
247    
248    /**
249     * Helper method to get this Control Request's Position parameter converted
250     * to a unit type. If not using the Java units library, {@link #Position}
251     * can be accessed directly instead.
252     * <p>
253     * Position to drive toward in rotations.
254     * 
255     * <ul>
256     *   <li> Units: rotations
257     * </ul>
258     * 
259     *
260     * @return Position
261     */
262    public Angle getPositionMeasure()
263    {
264        return Rotations.of(Position);
265    }
266    
267    /**
268     * Modifies this Control Request's Velocity parameter and returns itself for
269     * method-chaining and easier to use request API.
270     * <p>
271     * Velocity to drive toward in rotations per second. This is typically used for
272     * motion profiles generated by the robot program.
273     * 
274     * <ul>
275     *   <li> Units: rotations per second
276     * </ul>
277     * 
278     *
279     * @param newVelocity Parameter to modify
280     * @return Itself
281     */
282    public PositionVoltage withVelocity(double newVelocity)
283    {
284        Velocity = newVelocity;
285        return this;
286    }
287    
288    /**
289     * Modifies this Control Request's Velocity parameter and returns itself for
290     * method-chaining and easier to use request API.
291     * <p>
292     * Velocity to drive toward in rotations per second. This is typically used for
293     * motion profiles generated by the robot program.
294     * 
295     * <ul>
296     *   <li> Units: rotations per second
297     * </ul>
298     * 
299     *
300     * @param newVelocity Parameter to modify
301     * @return Itself
302     */
303    public PositionVoltage withVelocity(AngularVelocity newVelocity)
304    {
305        Velocity = newVelocity.in(RotationsPerSecond);
306        return this;
307    }
308    
309    /**
310     * Helper method to get this Control Request's Velocity parameter converted
311     * to a unit type. If not using the Java units library, {@link #Velocity}
312     * can be accessed directly instead.
313     * <p>
314     * Velocity to drive toward in rotations per second. This is typically used for
315     * motion profiles generated by the robot program.
316     * 
317     * <ul>
318     *   <li> Units: rotations per second
319     * </ul>
320     * 
321     *
322     * @return Velocity
323     */
324    public AngularVelocity getVelocityMeasure()
325    {
326        return RotationsPerSecond.of(Velocity);
327    }
328    
329    /**
330     * Modifies this Control Request's EnableFOC parameter and returns itself for
331     * method-chaining and easier to use request API.
332     * <p>
333     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
334     * peak power by ~15% on supported devices (see {@link SupportsFOC}). Set to
335     * false to use trapezoidal commutation.
336     * <p>
337     * FOC improves motor performance by leveraging torque (current) control. 
338     * However, this may be inconvenient for applications that require specifying
339     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
340     * combines the performances gains of FOC while still allowing applications to
341     * provide duty cycle or voltage demand.  This not to be confused with simple
342     * sinusoidal control or phase voltage control which lacks the performance
343     * gains.
344     *
345     * @param newEnableFOC Parameter to modify
346     * @return Itself
347     */
348    public PositionVoltage withEnableFOC(boolean newEnableFOC)
349    {
350        EnableFOC = newEnableFOC;
351        return this;
352    }
353    
354    /**
355     * Modifies this Control Request's FeedForward parameter and returns itself for
356     * method-chaining and easier to use request API.
357     * <p>
358     * Feedforward to apply in volts
359     * 
360     * <ul>
361     *   <li> Units: Volts
362     * </ul>
363     * 
364     *
365     * @param newFeedForward Parameter to modify
366     * @return Itself
367     */
368    public PositionVoltage withFeedForward(double newFeedForward)
369    {
370        FeedForward = newFeedForward;
371        return this;
372    }
373    
374    /**
375     * Modifies this Control Request's FeedForward parameter and returns itself for
376     * method-chaining and easier to use request API.
377     * <p>
378     * Feedforward to apply in volts
379     * 
380     * <ul>
381     *   <li> Units: Volts
382     * </ul>
383     * 
384     *
385     * @param newFeedForward Parameter to modify
386     * @return Itself
387     */
388    public PositionVoltage withFeedForward(Voltage newFeedForward)
389    {
390        FeedForward = newFeedForward.in(Volts);
391        return this;
392    }
393    
394    /**
395     * Helper method to get this Control Request's FeedForward parameter converted
396     * to a unit type. If not using the Java units library, {@link #FeedForward}
397     * can be accessed directly instead.
398     * <p>
399     * Feedforward to apply in volts
400     * 
401     * <ul>
402     *   <li> Units: Volts
403     * </ul>
404     * 
405     *
406     * @return FeedForward
407     */
408    public Voltage getFeedForwardMeasure()
409    {
410        return Volts.of(FeedForward);
411    }
412    
413    /**
414     * Modifies this Control Request's Slot parameter and returns itself for
415     * method-chaining and easier to use request API.
416     * <p>
417     * Select which gains are applied by selecting the slot.  Use the configuration
418     * api to set the gain values for the selected slot before enabling this
419     * feature. Slot must be within [0,2].
420     *
421     * @param newSlot Parameter to modify
422     * @return Itself
423     */
424    public PositionVoltage withSlot(int newSlot)
425    {
426        Slot = newSlot;
427        return this;
428    }
429    
430    /**
431     * Modifies this Control Request's OverrideBrakeDurNeutral parameter and returns itself for
432     * method-chaining and easier to use request API.
433     * <p>
434     * Set to true to static-brake the rotor when output is zero (or within
435     * deadband).  Set to false to use the NeutralMode configuration setting
436     * (default). This flag exists to provide the fundamental behavior of this
437     * control when output is zero, which is to provide 0V to the motor.
438     *
439     * @param newOverrideBrakeDurNeutral Parameter to modify
440     * @return Itself
441     */
442    public PositionVoltage withOverrideBrakeDurNeutral(boolean newOverrideBrakeDurNeutral)
443    {
444        OverrideBrakeDurNeutral = newOverrideBrakeDurNeutral;
445        return this;
446    }
447    
448    /**
449     * Modifies this Control Request's LimitForwardMotion parameter and returns itself for
450     * method-chaining and easier to use request API.
451     * <p>
452     * Set to true to force forward limiting.  This allows users to use other limit
453     * switch sensors connected to robot controller.  This also allows use of active
454     * sensors that require external power.
455     *
456     * @param newLimitForwardMotion Parameter to modify
457     * @return Itself
458     */
459    public PositionVoltage withLimitForwardMotion(boolean newLimitForwardMotion)
460    {
461        LimitForwardMotion = newLimitForwardMotion;
462        return this;
463    }
464    
465    /**
466     * Modifies this Control Request's LimitReverseMotion parameter and returns itself for
467     * method-chaining and easier to use request API.
468     * <p>
469     * Set to true to force reverse limiting.  This allows users to use other limit
470     * switch sensors connected to robot controller.  This also allows use of active
471     * sensors that require external power.
472     *
473     * @param newLimitReverseMotion Parameter to modify
474     * @return Itself
475     */
476    public PositionVoltage withLimitReverseMotion(boolean newLimitReverseMotion)
477    {
478        LimitReverseMotion = newLimitReverseMotion;
479        return this;
480    }
481    
482    /**
483     * Modifies this Control Request's IgnoreHardwareLimits parameter and returns itself for
484     * method-chaining and easier to use request API.
485     * <p>
486     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
487     * LimitReverseMotion parameters, instead allowing motion.
488     * <p>
489     * This can be useful on mechanisms such as an intake/feeder, where a limit
490     * switch stops motion while intaking but should be ignored when feeding to a
491     * shooter.
492     * <p>
493     * The hardware limit faults and Forward/ReverseLimit signals will still report
494     * the values of the limit switches regardless of this parameter.
495     *
496     * @param newIgnoreHardwareLimits Parameter to modify
497     * @return Itself
498     */
499    public PositionVoltage withIgnoreHardwareLimits(boolean newIgnoreHardwareLimits)
500    {
501        IgnoreHardwareLimits = newIgnoreHardwareLimits;
502        return this;
503    }
504    
505    /**
506     * Modifies this Control Request's UseTimesync parameter and returns itself for
507     * method-chaining and easier to use request API.
508     * <p>
509     * Set to true to delay applying this control request until a timesync boundary
510     * (requires Phoenix Pro and CANivore). This eliminates the impact of
511     * nondeterministic network delays in exchange for a larger but deterministic
512     * control latency.
513     * <p>
514     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
515     * Additionally, when this is enabled, the UpdateFreqHz of this request should
516     * be set to 0 Hz.
517     *
518     * @param newUseTimesync Parameter to modify
519     * @return Itself
520     */
521    public PositionVoltage withUseTimesync(boolean newUseTimesync)
522    {
523        UseTimesync = newUseTimesync;
524        return this;
525    }
526
527    /**
528     * Sets the period at which this control will update at.
529     * This is designated in Hertz, with a minimum of 20 Hz
530     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
531     * <p>
532     * If this field is set to 0 Hz, the control request will
533     * be sent immediately as a one-shot frame. This may be useful
534     * for advanced applications that require outputs to be
535     * synchronized with data acquisition. In this case, we
536     * recommend not exceeding 50 ms between control calls.
537     *
538     * @param newUpdateFreqHz Parameter to modify
539     * @return Itself
540     */
541    @Override
542    public PositionVoltage withUpdateFreqHz(double newUpdateFreqHz)
543    {
544        UpdateFreqHz = newUpdateFreqHz;
545        return this;
546    }
547
548    /**
549     * Sets the period at which this control will update at.
550     * This is designated in Hertz, with a minimum of 20 Hz
551     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
552     * <p>
553     * If this field is set to 0 Hz, the control request will
554     * be sent immediately as a one-shot frame. This may be useful
555     * for advanced applications that require outputs to be
556     * synchronized with data acquisition. In this case, we
557     * recommend not exceeding 50 ms between control calls.
558     *
559     * @param newUpdateFreqHz Parameter to modify
560     * @return Itself
561     */
562    @Override
563    public PositionVoltage withUpdateFreqHz(Frequency newUpdateFreqHz)
564    {
565        UpdateFreqHz = newUpdateFreqHz.in(Hertz);
566        return this;
567    }
568
569    @Override
570    public PositionVoltage clone()
571    {
572        try {
573            return (PositionVoltage)super.clone();
574        } catch (CloneNotSupportedException ex) {
575            /* this should never happen */
576            throw new RuntimeException(ex);
577        }
578    }
579}
580