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}