001package jmri.jmrix.roco.z21; 002 003import jmri.jmrix.lenz.XNetMessage; 004import jmri.jmrix.lenz.XNetReply; 005import jmri.jmrix.lenz.XNetTrafficController; 006import jmri.jmrix.lenz.XNetTurnout; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Extend jmri.jmrix.lenz.XNetTurnout for Roco Z21/z21 systems. 012 * 013 * @author Paul Bender Copyright (C) 2016 014 */ 015public class Z21XNetTurnout extends XNetTurnout { 016 017 public Z21XNetTurnout(String prefix, int pNumber, XNetTrafficController controller) { 018 super(prefix,pNumber,controller); 019 } 020 021 /** 022 * {@inheritDoc} 023 * Sends an XpressNet command. 024 */ 025 @Override 026 protected synchronized void forwardCommandChangeToLayout(int s) { 027 if (s != _mClosed && s != _mThrown) { 028 log.warn("Turnout {}: state {} not forwarded to layout.", mNumber, s); 029 return; 030 } 031 log.debug("Turnout {}: forwarding state {} to layout.", mNumber, s); 032 // get the right packet 033 XNetMessage msg = Z21XNetMessage.getZ21SetTurnoutRequestMessage(mNumber, 034 (s & _mThrown) != 0, 035 true, false ); // for now always active and not queued. 036 if (getFeedbackMode() == SIGNAL) { 037 msg.setTimeout(0); // Set the timeout to 0, so the off message can 038 // be sent immediately. 039 // leave the next line commented out for now. 040 // it may be enabled later to allow SIGNAL mode to ignore 041 // directed replies, which lets the traffic controller move on 042 // to the next message without waiting. 043 //msg.setBroadcastReply(); 044 tc.sendXNetMessage(msg, null); 045 sendOffMessage(s); 046 } else { 047 queueMessage(msg,COMMANDSENT,this); 048 } 049 } 050 051 /** 052 * Request an update on status by sending an XpressNet message. 053 */ 054 @Override 055 public void requestUpdateFromLayout() { 056 log.debug("Turnout {} requesting update from layout",mNumber); 057 // This will handle ONESENSOR and TWOSENSOR feedback modes. 058 super.requestUpdateFromLayout(); 059 060 // On the z21, we send a LAN_X_GET_TURNOUT_INFO message 061 // (see section 5.1 of the protocol documenation ). 062 XNetMessage msg = Z21XNetMessage.getZ21TurnoutInfoRequestMessage(mNumber); 063 msg.setBroadcastReply(); 064 queueMessage(msg,IDLE,null); //status is returned via the manager. 065 } 066 067 // Handle a timeout notification. 068 @Override 069 public synchronized void notifyTimeout(XNetMessage msg) { 070 log.debug("Notified of timeout on message {}",msg); 071 // If we're in the OFFSENT state, we need to send another OFF message. 072 synchronized (this) { 073 if (internalState == OFFSENT) { 074 sendOffMessage(getCommandedState()); 075 } 076 } 077 } 078 079 /** 080 * initmessage is a package proteceted class which allows the Manger to send 081 * a feedback message at initialization without changing the state of the 082 * turnout with respect to whether or not a feedback request was sent. 083 * This is used only when the turnout is created by on layout feedback. 084 * @param l Init message 085 */ 086 synchronized void initMessageZ21(XNetReply l) { 087 int oldState = internalState; 088 message(l); 089 internalState = oldState; 090 } 091 092 @Override 093 public synchronized void message(XNetReply l) { 094 log.debug("received message: {}",l); 095 if (l.getElement(0)==Z21Constants.LAN_X_TURNOUT_INFO) { 096 // bytes 2 and 3 are the address. 097 int address = (l.getElement(1) << 8) + l.getElement(2); 098 // the address sent byte the Z21 is one less than what JMRI's 099 // XpressNet code (and Lenz systems) expect. 100 address = address + 1; 101 if(log.isDebugEnabled()) { 102 log.debug("message has address: {}",address); 103 } 104 // if this is for this turnout, check the turnout state. 105 if(mNumber==address) { 106 int messageState = decodeZ21FeedbackMessageState(l); 107 if(getFeedbackMode() == MONITORING ) { 108 newKnownState(messageState); 109 } else if(getFeedbackMode()==DIRECT) { 110 newKnownState(getCommandedState()); 111 } 112 if(internalState == COMMANDSENT) { 113 /* the command was successfully received */ 114 sendOffMessage(messageState); // turn off the repetition on the track. 115 // and check to see if there are any more queued messages. 116 sendQueuedMessage(); 117 } 118 } 119 } else { 120 super.message(l); // the XpressNetTurnout code 121 // handles any other replies. 122 } 123 } 124 125 private int decodeZ21FeedbackMessageState(XNetReply l){ 126 int state; 127 switch (l.getElement(3)) { 128 case 0x03: 129 state = INCONSISTENT; 130 break; 131 case 0x02: 132 state = _inverted ? CLOSED : THROWN; 133 break; 134 case 0x01: 135 state = _inverted ? THROWN : CLOSED; 136 break; 137 case 0x00: 138 default: 139 state = UNKNOWN; 140 } 141 return state; 142 } 143 144 @Override 145 protected synchronized void sendOffMessage() { 146 sendOffMessage(getCommandedState()); 147 } 148 149 @SuppressWarnings("deprecation") // The method getId() from the type Thread is deprecated since version 19 150 // The replacement Thread.threadId() isn't available before version 19 151 protected synchronized void sendOffMessage(int state) { 152 // We need to tell the turnout to shut off the output. 153 if (log.isDebugEnabled()) { 154 log.debug("Sending off message for turnout {} commanded state={}", mNumber, getCommandedState()); 155 log.debug("Current Thread ID: {} Thread Name {}", java.lang.Thread.currentThread().getId(), java.lang.Thread.currentThread().getName()); 156 } 157 XNetMessage msg = getOffMessage(state == _mThrown); 158 // Set the known state to the commanded state. 159 newKnownState(getCommandedState()); 160 internalState = IDLE; 161 // Then send the message. 162 tc.sendXNetMessage(msg, this); // reply sent through loconet 163 } 164 165 protected synchronized XNetMessage getOffMessage(boolean state) { 166 XNetMessage msg = Z21XNetMessage.getZ21SetTurnoutRequestMessage(mNumber, 167 state, false, false );// for now always not active and not queued. 168 msg.setBroadcastReply(); // reply comes through loconet 169 return msg; 170 } 171 172 private static final Logger log = LoggerFactory.getLogger(Z21XNetTurnout.class); 173 174}