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};