001package jmri.jmrix.dccpp;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Implement a feedback message cache for DCC++ turnouts.
008 *
009 * @author Paul Bender Copyright (C) 2012
010 * @author Mark Underwood Copyright (C) 2015
011  *
012 * Based on XNetFeedbackMessageCache by Paul Bender
013 */
014public class DCCppTurnoutReplyCache implements DCCppListener {
015
016    protected DCCppTrafficController tc = null;
017
018    private DCCppReply[] messageCache; // an to hold each of the 512 possible
019    // reply messages for the turnouts.
020
021    private Boolean[] messagePending; // hold pending status for each of
022    // the possible status request messages.
023
024    // ctor has to register for DCCpp events
025    public DCCppTurnoutReplyCache(DCCppTrafficController controller) {
026 // TODO: This is likely to be a sparse table. Consider refactoring as
027 // a list or something more memory efficient.
028        messageCache = new DCCppReply[DCCppConstants.MAX_TURNOUT_ADDRESS];
029        for (int i = 0; i < DCCppConstants.MAX_TURNOUT_ADDRESS; i++) {
030            messageCache[i] = null;
031        }
032        messagePending = new Boolean[DCCppConstants.MAX_TURNOUT_ADDRESS];
033        for (int i = 0; i < DCCppConstants.MAX_TURNOUT_ADDRESS; i++) {
034            messagePending[i] = false;
035        }
036        tc = controller;
037        tc.addDCCppListener(DCCppInterface.FEEDBACK, this);
038    }
039
040    // requestCachedStateFromLayout
041    // provide any cached state to the turnout.  Otherwise, call the turnout's 
042    // requestUpdateFromLayout() method.
043    // @param turnout  the DCCppTurnout object we are requesting data for.
044    synchronized public void requestCachedStateFromLayout(DCCppTurnout turnout) {
045        int pNumber = turnout.getNumber();
046        if (messagePending[pNumber]) {
047            return;
048        }
049        try {
050            if (messageCache[pNumber] != null) {
051                log.debug("Message for turnout {} cached.", pNumber);
052                turnout.message(messageCache[pNumber]);
053            } else {
054  // TODO: Make sure this doesn't break under a no-feedback model.
055                messagePending[pNumber] = true;
056                turnout.requestUpdateFromLayout(); // this does nothing. 
057            }
058        } catch (java.lang.NullPointerException npe) {
059     // TODO: Make sure this doesn't break under a no-feedback model.
060            messagePending[pNumber] = true;
061            turnout.requestUpdateFromLayout();
062        }
063    }
064
065    // requestCachedStateFromLayout
066    // provide any cached state a sensor.  Otherwise, call the sensor's 
067    // requestUpdateFromLayout() method.
068    // @param sensor  the DCCppSensor object we are requesting data for.
069    //
070    // TODO: We don't have DCCppSensors yet. May never have them.
071    /*
072    synchronized public void requestCachedStateFromLayout(DCCppSensor sensor) {
073        int pNumber = sensor.getNumber();
074        if (messagePending[sensor.getBaseAddress()][sensor.getNibble() >> 4]) {
075            return;
076        }
077        try {
078            if (messageCache[sensor.getBaseAddress()][sensor.getNibble() >> 4] != null) {
079                if (log.isDebugEnabled()) {
080                    log.debug("Message for sensor " + pNumber + " cached.");
081                }
082                sensor.message(messageCache[sensor.getBaseAddress()][sensor.getNibble() >> 4]);
083            } else {
084                messagePending[sensor.getBaseAddress()][sensor.getNibble() >> 4] = true;
085                sensor.requestUpdateFromLayout();
086            }
087        } catch (java.lang.NullPointerException npe) {
088            messagePending[sensor.getBaseAddress()][sensor.getNibble() >> 4] = true;
089            sensor.requestUpdateFromLayout();
090        }
091    }
092    */
093
094    // listen for turnouts, creating them as needed
095    @Override
096    synchronized public void message(DCCppReply l) {
097        if (l.isTurnoutReply()) {
098            log.debug("received message: {}", l);
099            // cache the message for later requests
100            messageCache[l.getTOIDInt()] = l;
101            messagePending[l.getTOIDInt()] = false;
102        }
103    }
104
105    // Listen for the outgoing messages (to the command station)
106    @Override
107    public void message(DCCppMessage l) {
108    }
109
110    // Handle a timeout notification
111    @Override
112    public void notifyTimeout(DCCppMessage msg) {
113        log.debug("Notified of timeout on message '{}'", msg);
114    }
115
116    private final static Logger log = LoggerFactory.getLogger(DCCppTurnoutReplyCache.class);
117
118}
119
120