001package jmri.jmrix.roco.z21; 002 003import jmri.LocoAddress; 004import jmri.jmrix.lenz.XNetMessage; 005import jmri.jmrix.lenz.XNetReply; 006import jmri.jmrix.lenz.XNetSystemConnectionMemo; 007import jmri.jmrix.lenz.XNetTrafficController; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * An implementation of DccThrottle with code specific to a z21 XpressNet 013 * connection. 014 * 015 * @author Paul Bender (C) 2015 016 */ 017public class Z21XNetThrottle extends jmri.jmrix.roco.RocoXNetThrottle { 018 019 /** 020 * Constructor. 021 * @param memo system connection. 022 * @param controller traffic controller. 023 */ 024 public Z21XNetThrottle(XNetSystemConnectionMemo memo, XNetTrafficController controller) { 025 super(memo,controller); 026 } 027 028 /** 029 * Constructor. 030 * @param memo system connection. 031 * @param address loco address. 032 * @param controller traffic controller. 033 */ 034 public Z21XNetThrottle(XNetSystemConnectionMemo memo, LocoAddress address, XNetTrafficController controller) { 035 super(memo,address,controller); 036 } 037 038 /** 039 * {@inheritDoc} 040 */ 041 @Override 042 synchronized public void setSpeedSetting(float speed) { 043 log.debug("set Speed to: {} Current step mode is: {}",speed,this.speedStepMode); 044 synchronized(this) { 045 this.speedSetting = speed; 046 } 047 record(speed); 048 if (speed < 0) { 049 /* we're sending an emergency stop to this locomotive only */ 050 sendEmergencyStop(); 051 } else { 052 if (speed > 1) { 053 speed = (float) 1.0; 054 } 055 /* we're sending a speed to the locomotive */ 056 XNetMessage msg = Z21XNetMessage.getZ21LanXSetLocoDriveMsg(getDccAddress(), 057 this.speedStepMode, 058 speed, 059 this.isForward); 060 // now, queue the message for sending to the command station 061 queueMessage(msg, THROTTLEIDLE); 062 } 063 } 064 065 /** 066 * {@inheritDoc} 067 */ 068 @Override 069 public void setSpeedSetting(float speed, boolean allowDuplicates, boolean allowDuplicatesOnStop) { 070 synchronized(this) { 071 this.speedSetting = speed; 072 } 073 record(speed); 074 } 075 076 /** 077 * Send a request to get the speed, direction and function status from 078 * the command station. 079 */ 080 @Override 081 synchronized protected void sendStatusInformationRequest() { 082 /* Send the request for status */ 083 XNetMessage msg = Z21XNetMessage.getZ21LocomotiveInfoRequestMsg(this.address); 084 msg.setRetries(1); // Since we repeat this ourselves, don't ask the 085 // traffic controller to do this for us. 086 // now, we queue the message for sending to the command station 087 queueMessage(msg, THROTTLESTATSENT); 088 } 089 090 /** 091 * Send the XpressNet messages to set the state of locomotive direction and 092 * functions F0, F1, F2, F3, F4. 093 */ 094 @Override 095 protected void sendFunctionGroup1() { 096 // because of the way the z21 wants to see the functions, we 097 // send all the functions when there is a change in the group. 098 XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),0,getFunction(0)); 099 // now, queue the message for sending to the command station 100 queueMessage(msg, THROTTLEIDLE); 101 XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),1,getFunction(1)); 102 // now, queue the message for sending to the command station 103 queueMessage(msg1, THROTTLEIDLE); 104 XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),2,getFunction(2)); 105 // now, queue the message for sending to the command station 106 queueMessage(msg2, THROTTLEIDLE); 107 XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),3,getFunction(3)); 108 // now, queue the message for sending to the command station 109 queueMessage(msg3, THROTTLEIDLE); 110 XNetMessage msg4 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),4,getFunction(4)); 111 // now, queue the message for sending to the command station 112 queueMessage(msg4, THROTTLEIDLE); 113 } 114 115 /** 116 * Send the XpressNet message to set the state of functions F5, F6, F7, F8. 117 */ 118 @Override 119 protected void sendFunctionGroup2() { 120 // because of the way the z21 wants to see the functions, we 121 // send all the functions when there is a change in the group. 122 XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),5,getFunction(5)); 123 // now, queue the message for sending to the command station 124 queueMessage(msg, THROTTLEIDLE); 125 XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),6,getFunction(6)); 126 // now, queue the message for sending to the command station 127 queueMessage(msg1, THROTTLEIDLE); 128 XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),7,getFunction(7)); 129 // now, queue the message for sending to the command station 130 queueMessage(msg2, THROTTLEIDLE); 131 XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),8,getFunction(8)); 132 // now, queue the message for sending to the command station 133 queueMessage(msg3, THROTTLEIDLE); 134 } 135 136 /** 137 * Send the XpressNet message to set the state of functions F9, F10, F11, 138 * F12. 139 */ 140 @Override 141 protected void sendFunctionGroup3() { 142 // because of the way the z21 wants to see the functions, we 143 // send all the functions when there is a change in the group. 144 XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),9,getFunction(9)); 145 // now, queue the message for sending to the command station 146 queueMessage(msg, THROTTLEIDLE); 147 XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),10,getFunction(10)); 148 // now, queue the message for sending to the command station 149 queueMessage(msg1, THROTTLEIDLE); 150 XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),11,getFunction(11)); 151 // now, queue the message for sending to the command station 152 queueMessage(msg2, THROTTLEIDLE); 153 XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),12,getFunction(12)); 154 // now, queue the message for sending to the command station 155 queueMessage(msg3, THROTTLEIDLE); 156 } 157 158 /** 159 * Send the XpressNet message to set the state of functions F13, F14, F15, 160 * F16, F17, F18, F19, F20. 161 */ 162 @Override 163 protected void sendFunctionGroup4() { 164 // because of the way the z21 wants to see the functions, we 165 // send all the functions when there is a change in the group. 166 XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),13,getFunction(13)); 167 // now, queue the message for sending to the command station 168 queueMessage(msg, THROTTLEIDLE); 169 XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),14,getFunction(14)); 170 // now, queue the message for sending to the command station 171 queueMessage(msg1, THROTTLEIDLE); 172 XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),15,getFunction(15)); 173 // now, queue the message for sending to the command station 174 queueMessage(msg2, THROTTLEIDLE); 175 XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),16,getFunction(16)); 176 // now, queue the message for sending to the command station 177 queueMessage(msg3, THROTTLEIDLE); 178 XNetMessage msg4 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),17,getFunction(17)); 179 // now, queue the message for sending to the command station 180 queueMessage(msg4, THROTTLEIDLE); 181 XNetMessage msg5 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),18,getFunction(18)); 182 // now, queue the message for sending to the command station 183 queueMessage(msg5, THROTTLEIDLE); 184 XNetMessage msg6 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),19,getFunction(19)); 185 // now, queue the message for sending to the command station 186 queueMessage(msg6, THROTTLEIDLE); 187 XNetMessage msg7 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),20,getFunction(20)); 188 // now, queue the message for sending to the command station 189 queueMessage(msg7, THROTTLEIDLE); 190 } 191 /** 192 * Send the XpressNet message to set the state of functions F21, F22, F23, 193 * F24, F25, F26, F27, F28. 194 */ 195 @Override 196 protected void sendFunctionGroup5() { 197 // because of the way the z21 wants to see the functions, we 198 // send all the functions when there is a change in the group. 199 XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),21,getFunction(21)); 200 // now, queue the message for sending to the command station 201 queueMessage(msg, THROTTLEIDLE); 202 XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),22,getFunction(22)); 203 // now, queue the message for sending to the command station 204 queueMessage(msg1, THROTTLEIDLE); 205 XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),23,getFunction(23)); 206 // now, queue the message for sending to the command station 207 queueMessage(msg2, THROTTLEIDLE); 208 XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),24,getFunction(24)); 209 // now, queue the message for sending to the command station 210 queueMessage(msg3, THROTTLEIDLE); 211 XNetMessage msg4 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),25,getFunction(25)); 212 // now, queue the message for sending to the command station 213 queueMessage(msg4, THROTTLEIDLE); 214 XNetMessage msg5 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),26,getFunction(26)); 215 // now, queue the message for sending to the command station 216 queueMessage(msg5, THROTTLEIDLE); 217 XNetMessage msg6 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),27,getFunction(27)); 218 // now, queue the message for sending to the command station 219 queueMessage(msg6, THROTTLEIDLE); 220 XNetMessage msg7 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),28,getFunction(28)); 221 // now, queue the message for sending to the command station 222 queueMessage(msg7, THROTTLEIDLE); 223 } 224 225 // The Roco doesn't support the XpressNet directed emergency stop 226 // instruction, so override sendEmergencyStop in the parent, and 227 // just send speed step 0. 228 @Override 229 protected void sendEmergencyStop(){ 230 setSpeedSetting(0); 231 } 232 233 // Handle incoming messages for this throttle. 234 @Override 235 public void message(XNetReply l) { 236 log.debug("Throttle {} - received message {}",getDccAddress(),l.toString()); 237 if((l.getElement(0)&0xE0)==0xE0 && ((l.getElement(0)&0x0f) >= 7 && (l.getElement(0)&0x0f) <=15 )){ 238 //This is a Roco specific throttle information message. 239 //Data Byte 0 and 1 contain the locomotive address 240 int messageaddress=((l.getElement(1)&0x3F) << 8)+l.getElement(2); 241 if(messageaddress==getDccAddress()){ 242 //The message is for this throttle. 243 int b2= l.getElement(3)&0xff; 244 int b3= l.getElement(4)&0xff; 245 int b4= l.getElement(5)&0xff; 246 int b5= l.getElement(6)&0xff; 247 int b6= l.getElement(7)&0xff; 248 int b7= l.getElement(8)&0xff; 249 // byte 2 contains the speed step mode and availability 250 // information. 251 parseSpeedAndAvailability(b2); 252 // byte 3 contains the direction and the speed information 253 parseSpeedAndDirection(b3); 254 // byte 4 contains flags for whether or not the locomotive 255 // is in a double header and for smart search. These aren't used 256 // here. 257 258 // byte 4 and 5 contain function information for F0-F12 259 parseFunctionInformation(b4,b5); 260 // byte 6 and 7 contain function information for F13-F28 261 parseFunctionHighInformation(b6,b7); 262 263 // set the request state to idle 264 requestState = THROTTLEIDLE; 265 // and send any queued messages. 266 sendQueuedMessage(); 267 } 268 } else { 269 // let the standard XpressNet Throttle have a chance to look 270 // at the message. 271 super.message(l); 272 } 273 } 274 275 /** 276 * Dispose when finished with this object. After this, further usage of this 277 * Throttle object will result in a JmriException. 278 * <p> 279 * This is quite problematic, because a using object doesn't know when it's 280 * the last user. 281 */ 282 @Override 283 public void throttleDispose() { 284 active = false; 285 stopStatusTimer(); 286 finishRecord(); 287 } 288 289 // register for notification 290 private final static Logger log = LoggerFactory.getLogger(Z21XNetThrottle.class); 291 292}