001package jmri.jmrit.tracker;
002
003import jmri.Block;
004import jmri.SignalHead;
005import jmri.Throttle;
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008
009/**
010 * Stop a train in a block if required.
011 * <p>
012 * Watches a Block object that is passing around a Throttle object as its
013 * value. When the Block goes OCCUPIED, check whether a signal is telling the
014 * train to stop; if so, force the Throttle to zero speed.
015 * <p>
016 * This contains multiple SignalHead objects, each associated with a Path that
017 * contains one or more BeanSettings (e.g. Turnout positions) and directions.
018 * When needed, this consults the paths to see which one is active (has its
019 * Turnouts set) and corresponds to the current direction of the block. There
020 * should be exactly one of these, which will then identify which signal to
021 * monitor.
022 * <p>
023 * Limitations:
024 * <ul>
025 * <li>Current implementation does not protect against changing direction and
026 * backing out of the block
027 * <li>Should track speed at time of stop and restore it on restart (or should
028 * it not restart? Optional restart?)
029 * </ul>
030 *
031 * @author Bob Jacobsen Copyright (C) 2006
032 */
033public class StoppingBlock {
034
035    public StoppingBlock(Block b) {
036        block = b;
037
038        // set a listener in the block
039        block.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
040            @Override
041            public void propertyChange(java.beans.PropertyChangeEvent e) {
042                handleBlockChange(e);
043            }
044        });
045    }
046
047    void handleBlockChange(java.beans.PropertyChangeEvent e) {
048        // check for going occupied
049        if (e.getPropertyName().equals("state") && e.getNewValue().equals(Integer.valueOf(Block.OCCUPIED))) {
050            if (sig1 == null) {
051                return;
052            }
053
054            if (direction != block.getDirection()) {
055                return;  // no interesting
056            }
057            int val = fastestAppearance();
058            if (log.isDebugEnabled()) {
059                log.debug("Block {} occupied with {}", block.getSystemName(), val);
060            }
061
062            if (val == SignalHead.RED) {
063                doStop();
064            }
065            if (val == SignalHead.YELLOW) {
066                doSlow();
067            }
068        }
069    }
070
071    void handleSignalChange(java.beans.PropertyChangeEvent e) {
072        // if currently have a loco present and stopped,
073        // consider changing speed
074        if ((block.getValue() != null) && block.getState() == (Block.OCCUPIED)) {
075
076            if (sig1 == null) {
077                return;
078            }
079
080            if (direction != block.getDirection()) {
081                return;  // not interesting
082            }
083            int val = fastestAppearance();
084            if (log.isDebugEnabled()) {
085                log.debug("Block {} signal change to {}", block.getSystemName(), val);
086            }
087
088            if (val == SignalHead.YELLOW) {
089                doSlow();
090            }
091            if (val == SignalHead.GREEN) {
092                doRestart();
093            }
094        }
095    }
096
097    public void addSignal(SignalHead s, int dir) {
098        sig1 = s;
099        direction = dir;
100
101        sig1.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
102            @Override
103            public void propertyChange(java.beans.PropertyChangeEvent e) {
104                handleSignalChange(e);
105            }
106        });
107    }
108
109    public void addSignal(SignalHead s1, SignalHead s2, int dir) {
110        addSignal(s1, dir);
111        sig2 = s2;
112        sig2.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
113            @Override
114            public void propertyChange(java.beans.PropertyChangeEvent e) {
115                handleSignalChange(e);
116            }
117        });
118    }
119
120    int fastestAppearance() {
121        if (sig1 == null) {
122            log.error("Should not get null in fastestAppearance");
123            return 0;
124        }
125        if (sig2 == null) {
126            return sig1.getAppearance();
127        } else {
128            return Math.max(sig1.getAppearance(), sig2.getAppearance());
129        }
130    }
131
132    /**
133     * Perform the stop operation
134     */
135    void doStop() {
136        if (log.isDebugEnabled()) {
137            log.debug("Block {} speed being set to stop", block.getSystemName());
138        }
139        setSpeed(0.0f, false, false, false);  // bell on
140    }
141
142    void doSlow() {
143        if (log.isDebugEnabled()) {
144            log.debug("Block {} speed being set to slow", block.getSystemName());
145        }
146        setSpeed(slow, false, false, false);  // bell off
147    }
148
149    void doRestart() {
150        if (log.isDebugEnabled()) {
151            log.debug("Block {} speed being set to run", block.getSystemName());
152        }
153        setSpeed(fast, false, false, false);  // bell off
154    }
155
156    void setSpeed(float speed, boolean f1, boolean f2, boolean f3) {
157        Object o = block.getValue();
158        if (o == null) {
159            log.error("Block {} contained no Throttle object", block.getSystemName());
160            return;
161        }
162        try {
163            Throttle t = (Throttle) block.getValue();
164            t.setSpeedSetting(speed);
165            t.setF1(f1);
166            t.setF2(f2);
167        } catch (ClassCastException e) {
168            log.error("Block {} did not contain object of Throttle type: {}", block.getSystemName(), e);
169        }
170    }
171
172    public void setSpeeds(float s, float f) {
173        slow = s;
174        fast = f;
175    }
176
177    // data members
178    Block block;
179    SignalHead sig1;
180    SignalHead sig2;
181    int direction;
182    float slow = 0.3f;
183    float fast = 0.6f;
184
185    private final static Logger log = LoggerFactory.getLogger(StoppingBlock.class);
186
187}