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.phoenixpro;
008
009import java.lang.reflect.InvocationTargetException;
010import java.util.Map;
011import java.util.function.Supplier;
012
013import com.ctre.phoenix6.StatusCode;
014import com.ctre.phoenixpro.Timestamp.TimestampSource;
015import com.ctre.phoenixpro.hardware.DeviceIdentifier;
016import com.ctre.phoenixpro.hardware.ParentDevice.MapGenerator;
017import com.ctre.phoenix6.jni.ErrorReportingJNI;
018
019/**
020 * Represents a status signal with data of type T, and
021 * operations available to retrieve information about
022 * the signal.
023 *
024 * @deprecated StatusSignalValue has been renamed to StatusSignal.
025 *             Additionally, Classes in the phoenixpro package will be removed in 2024.
026 *             Users should instead use classes from the phoenix6 package.
027 */
028@Deprecated(forRemoval = true)
029public class StatusSignalValue<T> extends BaseStatusSignalValue implements Cloneable {
030    private Class<T> classOfSignal;
031    private boolean _containsUnderlyingTypes;
032    private Map<Integer, StatusSignalValue<T>> _basicTypeMap;
033    private Runnable _reportIfOldFunc;
034
035    /**
036     * @deprecated StatusSignalValue has been renamed to StatusSignal.
037     *             Additionally, Classes in the phoenixpro package will be removed in 2024.
038     *             Users should instead use classes from the phoenix6 package.
039     */
040    @Deprecated(forRemoval = true)
041    public StatusSignalValue(DeviceIdentifier deviceIdentifier, int spn, Runnable reportIfOldFunc,
042            Class<T> classOfSignal, String signalName) {
043        super(deviceIdentifier, spn, signalName);
044        this.classOfSignal = classOfSignal;
045        _containsUnderlyingTypes = false;
046        _basicTypeMap = null;
047        _reportIfOldFunc = reportIfOldFunc;
048    }
049
050    /**
051     * @deprecated StatusSignalValue has been renamed to StatusSignal.
052     *             Additionally, Classes in the phoenixpro package will be removed in 2024.
053     *             Users should instead use classes from the phoenix6 package.
054     */
055    @Deprecated(forRemoval = true)
056    public StatusSignalValue(DeviceIdentifier deviceIdentifier, int spn, Runnable reportIfOldFunc,
057            Class<T> classOfSignal,
058            MapGenerator<T> generator, String signalName) {
059        super(deviceIdentifier, spn, signalName);
060        this.classOfSignal = classOfSignal;
061        _containsUnderlyingTypes = true;
062        _basicTypeMap = generator.run();
063        _reportIfOldFunc = reportIfOldFunc;
064    }
065
066    /**
067     * Constructor for an invalid StatusSignalValue 
068     *
069     * @deprecated StatusSignalValue has been renamed to StatusSignal.
070     *             Additionally, Classes in the phoenixpro package will be removed in 2024.
071     *             Users should instead use classes from the phoenix6 package.
072     */
073    @Deprecated(forRemoval = true)
074    public StatusSignalValue(Class<T> classOfSignal, StatusCode error) {
075        super(error);
076        this.classOfSignal = classOfSignal;
077        _containsUnderlyingTypes = false;
078        _basicTypeMap = null;
079        _reportIfOldFunc = () -> {};
080    }
081
082    /**
083     * Returns a lambda that calls Refresh and GetValue on this object. This is useful for command-based programming.
084     *
085     * @return Supplier that refreshes this signal and returns it
086     */
087    public Supplier<T> asSupplier() {
088        return () -> {
089            return refresh().getValue();
090        };
091    }
092
093    /**
094     * Information from a single measurement of a status signal.
095     *
096     * @deprecated Classes in the phoenixpro package will be removed in 2024.
097     *             Users should instead use classes from the phoenix6 package.
098     */
099    @Deprecated(forRemoval = true)
100    public class SignalMeasurement<L> {
101        /**
102         * The value of the signal, this may be an enum so it is stored as a string
103         */
104        public L value;
105        /**
106         * Timestamp of when the data point was taken
107         */
108        public double timestamp;
109        /**
110         * Code response of getting the data
111         */
112        public StatusCode error;
113        /**
114         * Units that correspond to this point
115         */
116        public String units;
117    };
118
119    @Override
120    public String toString() {
121        if (getValue() != null && units != null)
122            return getValue().toString() + " " + units;
123        return "Invalid signal";
124    }
125
126    @Override
127    public StatusSignalValue<T> clone() {
128        try {
129            @SuppressWarnings("unchecked")
130            StatusSignalValue<T> toReturn = StatusSignalValue.class.cast(super.clone());
131            toReturn.jni = jni.clone();
132            return toReturn;
133        } catch (CloneNotSupportedException ex) {
134            /* this should never happen */
135            return new StatusSignalValue<T>(classOfSignal, StatusCode.InvalidParamValue);
136        }
137    }
138
139    public Class<T> getTypeClass() {
140        return classOfSignal;
141    }
142
143    /**
144     * Gets the cached value from this status signal value
145     * <p>
146     * Gets the cached value. To make sure the value is up-to-date call
147     * {@link #refresh()} or {@link #waitForUpdate(double)}
148     *
149     * @return Cached value
150     */
151    public T getValue() {
152        if (classOfSignal.equals(Double.class)) {
153            return classOfSignal.cast(baseValue);
154        } else if (classOfSignal.equals(Integer.class)) {
155            return classOfSignal.cast((int) baseValue);
156        } else if (classOfSignal.equals(Boolean.class)) {
157            return classOfSignal.cast(baseValue != 0 ? true : false);
158        } else if (classOfSignal.isEnum()) {
159            try {
160                /* This is an enum, so it contains a valueOf class method that we can invoke instead */
161                return classOfSignal.cast(classOfSignal.getMethod("valueOf", Integer.TYPE).invoke(null, (int)baseValue));
162            } catch (IllegalAccessException excep) {
163                /* valueOf is not accessible */
164                error = StatusCode.CouldNotCast;
165            } catch (IllegalArgumentException excep) {
166                /* Invalid valueOf argument */
167                error = StatusCode.CouldNotCast;
168            } catch (InvocationTargetException excep) {
169                /* Could not invoke valueOf on this enum */
170                error = StatusCode.CouldNotCast;
171            } catch (NoSuchMethodException excep) {
172                /* valueOf with parameter of int is not available for this enum */
173                error = StatusCode.CouldNotCast;
174            } catch (ClassCastException excep) {
175                /* The valueOf return didn't match the class type that we need to return */
176                error = StatusCode.CouldNotCast;
177            }
178        } else {
179            /* Try to cast it, I guess */
180            try {
181                return classOfSignal.cast(baseValue);
182            } catch (ClassCastException excep) {
183                /* Cast failed, do something I guess */
184                error = StatusCode.CouldNotCast;
185            }
186        }
187        return null;
188    }
189
190    private void refreshMappable(boolean waitForSignal, double timeout) {
191        if (!_containsUnderlyingTypes)
192            return;
193        if (waitForSignal) {
194            error = StatusCode.valueOf(jni.JNI_WaitForSignal(deviceIdentifier.getNetwork(), timeout));
195        } else {
196            error = StatusCode.valueOf(jni.JNI_RefreshSignal(deviceIdentifier.getNetwork(), timeout));
197        }
198
199        if (_basicTypeMap.containsKey((int) jni.value)) {
200            StatusSignalValue<T> gottenValue = _basicTypeMap.get((int) jni.value);
201            gottenValue.updateValue(waitForSignal, timeout, false);
202            /* no lock needed, pointer and primitive copies are atomic */
203            copyFrom(gottenValue);
204        }
205    }
206
207    private void refreshNonmappable(boolean waitForSignal, double timeout) {
208        if (_containsUnderlyingTypes)
209            return;
210        if (waitForSignal) {
211            error = StatusCode.valueOf(jni.JNI_WaitForSignal(deviceIdentifier.getNetwork(), timeout));
212        } else {
213            error = StatusCode.valueOf(jni.JNI_RefreshSignal(deviceIdentifier.getNetwork(), timeout));
214        }
215        if (error.isError()) { // don't update on an error
216            return;
217        }
218        baseValue = jni.value;
219        timestamps.update(jni.swtimeStampSeconds, TimestampSource.System, true,
220                jni.hwtimeStampSeconds, TimestampSource.CANivore, true,
221                0, null, false);
222    }
223
224    private void updateValue(boolean waitForSignal, double timeout, boolean reportError) {
225        _reportIfOldFunc.run();
226        if (_containsUnderlyingTypes) {
227            refreshMappable(waitForSignal, timeout);
228        } else {
229            refreshNonmappable(waitForSignal, timeout);
230        }
231        if (reportError && !this.error.isOK()) {
232            String device = this.deviceIdentifier.toString() + " Status Signal Value " + this.signalName;
233            ErrorReportingJNI.reportStatusCode(this.error.value, device);
234        }
235    }
236
237    /**
238     * Refreshes this status signal value
239     * <p>
240     * If the user application caches this StatusSignalValue object
241     * instead of periodically fetching it from the hardware device,
242     * this function must be called to fetch fresh data.
243     * <p>
244     * This performs a non-blocking refresh operation. If you want to wait until you
245     * receive the signal, call {@link #waitForUpdate(double)} instead.
246     *
247     * @param reportError Whether to report any errors to the Driver Station/stderr.
248     *                    Defaults true
249     *
250     * @return Reference to itself
251     */
252    public StatusSignalValue<T> refresh(boolean reportError) {
253        updateValue(false, 0.300, reportError);
254        return this;
255    }
256
257    /**
258     * Refreshes this status signal value
259     * <p>
260     * If the user application caches this StatusSignalValue object
261     * instead of periodically fetching it from the hardware device,
262     * this function must be called to fetch fresh data.
263     * <p>
264     * This performs a non-blocking refresh operation. If you want to wait until you
265     * receive the signal, call {@link #waitForUpdate(double)} instead.
266     *
267     * @return Reference to itself
268     */
269    public StatusSignalValue<T> refresh() {
270        return refresh(true);
271    }
272
273    /**
274     * Waits up to timeoutSec to get the up-to-date status signal value
275     * <p>
276     * This performs a blocking refresh operation. If you want to non-blocking
277     * refresh the signal, call {@link #refresh()} instead.
278     *
279     * @param timeoutSec  Maximum time to wait for the signal to update
280     * @param reportError Whether to report any errors to the Driver Station/stderr.
281     *                    Defaults true
282     *
283     * @return Reference to itself
284     */
285    public StatusSignalValue<T> waitForUpdate(double timeoutSec, boolean reportError) {
286        updateValue(true, timeoutSec, reportError);
287        return this;
288    }
289
290    /**
291     * Waits up to timeoutSec to get the up-to-date status signal value
292     * <p>
293     * This performs a blocking refresh operation. If you want to non-blocking
294     * refresh the signal, call {@link #refresh()} instead.
295     *
296     * @param timeoutSec Maximum time to wait for the signal to update
297     *
298     * @return Reference to itself
299     */
300    public StatusSignalValue<T> waitForUpdate(double timeoutSec) {
301        return waitForUpdate(timeoutSec, true);
302    }
303
304    /**
305     * Get a basic data-only container with this information, to be used for things
306     * such as data logging.
307     *
308     * @return Basic structure with all relevant information
309     */
310    public SignalMeasurement<T> getDataCopy() {
311        SignalMeasurement<T> toRet = new SignalMeasurement<T>();
312        toRet.value = getValue();
313        toRet.error = getError();
314        toRet.units = getUnits();
315        toRet.timestamp = getTimestamp().getTime();
316        return toRet;
317    }
318}