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