001package jmri.jmrix.powerline.cm11; 002 003import jmri.jmrix.AbstractMRListener; 004import jmri.jmrix.AbstractMRMessage; 005import jmri.jmrix.AbstractMRReply; 006import jmri.jmrix.powerline.SerialListener; 007import jmri.jmrix.powerline.SerialMessage; 008import jmri.jmrix.powerline.SerialSystemConnectionMemo; 009import jmri.jmrix.powerline.SerialTrafficController; 010import jmri.jmrix.powerline.X10Sequence; 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014/** 015 * Converts Stream-based I/O to/from messages. The "SerialInterface" side 016 * sends/receives message objects. 017 * <p> 018 * The connection to a SerialPortController is via a pair of *Streams, which 019 * then carry sequences of characters for transmission. Note that this 020 * processing is handled in an independent thread. 021 * <p> 022 * This maintains a list of nodes, but doesn't currently do anything with it. 023 * 024 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2005, 2006, 2008 Converted to 025 * multiple connection 026 * @author kcameron Copyright (C) 2011 027 */ 028public class SpecificTrafficController extends SerialTrafficController { 029 030 public SpecificTrafficController(SerialSystemConnectionMemo memo) { 031 super(); 032 this.memo = memo; 033 logDebug = log.isDebugEnabled(); 034 035 // not polled at all, so allow unexpected messages, and 036 // use poll delay just to spread out startup 037 setAllowUnexpectedReply(true); 038 mWaitBeforePoll = 1000; // can take a long time to send 039 040 } 041 042 /** 043 * Send a sequence of X10 messages 044 * <p> 045 * Makes them into the local messages and then queues in order 046 */ 047 @Override 048 synchronized public void sendX10Sequence(X10Sequence s, SerialListener l) { 049 s.reset(); 050 X10Sequence.Command c; 051 while ((c = s.getCommand()) != null) { 052 SerialMessage m; 053 if (c.isAddress()) { 054 m = SpecificMessage.getAddress(c.getHouseCode(), ((X10Sequence.Address) c).getAddress()); 055 } else if (c.isFunction()) { 056 X10Sequence.Function f = (X10Sequence.Function) c; 057 if (f.getDimCount() > 0) { 058 m = SpecificMessage.getFunctionDim(f.getHouseCode(), f.getFunction(), f.getDimCount()); 059 } else { 060 m = SpecificMessage.getFunction(f.getHouseCode(), f.getFunction()); 061 } 062 } else { 063 // isn't address or function 064 X10Sequence.ExtData e = (X10Sequence.ExtData) c; 065 m = SpecificMessage.getExtCmd(c.getHouseCode(), e.getAddress(), e.getExtCmd(), e.getExtData()); 066 } 067 sendSerialMessage(m, l); 068 } 069 } 070 071 /** 072 * This system provides 22 dim steps 073 */ 074 @Override 075 public int getNumberOfIntensitySteps() { 076 return 63; 077 } 078 079 /** 080 * Get a message of a specific length for filling in. 081 */ 082 @Override 083 public SerialMessage getSerialMessage(int length) { 084 return new SpecificMessage(length); 085 } 086 087 @Override 088 protected void forwardToPort(AbstractMRMessage m, AbstractMRListener reply) { 089 if (logDebug) { 090 log.debug("forward {}", m); 091 } 092 sendInterlock = ((SerialMessage) m).getInterlocked(); 093 super.forwardToPort(m, reply); 094 } 095 096 /** 097 * Specific class override of the Serial class 098 */ 099 @Override 100 protected AbstractMRReply newReply() { 101 SpecificReply reply = new SpecificReply(memo.getTrafficController()); 102 return reply; 103 } 104 105 boolean sendInterlock = false; // send the 00 interlock when CRC received 106 boolean expectLength = false; // next byte is length of read 107 boolean countingBytes = false; // counting remainingBytes into reply buffer 108 int remainingBytes = 0; // count of bytes _left_ 109 110 /** 111 * Specific class override of the Serial class 112 */ 113 @Override 114 public boolean endOfMessage(AbstractMRReply msg) { 115 // check if this byte is length 116 if (expectLength) { 117 expectLength = false; 118 countingBytes = true; 119 remainingBytes = msg.getElement(1) & 0xF; // 0 was the read command; max 9, really 120 if (logDebug) { 121 log.debug("Receive count set to {}", remainingBytes); 122 } 123 return false; 124 } 125 if (remainingBytes > 0) { 126 if (remainingBytes > 8) { 127 log.error("Invalid remainingBytes: {}", remainingBytes); 128 remainingBytes = 0; 129 return true; 130 } 131 remainingBytes--; 132 if (remainingBytes == 0) { 133 countingBytes = false; 134 return true; // done 135 } 136 return false; // wait for one more 137 } 138 // check for data available 139 if ((msg.getElement(0) & 0xFF) == Constants.POLL_REQ) { 140 // get message 141 SerialMessage m = new SpecificMessage(1); 142 m.setElement(0, Constants.POLL_ACK); 143 expectLength = true; // next byte is length 144 forwardToPort(m, null); 145 return false; // reply message will get data appended 146 } 147 // check for request time 148 if (((msg.getElement(0) & 0xFF) == Constants.TIME_REQ_CP10) || ((msg.getElement(0) & 0xFF) == Constants.TIME_REQ_CP11)) { 149 SerialMessage m = SpecificMessage.setCM11Time(X10Sequence.encode(1)); 150 forwardToPort(m, null); 151 return true; // message done 152 } 153 // check for reporting macro trigger 154 if ((msg.getElement(0) & 0xFF) == 0x5B) { 155 if (msg.getNumDataElements() >= 3) { 156 return true; 157 } else { 158 return false; // waiting for high-low addr 159 } 160 } 161 // if the interlock is present, send it 162 if (sendInterlock) { 163 if (logDebug) { 164 log.debug("Send interlock"); 165 } 166 sendInterlock = false; 167 SerialMessage m = new SpecificMessage(1); 168 m.setElement(0, 0); // not really needed, but this is a slow protocol anyway 169 forwardToPort(m, null); 170 return false; // just leave in buffer 171 } 172 if (logDebug) { 173 log.debug("end of message: {}", msg); 174 } 175 return true; 176 } 177 178 private final static Logger log = LoggerFactory.getLogger(SpecificTrafficController.class); 179 180} 181