001/**
002 * Concrete subclass of TurnoutOperator for a turnout that has sensor feedback.
003 *
004 * @author John Harper Copyright 2005
005 */
006package jmri.implementation;
007
008import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
009import java.beans.PropertyChangeEvent;
010import java.beans.PropertyChangeListener;
011import jmri.TurnoutOperator;
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015public class SensorTurnoutOperator extends TurnoutOperator {
016
017    long interval;
018    int maxTries;
019    int tries = 0;
020    PropertyChangeListener listener;
021
022    public SensorTurnoutOperator(AbstractTurnout t, long i, int mt) {
023        super(t);
024        interval = i;
025        maxTries = mt;
026    }
027
028    /**
029     * Do the autmation for a turnout with sensor feedback. Keep trying up to
030     * maxTries until the sensor tells us the change has actually happened. Note
031     * the call to operatorCheck each time we're about to actually do something
032     * - if we're no longer the current operator this throws
033     * TurnoutOperatorException which just terminates the thread.
034     */
035    @Override
036    public void run() {
037        //long startTime = System.currentTimeMillis();
038        listener = new PropertyChangeListener() {
039            @SuppressFBWarnings(value = "NN_NAKED_NOTIFY",
040                    justification = "notify not naked, outside sensor and turnout is shared state")
041            @Override
042            public void propertyChange(PropertyChangeEvent e) {
043                if (e.getPropertyName().equals("KnownState")) {
044                    synchronized (this) {
045                        this.notify();
046                    }
047                }
048            }
049        };
050        myTurnout.addPropertyChangeListener(listener);
051        try {
052            operatorCheck();
053            myTurnout.forwardCommandChangeToLayout();
054            while (++tries < maxTries) {
055                long nextTry = System.currentTimeMillis() + interval;
056                long remaining;
057                while ((remaining = nextTry - System.currentTimeMillis()) > 0) {
058                    try {
059                        synchronized (this) {
060                            wait(remaining);
061                        }
062                    } catch (InterruptedException e) {
063                        Thread.currentThread().interrupt(); // retain if needed later
064                    }
065                }
066                if (myTurnout.isConsistentState()) {
067                    break;
068                }
069                operatorCheck();
070                myTurnout.forwardCommandChangeToLayout();
071                log.warn("retrying {}, try #{}", myTurnout.getSystemName(), tries + 1);
072            }
073            if (!myTurnout.isConsistentState()) {
074                log.warn("failed to throw {}", myTurnout.getSystemName());
075            }
076        } catch (TurnoutOperatorException e) {
077        }
078        myTurnout.removePropertyChangeListener(listener);
079    }
080
081    private final static Logger log = LoggerFactory.getLogger(SensorTurnoutOperator.class);
082}