001package jmri.jmrix.lenz;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Implement a feedback message cache for XpressNet sensors and turnouts.
008 *
009 * @author Paul Bender Copyright (C) 2012
010 */
011public class XNetFeedbackMessageCache implements XNetListener {
012
013    protected XNetTrafficController tc;
014
015    private final XNetReply[] messageCache = new XNetReply[512]; // an to hold each of the 512 possible
016    // reply messages for the turnouts.
017
018    private final byte[] messagePending = new byte[512 / 8]; // hold pending status for each of
019    // the possible status request messages (bitfield)
020
021    // ctor has to register for XNet events
022    public XNetFeedbackMessageCache(XNetTrafficController controller) {
023        tc = controller;
024        tc.addXNetListener(XNetInterface.FEEDBACK, this);
025    }
026
027    // requestCachedStateFromLayout
028    // provide any cached state to the turnout.  Otherwise, call the turnout's 
029    // requestUpdateFromLayout() method.
030    // @param turnout  the XNetTurnout object we are requesting data for.
031    public void requestCachedStateFromLayout(XNetTurnout turnout) {
032        int pNumber = turnout.getNumber();
033        log.debug("asking for cached feedback for turnout {}.",pNumber);
034        pNumber--;
035        if (requestCachedState(2, pNumber, turnout)) {
036            turnout.requestUpdateFromLayout();
037        }
038    }
039
040    /**
041     * Provide any cached state a sensor. Otherwise, call the sensor's
042     * requestUpdateFromLayout() method.
043     *
044     * @param sensor the XNetSensor object we are requesting data for
045     */
046    public synchronized void requestCachedStateFromLayout(XNetSensor sensor) {
047        int pNumber = sensor.getNumber();
048        log.debug("asking for cached feedback for sensor {}.",pNumber);
049        pNumber--;
050        if (requestCachedState(4, pNumber, sensor)) {
051            sensor.requestUpdateFromLayout();
052        }
053    }
054    
055    private boolean requestCachedState(int statesPerNibble, int pNumber, XNetListener target) {
056        int replyIndex = pNumber / statesPerNibble;
057        int bitIdx = replyIndex / 8;
058        int bitMask = pNumber % 8;
059        XNetReply cached;
060        
061        // do not extend the lock to code execution:
062        synchronized (this) {
063            if ((messagePending[bitIdx] & (1 << (bitMask))) > 0) {
064                return false;
065            }
066             cached = messageCache[replyIndex];
067             if (cached == null) {
068                messagePending[bitIdx] |= (1 << bitMask);
069             }
070        }
071        if (cached != null) {
072            target.message(cached);
073            return false;
074        } else {
075            return true;
076        }
077    }
078
079    /**
080     * Listen for turnouts, creating them as needed.
081     */
082    @Override
083    public synchronized void message(XNetReply l) {
084        if (log.isDebugEnabled()) {
085            log.debug("received message: {}",l);
086        }
087        if (!l.isFeedbackBroadcastMessage()) {
088            return;
089        }
090        int numDataBytes = l.getElement(0) & 0x0f;
091        for (int i = 1; i < numDataBytes; i += 2) {
092            // cache the message for later requests
093            int nibbleIndex = l.getElement(i) * 2 + (l.getElement(i + 1) & 0x10) >> 4;
094            messageCache[nibbleIndex] = l;
095        }
096    }
097
098    /**
099     * Listen for the messages to the LI100/LI101.
100     */
101    @Override
102    public void message(XNetMessage l) {
103        // outgoing messages are not currently used
104    }
105
106    /**
107     * Handle a timeout notification.
108     */
109    @Override
110    public void notifyTimeout(XNetMessage msg) {
111        if (log.isDebugEnabled()) {
112            log.debug("Notified of timeout on message {}",msg);
113        }
114    }
115
116    private static final Logger log = LoggerFactory.getLogger(XNetFeedbackMessageCache.class);
117
118}
119
120