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