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.hardware; 008 009import com.ctre.phoenixpro.BaseStatusSignalValue; 010import com.ctre.phoenixpro.StatusCode; 011import com.ctre.phoenixpro.StatusSignalValue; 012import com.ctre.phoenixpro.controls.ControlRequest; 013import com.ctre.phoenixpro.controls.EmptyControl; 014import com.ctre.phoenixpro.jni.CtreJniWrapper; 015import com.ctre.phoenixpro.jni.ErrorReportingJNI; 016 017import java.util.Map; 018import java.util.concurrent.ConcurrentHashMap; 019import java.util.concurrent.locks.Lock; 020import java.util.concurrent.locks.ReentrantLock; 021 022public abstract class ParentDevice extends CtreJniWrapper { 023 protected static final double kDefaultControlRatePeriodsSec = 0.010; 024 025 private final Map<Integer, BaseStatusSignalValue> _signalValues = new ConcurrentHashMap<Integer, BaseStatusSignalValue>(); 026 private ControlRequest _controlReq = new EmptyControl(); 027 private final Lock _controlReqLck = new ReentrantLock(); 028 029 protected final DeviceIdentifier deviceIdentifier; 030 031 protected abstract void reportIfTooOld(); 032 033 public interface MapGenerator<T> { 034 Map<Integer, StatusSignalValue<T>> run(); 035 } 036 037 public ParentDevice(int deviceID, String model, String canbus) { 038 this.deviceIdentifier = new DeviceIdentifier(deviceID, model, canbus); 039 } 040 041 /** 042 * @return the device ID of this device [0,62]. 043 */ 044 public int getDeviceID() { 045 return deviceIdentifier.deviceID; 046 } 047 048 /** 049 * @return name of the CAN bus this device is on. 050 */ 051 public String getCANBus() { 052 return deviceIdentifier.network; 053 } 054 055 protected StatusCode setControlPrivate(ControlRequest request) { 056 StatusCode status; 057 058 _controlReqLck.lock(); 059 /* make sure we always unlock */ 060 try { 061 reportIfTooOld(); 062 063 boolean cancelOtherRequests = false; 064 if (request.getControlInfo().getNameValues().get("name") != getAppliedControl() 065 .getControlInfo().getNameValues().get("name")) { 066 cancelOtherRequests = true; 067 } 068 069 _controlReq = request; 070 status = _controlReq.sendRequest(deviceIdentifier.network, deviceIdentifier.deviceHash, 071 cancelOtherRequests); 072 } finally { 073 _controlReqLck.unlock(); 074 } 075 076 if (!status.isOK()) { 077 ErrorReportingJNI.reportStatusCode(status.value, deviceIdentifier.toString() + " SetControl " 078 + request.getControlInfo().getNameValues().get("name")); 079 } 080 return status; 081 } 082 083 /** 084 * Get the latest applied control 085 * 086 * @return Latest applied control 087 */ 088 public ControlRequest getAppliedControl() { 089 return _controlReq; 090 } 091 092 private <T> StatusSignalValue<T> commonLookup(int spn, Class<T> classOfSignal, int mapIter, 093 MapGenerator<T> generator, String signalName, boolean reportOnConstruction) { 094 int totalHash = spn | (mapIter << 16); 095 /* lookup and return if found */ 096 BaseStatusSignalValue toFind; 097 if (_signalValues.containsKey(totalHash)) { 098 /* Found it, toFind is now the found StatusSignalValue */ 099 toFind = _signalValues.get(totalHash); 100 /* since we didn't construct, report errors */ 101 reportOnConstruction = true; 102 } else { 103 /* insert into map */ 104 if (mapIter == 0) { 105 _signalValues.put(totalHash, 106 new StatusSignalValue<T>(deviceIdentifier, spn, () -> { 107 reportIfTooOld(); 108 }, classOfSignal, signalName)); 109 } else { 110 _signalValues.put(totalHash, 111 new StatusSignalValue<T>(deviceIdentifier, spn, () -> { 112 reportIfTooOld(); 113 }, classOfSignal, generator, signalName)); 114 } 115 116 /* look up and return */ 117 toFind = _signalValues.get(totalHash); 118 } 119 /* Try to return the found value cast to the child class */ 120 121 /* Turn off unchecked warning, we check the type afterwards */ 122 @SuppressWarnings("unchecked") 123 StatusSignalValue<T> toReturn = StatusSignalValue.class.cast(toFind); 124 125 if (toReturn == null) { 126 /* Cast failed, so return with error */ 127 return new StatusSignalValue<T>(classOfSignal, StatusCode.InvalidParamValue); 128 } else if (toReturn.getTypeClass().equals(classOfSignal)) { 129 /* Refresh the signal before we pass it down */ 130 toReturn.refresh(reportOnConstruction); 131 /* We're good, go ahead and return */ 132 return toReturn; 133 } else { 134 /* Type does not match, return with error */ 135 return new StatusSignalValue<T>(classOfSignal, StatusCode.InvalidParamValue); 136 } 137 } 138 139 protected <T> StatusSignalValue<T> lookupStatusSignalValue(int spn, Class<T> classOfSignal, String signalName, boolean reportOnConstruction) { 140 return commonLookup(spn, classOfSignal, 0, null, signalName, reportOnConstruction); 141 } 142 143 protected <T> StatusSignalValue<T> lookupStatusSignalValue(int spn, Class<T> classOfSignal, int mapIter, 144 MapGenerator<T> generator, String signalName, boolean reportOnConstruction) { 145 return commonLookup(spn, classOfSignal, mapIter, generator, signalName, reportOnConstruction); 146 } 147}