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