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}