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