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.CANBus;
010import com.ctre.phoenix6.StatusCode;
011import com.ctre.phoenix6.controls.jni.ControlJNI;
012import com.ctre.phoenix6.hardware.traits.*;
013
014import edu.wpi.first.units.*;
015import edu.wpi.first.units.measure.*;
016import static edu.wpi.first.units.Units.*;
017
018import java.util.HashMap;
019import java.util.Map;
020
021/**
022 * Request a specified motor duty cycle.
023 * <p>
024 * This control mode will output a proportion of the supplied voltage which is supplied by the user.
025 */
026public final class DutyCycleOut implements ControlRequest, Cloneable {
027    /**
028     * Proportion of supply voltage to apply in fractional units between -1 and +1
029     * 
030     * <ul>
031     *   <li> Units: fractional
032     * </ul>
033     * 
034     */
035    public double Output;
036    /**
037     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
038     * peak power by ~15% on supported devices (see {@link SupportsFOC}). Set to
039     * false to use trapezoidal commutation.
040     * <p>
041     * FOC improves motor performance by leveraging torque (current) control. 
042     * However, this may be inconvenient for applications that require specifying
043     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
044     * combines the performances gains of FOC while still allowing applications to
045     * provide duty cycle or voltage demand.  This not to be confused with simple
046     * sinusoidal control or phase voltage control which lacks the performance
047     * gains.
048     */
049    public boolean EnableFOC = true;
050    /**
051     * Set to true to static-brake the rotor when output is zero (or within
052     * deadband).  Set to false to use the NeutralMode configuration setting
053     * (default). This flag exists to provide the fundamental behavior of this
054     * control when output is zero, which is to provide 0V to the motor.
055     */
056    public boolean OverrideBrakeDurNeutral = false;
057    /**
058     * Set to true to force forward limiting.  This allows users to use other limit
059     * switch sensors connected to robot controller.  This also allows use of active
060     * sensors that require external power.
061     */
062    public boolean LimitForwardMotion = false;
063    /**
064     * Set to true to force reverse limiting.  This allows users to use other limit
065     * switch sensors connected to robot controller.  This also allows use of active
066     * sensors that require external power.
067     */
068    public boolean LimitReverseMotion = false;
069    /**
070     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
071     * LimitReverseMotion parameters, instead allowing motion.
072     * <p>
073     * This can be useful on mechanisms such as an intake/feeder, where a limit
074     * switch stops motion while intaking but should be ignored when feeding to a
075     * shooter.
076     * <p>
077     * The hardware limit faults and Forward/ReverseLimit signals will still report
078     * the values of the limit switches regardless of this parameter.
079     */
080    public boolean IgnoreHardwareLimits = false;
081    /**
082     * Set to true to ignore software limits, instead allowing motion.
083     * <p>
084     * This can be useful when calibrating the zero point of a mechanism such as an
085     * elevator.
086     * <p>
087     * The software limit faults will still report the values of the software limits
088     * regardless of this parameter.
089     */
090    public boolean IgnoreSoftwareLimits = false;
091    /**
092     * Set to true to delay applying this control request until a timesync boundary
093     * (requires Phoenix Pro and CANivore). This eliminates the impact of
094     * nondeterministic network delays in exchange for a larger but deterministic
095     * control latency.
096     * <p>
097     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
098     * Additionally, when this is enabled, the UpdateFreqHz of this request should
099     * be set to 0 Hz.
100     */
101    public boolean UseTimesync = false;
102
103    /**
104     * The frequency at which this control will update.
105     * This is designated in Hertz, with a minimum of 20 Hz
106     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
107     * Some update frequencies are not supported and will be
108     * promoted up to the next highest supported frequency.
109     * <p>
110     * If this field is set to 0 Hz, the control request will
111     * be sent immediately as a one-shot frame. This may be useful
112     * for advanced applications that require outputs to be
113     * synchronized with data acquisition. In this case, we
114     * recommend not exceeding 50 ms between control calls.
115     */
116    public double UpdateFreqHz = 100;
117
118    /**
119     * Request a specified motor duty cycle.
120     * <p>
121     * This control mode will output a proportion of the supplied voltage which is
122     * supplied by the user.
123     * 
124     * @param Output Proportion of supply voltage to apply in fractional units
125     *               between -1 and +1
126     */
127    public DutyCycleOut(double Output) {
128        this.Output = Output;
129    }
130
131    @Override
132    public String getName() {
133        return "DutyCycleOut";
134    }
135
136    @Override
137    public String toString() {
138        String ss = "Control: DutyCycleOut\n";
139        ss += "    Output: " + Output + " fractional" + "\n";
140        ss += "    EnableFOC: " + EnableFOC + "\n";
141        ss += "    OverrideBrakeDurNeutral: " + OverrideBrakeDurNeutral + "\n";
142        ss += "    LimitForwardMotion: " + LimitForwardMotion + "\n";
143        ss += "    LimitReverseMotion: " + LimitReverseMotion + "\n";
144        ss += "    IgnoreHardwareLimits: " + IgnoreHardwareLimits + "\n";
145        ss += "    IgnoreSoftwareLimits: " + IgnoreSoftwareLimits + "\n";
146        ss += "    UseTimesync: " + UseTimesync + "\n";
147        return ss;
148    }
149
150    @Override
151    public StatusCode sendRequest(CANBus network, int deviceHash) {
152        return StatusCode.valueOf(ControlJNI.JNI_RequestControlDutyCycleOut(
153                network.getNameUTF8Bytes(), deviceHash, UpdateFreqHz, Output, EnableFOC, OverrideBrakeDurNeutral, LimitForwardMotion, LimitReverseMotion, IgnoreHardwareLimits, IgnoreSoftwareLimits, UseTimesync));
154    }
155
156    /**
157     * Gets information about this control request.
158     *
159     * @return Map of control parameter names and corresponding applied values
160     */
161    @Override
162    public Map<String, String> getControlInfo() {
163        var controlInfo = new HashMap<String, String>();
164        controlInfo.put("Name", getName());
165        controlInfo.put("Output", String.valueOf(this.Output));
166        controlInfo.put("EnableFOC", String.valueOf(this.EnableFOC));
167        controlInfo.put("OverrideBrakeDurNeutral", String.valueOf(this.OverrideBrakeDurNeutral));
168        controlInfo.put("LimitForwardMotion", String.valueOf(this.LimitForwardMotion));
169        controlInfo.put("LimitReverseMotion", String.valueOf(this.LimitReverseMotion));
170        controlInfo.put("IgnoreHardwareLimits", String.valueOf(this.IgnoreHardwareLimits));
171        controlInfo.put("IgnoreSoftwareLimits", String.valueOf(this.IgnoreSoftwareLimits));
172        controlInfo.put("UseTimesync", String.valueOf(this.UseTimesync));
173        return controlInfo;
174    }
175    
176    /**
177     * Modifies this Control Request's Output parameter and returns itself for
178     * method-chaining and easier to use request API.
179     * <p>
180     * Proportion of supply voltage to apply in fractional units between -1 and +1
181     * 
182     * <ul>
183     *   <li> Units: fractional
184     * </ul>
185     * 
186     *
187     * @param newOutput Parameter to modify
188     * @return Itself
189     */
190    public DutyCycleOut withOutput(double newOutput) {
191        Output = newOutput;
192        return this;
193    }
194    
195    /**
196     * Modifies this Control Request's EnableFOC parameter and returns itself for
197     * method-chaining and easier to use request API.
198     * <p>
199     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
200     * peak power by ~15% on supported devices (see {@link SupportsFOC}). Set to
201     * false to use trapezoidal commutation.
202     * <p>
203     * FOC improves motor performance by leveraging torque (current) control. 
204     * However, this may be inconvenient for applications that require specifying
205     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
206     * combines the performances gains of FOC while still allowing applications to
207     * provide duty cycle or voltage demand.  This not to be confused with simple
208     * sinusoidal control or phase voltage control which lacks the performance
209     * gains.
210     *
211     * @param newEnableFOC Parameter to modify
212     * @return Itself
213     */
214    public DutyCycleOut withEnableFOC(boolean newEnableFOC) {
215        EnableFOC = newEnableFOC;
216        return this;
217    }
218    
219    /**
220     * Modifies this Control Request's OverrideBrakeDurNeutral parameter and returns itself for
221     * method-chaining and easier to use request API.
222     * <p>
223     * Set to true to static-brake the rotor when output is zero (or within
224     * deadband).  Set to false to use the NeutralMode configuration setting
225     * (default). This flag exists to provide the fundamental behavior of this
226     * control when output is zero, which is to provide 0V to the motor.
227     *
228     * @param newOverrideBrakeDurNeutral Parameter to modify
229     * @return Itself
230     */
231    public DutyCycleOut withOverrideBrakeDurNeutral(boolean newOverrideBrakeDurNeutral) {
232        OverrideBrakeDurNeutral = newOverrideBrakeDurNeutral;
233        return this;
234    }
235    
236    /**
237     * Modifies this Control Request's LimitForwardMotion parameter and returns itself for
238     * method-chaining and easier to use request API.
239     * <p>
240     * Set to true to force forward limiting.  This allows users to use other limit
241     * switch sensors connected to robot controller.  This also allows use of active
242     * sensors that require external power.
243     *
244     * @param newLimitForwardMotion Parameter to modify
245     * @return Itself
246     */
247    public DutyCycleOut withLimitForwardMotion(boolean newLimitForwardMotion) {
248        LimitForwardMotion = newLimitForwardMotion;
249        return this;
250    }
251    
252    /**
253     * Modifies this Control Request's LimitReverseMotion parameter and returns itself for
254     * method-chaining and easier to use request API.
255     * <p>
256     * Set to true to force reverse limiting.  This allows users to use other limit
257     * switch sensors connected to robot controller.  This also allows use of active
258     * sensors that require external power.
259     *
260     * @param newLimitReverseMotion Parameter to modify
261     * @return Itself
262     */
263    public DutyCycleOut withLimitReverseMotion(boolean newLimitReverseMotion) {
264        LimitReverseMotion = newLimitReverseMotion;
265        return this;
266    }
267    
268    /**
269     * Modifies this Control Request's IgnoreHardwareLimits parameter and returns itself for
270     * method-chaining and easier to use request API.
271     * <p>
272     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
273     * LimitReverseMotion parameters, instead allowing motion.
274     * <p>
275     * This can be useful on mechanisms such as an intake/feeder, where a limit
276     * switch stops motion while intaking but should be ignored when feeding to a
277     * shooter.
278     * <p>
279     * The hardware limit faults and Forward/ReverseLimit signals will still report
280     * the values of the limit switches regardless of this parameter.
281     *
282     * @param newIgnoreHardwareLimits Parameter to modify
283     * @return Itself
284     */
285    public DutyCycleOut withIgnoreHardwareLimits(boolean newIgnoreHardwareLimits) {
286        IgnoreHardwareLimits = newIgnoreHardwareLimits;
287        return this;
288    }
289    
290    /**
291     * Modifies this Control Request's IgnoreSoftwareLimits parameter and returns itself for
292     * method-chaining and easier to use request API.
293     * <p>
294     * Set to true to ignore software limits, instead allowing motion.
295     * <p>
296     * This can be useful when calibrating the zero point of a mechanism such as an
297     * elevator.
298     * <p>
299     * The software limit faults will still report the values of the software limits
300     * regardless of this parameter.
301     *
302     * @param newIgnoreSoftwareLimits Parameter to modify
303     * @return Itself
304     */
305    public DutyCycleOut withIgnoreSoftwareLimits(boolean newIgnoreSoftwareLimits) {
306        IgnoreSoftwareLimits = newIgnoreSoftwareLimits;
307        return this;
308    }
309    
310    /**
311     * Modifies this Control Request's UseTimesync parameter and returns itself for
312     * method-chaining and easier to use request API.
313     * <p>
314     * Set to true to delay applying this control request until a timesync boundary
315     * (requires Phoenix Pro and CANivore). This eliminates the impact of
316     * nondeterministic network delays in exchange for a larger but deterministic
317     * control latency.
318     * <p>
319     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
320     * Additionally, when this is enabled, the UpdateFreqHz of this request should
321     * be set to 0 Hz.
322     *
323     * @param newUseTimesync Parameter to modify
324     * @return Itself
325     */
326    public DutyCycleOut withUseTimesync(boolean newUseTimesync) {
327        UseTimesync = newUseTimesync;
328        return this;
329    }
330
331    /**
332     * Sets the frequency at which this control will update.
333     * This is designated in Hertz, with a minimum of 20 Hz
334     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
335     * Some update frequencies are not supported and will be
336     * promoted up to the next highest supported frequency.
337     * <p>
338     * If this field is set to 0 Hz, the control request will
339     * be sent immediately as a one-shot frame. This may be useful
340     * for advanced applications that require outputs to be
341     * synchronized with data acquisition. In this case, we
342     * recommend not exceeding 50 ms between control calls.
343     *
344     * @param newUpdateFreqHz Parameter to modify
345     * @return Itself
346     */
347    @Override
348    public DutyCycleOut withUpdateFreqHz(double newUpdateFreqHz) {
349        UpdateFreqHz = newUpdateFreqHz;
350        return this;
351    }
352
353    /**
354     * Sets the frequency at which this control will update.
355     * This is designated in Hertz, with a minimum of 20 Hz
356     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
357     * Some update frequencies are not supported and will be
358     * promoted up to the next highest supported frequency.
359     * <p>
360     * If this field is set to 0 Hz, the control request will
361     * be sent immediately as a one-shot frame. This may be useful
362     * for advanced applications that require outputs to be
363     * synchronized with data acquisition. In this case, we
364     * recommend not exceeding 50 ms between control calls.
365     *
366     * @param newUpdateFreqHz Parameter to modify
367     * @return Itself
368     */
369    @Override
370    public DutyCycleOut withUpdateFreqHz(Frequency newUpdateFreqHz) {
371        UpdateFreqHz = newUpdateFreqHz.in(Hertz);
372        return this;
373    }
374
375    @Override
376    public DutyCycleOut clone() {
377        try {
378            return (DutyCycleOut)super.clone();
379        } catch (CloneNotSupportedException ex) {
380            /* this should never happen */
381            throw new RuntimeException(ex);
382        }
383    }
384}
385