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 com.ctre.phoenix6.StatusCode;
010import com.ctre.phoenixpro.Timestamp.TimestampSource;
011import com.ctre.phoenixpro.hardware.DeviceIdentifier;
012import com.ctre.phoenix6.jni.ErrorReportingJNI;
013import com.ctre.phoenix6.jni.StatusSignalJNI;
014
015/**
016 * Class that provides operations to retrieve
017 * information about a status signal.
018 *
019 * @deprecated BaseStatusSignalValue has been renamed to BaseStatusSignal.
020 *             Additionally, Classes in the phoenixpro package will be removed in 2024.
021 *             Users should instead use classes from the phoenix6 package.
022 */
023@Deprecated(forRemoval = true)
024public abstract class BaseStatusSignalValue {
025    protected DeviceIdentifier deviceIdentifier;
026    protected int spn;
027    protected String units;
028    protected StatusCode error = StatusCode.StatusCodeNotInitialized;
029    protected double baseValue = 0;
030    protected AllTimestamps timestamps = new AllTimestamps();
031    protected final String signalName;
032
033    protected StatusSignalJNI jni = new StatusSignalJNI();
034
035    BaseStatusSignalValue(DeviceIdentifier deviceIdentifier, int spn, String signalName) {
036        this.deviceIdentifier = deviceIdentifier;
037        this.spn = spn;
038        this.signalName = signalName;
039
040        jni.deviceHash = deviceIdentifier.getDeviceHash();
041        jni.spn = spn;
042
043        this.units = jni.JNI_GetUnits();
044    }
045
046    /* Constructor for an invalid BaseStatusSignalValue */
047    BaseStatusSignalValue(StatusCode error) {
048        this(new DeviceIdentifier(), 0, "Invalid");
049        this.error = error;
050    }
051
052    protected void copyFrom(BaseStatusSignalValue other) {
053        this.units = other.units;
054        this.error = other.error;
055        this.baseValue = other.baseValue;
056        this.timestamps = other.timestamps;
057        // Don't copy the signal name since the user expects the original name
058    }
059
060    /**
061     * Waits for new data on all provided signals up to timeout.
062     * This API is typically used with CANivore Bus signals as they will be synced using the
063     * CANivore Timesync feature and arrive simultaneously. Signals on a roboRIO bus cannot
064     * be synced and may require a significantly longer blocking call to receive all signals.
065     *
066     * This can also be used with a timeout of zero to refresh many signals at once, which
067     * is faster than calling refresh() on every signal.
068     *
069     * @param timeoutSeconds Maximum time to wait for new data in seconds.
070     *                       Pass zero to refresh all signals without blocking.
071     * @param signals        Signals to wait for new data against
072     * @return An InvalidParamValue if signals array is empty,
073     *         InvalidNetwork if signals are on different CAN bus networks,
074     *         RxTimeout if it took longer than timeoutSeconds to receive all the signals,
075     *         MultiSignalNotSupported if using the roboRIO bus with more than one signal and a non-zero timeout.
076     *         An OK status code means that all signals arrived within timeoutSeconds and they are all OK.
077     * 
078     *         Any other value represents the StatusCode of the first failed signal.
079     *         Call getError() on each signal to determine which ones failed.
080     */
081    public static StatusCode waitForAll(double timeoutSeconds, BaseStatusSignalValue... signals) {
082        String network = "";
083        boolean first = true;
084        if (signals.length < 1)
085        {
086            /* We don't have any signals to wait for, so return early */
087            ErrorReportingJNI.reportStatusCode(StatusCode.InvalidParamValue.value, "ctre.phoenixpro.BaseStatusSignalValue.waitForAll");
088            return StatusCode.InvalidParamValue;
089        }
090        StatusSignalJNI[] toGet = new StatusSignalJNI[signals.length];
091        for (int i = 0; i < signals.length; ++i) {
092            var sig = signals[i];
093            if (first) {
094                network = sig.deviceIdentifier.getNetwork();
095                first = false;
096            } else if (!sig.deviceIdentifier.getNetwork().equals(network)) {
097                // Networks don't match, return early
098                ErrorReportingJNI.reportStatusCode(StatusCode.InvalidNetwork.value, "ctre.phoenixpro.BaseStatusSignalValue.waitForAll");
099                return StatusCode.InvalidNetwork;
100            }
101            toGet[i] = sig.jni;
102        }
103        int err = StatusSignalJNI.JNI_WaitForAll(network, timeoutSeconds, toGet);
104        for (int i = 0; i < signals.length; ++i) {
105            signals[i].spn = toGet[i].spn;
106            signals[i].error = StatusCode.valueOf(toGet[i].statusCode);
107            signals[i].baseValue = toGet[i].value;
108            signals[i].timestamps.update(toGet[i].swtimeStampSeconds, TimestampSource.System, true,
109                    toGet[i].hwtimeStampSeconds, TimestampSource.CANivore, true,
110                    0, null, false);
111        }
112        StatusCode retval = StatusCode.valueOf(err);
113        if (false == retval.isOK()) {
114            ErrorReportingJNI.reportStatusCode(retval.value, "ctre.phoenixpro.BaseStatusSignalValue.waitForAll");
115        }
116        return StatusCode.valueOf(err);
117    }
118    /**
119     * Performs latency compensation on signal using the signalSlope and signal's latency to determine
120     * the magnitude of compensation. The caller must refresh these StatusSignalValues beforehand;
121     * this function only does the math required for latency compensation.
122     * <p>
123     * <b>Important</b>: The signalSlope must be the rate of change of the signal. If it is not the latency
124     * compensation may not perform as expected.
125     * <p>
126     * <b>Example</b>:
127     * double compensatedTurns = getLatencyCompensatedValue(fx.getPosition(), fx.getVelocity());
128     *
129     * @param signal Signal to be latency compensated. Caller must make sure this signal is up to date
130     *               either by calling {@link StatusSignalValue#refresh()} or {@link StatusSignalValue#waitForUpdate(double)}.
131     * @param signalSlope Derivative of signal that informs compensation magnitude. Caller must make sure this
132     *                    signal is up to date either by calling {@link StatusSignalValue#refresh()} or
133     *                    {@link StatusSignalValue#waitForUpdate(double)}.
134     * @return Latency compensated value from the signal StatusSignalValue.
135     */
136    public static double getLatencyCompensatedValue(StatusSignalValue<Double> signal, StatusSignalValue<Double> signalSlope)
137    {
138        double nonCompensatedSignal = signal.getValue();
139        double changeInSignal = signalSlope.getValue();
140        double latency = signal.getTimestamp().getLatency();
141        return nonCompensatedSignal + (changeInSignal * latency);
142    }
143
144    /**
145     * Checks if all signals have an OK error code.
146     *
147     * @param signals Signals to check error code of.
148     * @return True if all are good, false otherwise
149     */
150    public static boolean isAllGood(BaseStatusSignalValue... signals) {
151        for (BaseStatusSignalValue sig : signals) {
152            if (!sig.getError().isOK())
153                return false;
154        }
155        return true;
156    }
157
158    /**
159     * Sets the rate at which the device will publish this signal.
160     * <p>
161     * The minimum supported signal frequency is 4 Hz, and the maximum is 1000 Hz.
162     * <p>
163     * This will wait up to 0.050 seconds (50ms) by default.
164     *
165     * @param frequencyHz Rate to publish the signal in Hz.
166     * @return Status code of setting the update frequency
167     */
168    public StatusCode setUpdateFrequency(double frequencyHz) {
169        return setUpdateFrequency(frequencyHz, 0.050);
170    }
171
172    /**
173     * Sets the rate at which the device will publish this signal.
174     * <p>
175     * The minimum supported signal frequency is 4 Hz, and the maximum is 1000 Hz.
176     *
177     * @param frequencyHz Rate to publish the signal in Hz.
178     * @param timeoutSeconds Maximum amount of time to wait when performing the action
179     * @return Status code of setting the update frequency
180     */
181    public StatusCode setUpdateFrequency(double frequencyHz, double timeoutSeconds) {
182        /* attempt to change the period */
183        return StatusCode.valueOf(jni.JNI_SetUpdateFrequency(this.deviceIdentifier.getNetwork(), frequencyHz, timeoutSeconds));
184    }
185
186    /**
187     * Gets the units for this signal
188     *
189     * @return String representation of units for this signal
190     */
191    public String getUnits() {
192        return units;
193    }
194
195    /**
196     * Get all timestamps relevant for this signal
197     *
198     * @return All timestamps available for this signal
199     */
200    public AllTimestamps getAllTimestamps() {
201        return timestamps;
202    }
203
204    /**
205     * Get the best available timestamp for this signal
206     *
207     * @return Best available timestamp
208     */
209    public Timestamp getTimestamp() {
210        return timestamps.getBestTimestamp();
211    }
212
213    /**
214     * Get the error code from when we last received this signal
215     *
216     * @return Last cached Error Code
217     */
218    public StatusCode getError() {
219        return error;
220    }
221};