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