001package jmri.jmrix.mqtt;
002
003import java.util.EnumSet;
004import java.util.HashMap;
005
006import jmri.DccThrottle;
007import jmri.LocoAddress;
008import jmri.SpeedStepMode;
009import jmri.ThrottleListener;
010import jmri.jmrix.AbstractThrottleManager;
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013import javax.annotation.Nonnull;
014
015/**
016 * MQTT implementation of a ThrottleManager based on the
017 * AbstractThrottleManager.
018 *
019 * @author Dean Cording Copyright (C) 2023
020 */
021
022public class MqttThrottleManager extends AbstractThrottleManager {
023
024    protected HashMap<LocoAddress, MqttThrottle> throttles = new HashMap<LocoAddress, MqttThrottle>(5);
025
026    /**
027     * Constructor.
028     * @param memo the memo for the connection this tm will use
029     */
030    public MqttThrottleManager(MqttSystemConnectionMemo memo) {
031        super(memo);
032    }
033
034    public void setSendThrottleTopic(@Nonnull String sendThrottleTopic) {
035        this.sendThrottleTopic = sendThrottleTopic;
036    }
037    public void setRcvThrottleTopic(@Nonnull String rcvThrottleTopic) {
038        this.rcvThrottleTopic = rcvThrottleTopic;
039    }
040    public void setSendDirectionTopic(@Nonnull String sendDirectionTopic) {
041        this.sendDirectionTopic = sendDirectionTopic;
042    }
043    public void setRcvDirectionTopic(@Nonnull String rcvDirectionTopic) {
044        this.rcvDirectionTopic = rcvDirectionTopic;
045    }
046    public void setSendFunctionTopic(@Nonnull String sendFunctionTopic) {
047        this.sendFunctionTopic = sendFunctionTopic;
048    }
049    public void setRcvFunctionTopic(@Nonnull String rcvFunctionTopic) {
050        this.rcvFunctionTopic = rcvFunctionTopic;
051    }
052
053    @Nonnull
054    public String sendThrottleTopic = "cab/{0}/throttle"; // for constructing topic; public for script access
055    @Nonnull
056    public String rcvThrottleTopic = "cab/{0}/throttle"; // for constructing topic; public for script access
057    @Nonnull
058    public String sendDirectionTopic = "cab/{0}/direction"; // for constructing topic; public for script access
059    @Nonnull
060    public String rcvDirectionTopic = "cab/{0}/direction"; // for constructing topic; public for script access
061    @Nonnull
062    public String sendFunctionTopic = "cab/{0}/function/{1}"; // for constructing topic; public for script access
063    @Nonnull
064    public String rcvFunctionTopic = "cab/{0}/function/{1}"; // for constructing topic; public for script access
065
066
067    /**
068     * Request a new throttle object be created for the address, and let the
069     * throttle listeners know about it.
070     *
071     */
072    @Override
073    public void requestThrottleSetup(LocoAddress address, boolean control) {
074        MqttThrottle throttle;
075        log.debug("Requesting Throttle: {}", address);
076        if (throttles.containsKey(address)) {
077            notifyThrottleKnown(throttles.get(address), address);
078        } else {
079            throttle = new MqttThrottle((MqttSystemConnectionMemo) adapterMemo,
080                sendThrottleTopic, rcvThrottleTopic,
081                sendDirectionTopic, rcvDirectionTopic,
082                sendFunctionTopic, rcvFunctionTopic, address);
083            throttles.put(address, throttle);
084            notifyThrottleKnown(throttle, address);
085        }
086    }
087
088    /**
089     * MQTT based systems DO use the Dispatch Function
090     */
091    @Override
092    public boolean hasDispatchFunction() {
093        return true;
094    }
095
096    /**
097     * MQTT based systems can have multiple throttles for the same
098     * device
099     * <p>
100     * {@inheritDoc}
101     */
102    @Override
103    protected boolean singleUse() {
104        return false;
105    }
106
107    /**
108     * Address 128 and above is a long address
109     *
110     */
111    @Override
112    public boolean canBeLongAddress(int address) {
113        return isLongAddress(address);
114    }
115
116    /**
117     * Address between 1 and 127 is a short address
118     *
119     */
120    @Override
121    public boolean canBeShortAddress(int address) {
122        return (address >= 1 && !isLongAddress(address));
123    }
124
125    /**
126     * There are no ambiguous addresses on this system.
127     */
128    @Override
129    public boolean addressTypeUnique() {
130        return true;
131    }
132
133    /*
134     * Local method for deciding short/long address
135     * (is it?)
136     */
137    static protected boolean isLongAddress(int num) {
138        return (num >= 128);
139    }
140
141    /**
142     * What speed modes are supported by this system? value should be xor of
143     * possible modes specifed by the DccThrottle interface DCC++ supports
144     * 14,27,28 and 128 speed step modes
145     */
146    @Override
147    public EnumSet<SpeedStepMode> supportedSpeedModes() {
148        return EnumSet.of(SpeedStepMode.NMRA_DCC_128); }
149
150    /**
151     * {@inheritDoc}
152     */
153    @Override
154    public boolean disposeThrottle(DccThrottle t, ThrottleListener l) {
155
156        log.debug("disposeThrottle {}", t);
157
158        if (super.disposeThrottle(t, l)) {
159            if (t instanceof MqttThrottle) {
160                MqttThrottle lnt = (MqttThrottle) t;
161                throttles.remove(lnt.getLocoAddress()); // remove from throttles map.
162                lnt.throttleDispose();
163                return true;
164            }
165        }
166        log.error("Dispose Throttle failed {}", t);
167        return false;
168    }
169
170    /**
171     * {@inheritDoc}
172     */
173    @Override
174    public void dispatchThrottle(DccThrottle t, ThrottleListener l) {
175        log.debug("dispatchThrottle {}", t);
176        disposeThrottle(t,l);
177    }
178
179    /**
180     * {@inheritDoc}
181     */
182    @Override
183    public void releaseThrottle(DccThrottle t, ThrottleListener l) {
184        log.debug("releaseThrottle {}", t);
185        if (t instanceof MqttThrottle) { // should always be the case as it was made that way
186            ((MqttThrottle)t).throttleRelease();
187        }
188        disposeThrottle(t, l);
189    }
190
191
192    private final static Logger log = LoggerFactory.getLogger(MqttThrottleManager.class);
193
194}