001package jmri.jmrix.loconet.uhlenbrock;
002
003import java.util.Hashtable;
004import jmri.DccLocoAddress;
005import jmri.DccThrottle;
006import jmri.LocoAddress;
007import jmri.ThrottleListener;
008import jmri.jmrix.loconet.LnThrottleManager;
009import jmri.jmrix.loconet.LocoNetSlot;
010import jmri.jmrix.loconet.LocoNetSystemConnectionMemo;
011import jmri.jmrix.loconet.LocoNetThrottle;
012import jmri.jmrix.loconet.SlotListener;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016/**
017 * LocoNet implementation of a ThrottleManager for Uhlenbrock.
018 * <p>
019 * Works in cooperation with the SlotManager, which actually handles the
020 * communications.
021 *
022 * @see jmri.jmrix.loconet.SlotManager
023 * @author Bob Jacobsen Copyright (C) 2001
024 */
025public class UhlenbrockLnThrottleManager extends LnThrottleManager {
026
027    public UhlenbrockLnThrottleManager(UhlenbrockSystemConnectionMemo memo) {
028        super(memo);
029    }
030
031    /**
032     * The Intellibox-II doesn't always respond to a request loco message,
033     * therefore the system will not allow further requests to create a throttle
034     * to that address as one is already in process.
035     *
036     * With the Intellibox throttle, it will make three attempts to connect if
037     * no response is received from the command station after 2 seconds.
038     * Otherwise it will send a failthrottlerequest message out.
039     */
040    @Override
041    public void requestThrottleSetup(LocoAddress address, boolean control) {
042        if (!(address instanceof DccLocoAddress)){
043            log.error("{} is not a DCCLocoAddress",address);
044            failedThrottleRequest(address, "Address" + address + " is not a DccLocoAddress");
045            return;
046        }
047        slotManager.slotFromLocoAddress(((DccLocoAddress) address).getNumber(), this);
048
049        class RetrySetup implements Runnable {
050
051            DccLocoAddress address;
052            SlotListener list;
053
054            RetrySetup(DccLocoAddress address, SlotListener list) {
055                this.address = address;
056                this.list = list;
057            }
058
059            @Override
060            public void run() {
061                int count = 0;
062                while (count < 3) {
063                    try {
064                        Thread.sleep(2000);
065                    } catch (InterruptedException ex) {
066                        return;
067                    }
068                    slotManager.slotFromLocoAddress(address.getNumber(), list);
069                    log.warn("No response to requesting loco {}, will try again {}", address, count);
070                    count++;
071                }
072                log.error("No response to requesting loco {} after {} attempts; will cancel the request", address, count);
073                failedThrottleRequest(address, "Failed to get response from command station");
074            }
075        }
076        Thread thr = new Thread(new RetrySetup((DccLocoAddress) address, this));
077        thr.start();
078        waitingForNotification.put(((DccLocoAddress) address).getNumber(), thr);
079    }
080
081    Hashtable<Integer, Thread> waitingForNotification = new Hashtable<Integer, Thread>(5);
082
083    /**
084     * SlotListener contract. Get notification that an address has changed slot.
085     * This method creates a throttle for all ThrottleListeners of that address
086     * and notifies them via the ThrottleListener.notifyThrottleFound method.
087     */
088    @Override
089    public void notifyChangedSlot(LocoNetSlot s) {
090        DccThrottle throttle = new LocoNetThrottle((LocoNetSystemConnectionMemo) adapterMemo, s);
091        notifyThrottleKnown(throttle, new DccLocoAddress(s.locoAddr(), isLongAddress(s.locoAddr())));
092        if (waitingForNotification.containsKey(s.locoAddr())) {
093            Thread r = waitingForNotification.get(s.locoAddr());
094            synchronized (r) {
095                r.interrupt();
096            }
097            waitingForNotification.remove(s.locoAddr());
098        }
099    }
100
101    @Override
102    public void failedThrottleRequest(LocoAddress address, String reason) {
103        if (waitingForNotification.containsKey(address.getNumber())) {
104            waitingForNotification.get(address.getNumber()).interrupt();
105            waitingForNotification.remove(address.getNumber());
106        }
107        super.failedThrottleRequest(address, reason);
108    }
109
110    /**
111     * Cancel a request for a throttle.
112     *
113     * @param address The decoder address desired.
114     * @param l       The ThrottleListener cancelling request for a throttle.
115     */
116    @Override
117    public void cancelThrottleRequest(LocoAddress address, ThrottleListener l) {
118        int loconumber = address.getNumber();
119        if (waitingForNotification.containsKey(loconumber)) {
120            waitingForNotification.get(loconumber).interrupt();
121            waitingForNotification.remove(loconumber);
122        }
123        super.cancelThrottleRequest(address, l);
124    }
125
126    private final static Logger log = LoggerFactory.getLogger(UhlenbrockLnThrottleManager.class);
127
128}