001package jmri.jmrix.lenz;
002
003import java.util.EnumSet;
004import java.util.HashMap;
005import jmri.LocoAddress;
006import jmri.SpeedStepMode;
007import jmri.jmrix.AbstractThrottleManager;
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011/**
012 * XNet implementation of a ThrottleManager based on the
013 * AbstractThrottleManager.
014 *
015 * @author Paul Bender Copyright (C) 2002-2004
016 * @navassoc 1 - * jmri.jmrix.lenz.XNetThrottle
017 */
018public class XNetThrottleManager extends AbstractThrottleManager implements XNetListener {
019
020    protected final HashMap<LocoAddress, XNetThrottle> throttles = new HashMap<>(5);
021
022    protected XNetTrafficController tc;
023
024    /**
025     * Constructor.
026     * @param memo system connection.
027     */
028    public XNetThrottleManager(XNetSystemConnectionMemo memo) {
029        super(memo);
030        // connect to the TrafficManager
031        tc = memo.getXNetTrafficController();
032
033        // Register to listen for throttle messages
034        tc.addXNetListener(XNetInterface.THROTTLE, this);
035    }
036
037    /**
038     * Request a new throttle object be creaetd for the address, and let the
039     * throttle listeners know about it.
040     */
041    @Override
042    public void requestThrottleSetup(LocoAddress address, boolean control) {
043        XNetThrottle throttle;
044        if (log.isDebugEnabled()) {
045            log.debug("Requesting Throttle: {}", address);
046        }
047        // range check for LH200 and Compact/Commander
048        if (tc.getCommandStation().getCommandStationType() == 0x01 ||
049            tc.getCommandStation().getCommandStationType() == 0x02 ) {
050            if(address.getNumber()>=100) {
051               String typeString = Bundle.getMessage(tc.getCommandStation().getCommandStationType() == 0x01?"CSTypeLH200":"CSTypeCompact");
052               failedThrottleRequest(address,Bundle.getMessage("ThrottleErrorCSTwoDigit",typeString));
053               return;
054            }
055        }
056        if (throttles.containsKey(address)) {
057            notifyThrottleKnown(throttles.get(address), address);
058        } else {
059            throttle = new XNetThrottle((XNetSystemConnectionMemo) adapterMemo, address, tc);
060            throttles.put(address, throttle);
061            notifyThrottleKnown(throttle, address);
062        }
063    }
064
065    /**
066     * XpressNet based systems DO NOT use the Dispatch Function.
067     */
068    @Override
069    public boolean hasDispatchFunction() {
070        return false;
071    }
072
073    /**
074     * XpressNet based systems can have multiple throttles for the same
075     * device.
076     * <p>
077     * {@inheritDoc}
078     */
079    @Override
080    protected boolean singleUse() {
081        return false;
082    }
083
084    /**
085     * Address 100 and above is a long address.
086     */
087    @Override
088    public boolean canBeLongAddress(int address) {
089        return isLongAddress(address);
090    }
091
092    /**
093     * Address 99 and below is a short address.
094     */
095    @Override
096    public boolean canBeShortAddress(int address) {
097        return !isLongAddress(address);
098    }
099
100    /**
101     * Are there any ambiguous addresses (short vs long) on this system?
102     */
103    @Override
104    public boolean addressTypeUnique() {
105        return true;
106    }
107
108    /**
109     * Local method for deciding short/long address
110     * @param num address to examine
111     * @return true if can be long address
112     */
113    static protected boolean isLongAddress(int num) {
114        return (num >= 100);
115    }
116
117    /**
118     * What speed modes are supported by this system? value should be xor of
119     * possible modes specifed by the DccThrottle interface XpressNet supports
120     * 14,27,28 and 128 speed step modes
121     */
122    @Override
123    public EnumSet<SpeedStepMode> supportedSpeedModes() {
124        return EnumSet.of(SpeedStepMode.NMRA_DCC_128
125                , SpeedStepMode.NMRA_DCC_28
126                , SpeedStepMode.NMRA_DCC_27
127                , SpeedStepMode.NMRA_DCC_14);
128    }
129
130    /**
131     * Handle incoming messages for throttles.
132     */
133    @Override
134    public void message(XNetReply r) {
135        // We want to check to see if a throttle has taken over an address
136        if (r.getElement(0) == XNetConstants.LOCO_INFO_RESPONSE) {
137            if (r.getElement(1) == XNetConstants.LOCO_NOT_AVAILABLE) {
138                // This is a take over message.  If we know about this throttle,
139                // send the message on.
140                LocoAddress address = new jmri.DccLocoAddress(r.getThrottleMsgAddr(),
141                        isLongAddress(r.getThrottleMsgAddr()));
142                if (throttles.containsKey(address)) {
143                    throttles.get(address).message(r);
144                }
145            }
146        }
147
148    }
149
150    /**
151     * Listen for the messages to the LI100/LI101.
152     */
153    @Override
154    public void message(XNetMessage l) {
155    }
156
157    /**
158     * Handle a timeout notification.
159     */
160    @Override
161    public void notifyTimeout(XNetMessage msg) {
162    }
163
164    @Override
165    public void releaseThrottle(jmri.DccThrottle t, jmri.ThrottleListener l) {
166    }
167
168    @Override
169    public boolean disposeThrottle(jmri.DccThrottle t, jmri.ThrottleListener l) {
170        if (super.disposeThrottle(t, l)) {
171            if(!(t instanceof XNetThrottle)) {
172               throw new IllegalArgumentException("Attempt to dispose non-XpressNet Throttle");
173            }
174            XNetThrottle lnt = (XNetThrottle) t;
175            lnt.throttleDispose();
176            return true;
177        }
178        return false;
179    }
180
181    private static final Logger log = LoggerFactory.getLogger(XNetThrottleManager.class);
182
183}