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