001package jmri.jmrix.mrc; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.util.Date; 005import jmri.DccLocoAddress; 006import jmri.LocoAddress; 007import jmri.jmrix.AbstractThrottle; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * An implementation of DccThrottle with code specific to an MRC connection. 013 * <p> 014 * Addresses of 99 and below are considered short addresses, and over 100 are 015 * considered long addresses. This is not the MRC system standard, but is used 016 * as an expedient here. 017 * <p> 018 * Based on Glen Oberhauser's original LnThrottleManager implementation 019 * 020 * @author Bob Jacobsen Copyright (C) 2001 021 */ 022public class MrcThrottle extends AbstractThrottle implements MrcTrafficListener { 023 024 private MrcTrafficController tc = null; 025 026 /** 027 * Throttle Constructor. 028 * @param memo system connection memo 029 * @param address DCC loco address for throttle 030 */ 031 public MrcThrottle(MrcSystemConnectionMemo memo, DccLocoAddress address) { 032 super(memo); 033 this.tc = memo.getMrcTrafficController(); 034 super.speedStepMode = jmri.SpeedStepMode.NMRA_DCC_128; 035 036 // cache settings. It would be better to read the 037 // actual state, but I don't know how to do this 038 synchronized(this) { 039 this.speedSetting = 0; 040 } 041 // Functions default to false 042 this.address = address; 043 this.isForward = true; 044 if (address.isLongAddress()) { 045 addressLo = address.getNumber(); 046 addressHi = address.getNumber() >> 8; 047 addressHi = addressHi + 0xc0; //We add 0xc0 to the high byte. 048 } else { 049 addressLo = address.getNumber(); 050 } 051 tc.addTrafficListener(MrcInterface.THROTTLEINFO, this); 052 } 053 054 DccLocoAddress address; 055 056 int addressLo = 0x00; 057 int addressHi = 0x00; 058 059 @Override 060 public LocoAddress getLocoAddress() { 061 return address; 062 } 063 064 @Override 065 protected void sendFunctionGroup1() { 066 067 int data = 0x00 068 | (getFunction(0) ? 0x10 : 0) 069 | (getFunction(1) ? 0x01 : 0) 070 | (getFunction(2) ? 0x02 : 0) 071 | (getFunction(3) ? 0x04 : 0) 072 | (getFunction(4) ? 0x08 : 0); 073 074 data = data + 0x80; 075 MrcMessage m = MrcMessage.getSendFunction(1, addressLo, addressHi, data); 076 if (m != null) { 077 tc.sendMrcMessage(m); 078 } 079 } 080 081 /** 082 * Send the message to set the state of functions F5, F6, F7, F8. 083 */ 084 @Override 085 protected void sendFunctionGroup2() { 086 int data = 0x00 087 | (getFunction(8) ? 0x08 : 0) 088 | (getFunction(7) ? 0x04 : 0) 089 | (getFunction(6) ? 0x02 : 0) 090 | (getFunction(5) ? 0x01 : 0); 091 092 data = data + 0xB0; 093 094 MrcMessage m = MrcMessage.getSendFunction(2, addressLo, addressHi, data); 095 if (m != null) { 096 tc.sendMrcMessage(m); 097 } 098 } 099 100 /** 101 * Send the message to set the state of functions F9, F12, F11, F12. 102 */ 103 @Override 104 protected void sendFunctionGroup3() { 105 106 int data = 0x00 107 | (getFunction(9) ? 0x01 : 0) 108 | (getFunction(10) ? 0x02 : 0) 109 | (getFunction(11) ? 0x04 : 0) 110 | (getFunction(12) ? 0x08 : 0); 111 112 data = data + 0xA0; 113 MrcMessage m = MrcMessage.getSendFunction(3, addressLo, addressHi, data); 114 if (m != null) { 115 tc.sendMrcMessage(m); 116 } 117 } 118 119 /** 120 * Send the message to set the state of functions F13 to F20. MRC Group 4 and 5 121 */ 122 @Override 123 protected void sendFunctionGroup4() { 124 int data = 0x00 125 | (getFunction(16) ? 0x08 : 0) 126 | (getFunction(15) ? 0x04 : 0) 127 | (getFunction(14) ? 0x02 : 0) 128 | (getFunction(13) ? 0x01 : 0); 129 130 data = data + 0xD0; 131 132 MrcMessage m = MrcMessage.getSendFunction(4, addressLo, addressHi, data); 133 if (m != null) { 134 tc.sendMrcMessage(m); 135 } 136 137 data = 0x00 138 | (getFunction(20) ? 0x08 : 0) 139 | (getFunction(19) ? 0x04 : 0) 140 | (getFunction(18) ? 0x02 : 0) 141 | (getFunction(17) ? 0x01 : 0); 142 data = data + 0xC0; 143 144 m = MrcMessage.getSendFunction(5, addressLo, addressHi, data); 145 if (m != null) { 146 tc.sendMrcMessage(m); 147 } 148 } 149 150 /** 151 * Send the message to set the state of functions F21 to F28. MRC Group 6 152 */ 153 @Override 154 protected void sendFunctionGroup5() { 155 int data = 0x00 156 | (getFunction(28) ? 0x80 : 0) 157 | (getFunction(27) ? 0x40 : 0) 158 | (getFunction(26) ? 0x20 : 0) 159 | (getFunction(25) ? 0x10 : 0) 160 | (getFunction(24) ? 0x08 : 0) 161 | (getFunction(23) ? 0x04 : 0) 162 | (getFunction(22) ? 0x02 : 0) 163 | (getFunction(21) ? 0x01 : 0); 164 165 MrcMessage m = MrcMessage.getSendFunction(6, addressLo, addressHi, data); 166 if (m != null) { 167 tc.sendMrcMessage(m); 168 } 169 } 170 171 /** 172 * Set the speed and direction. 173 * 174 * @param speed Number from 0 to 1, or less than zero for emergency stop 175 */ 176 @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY") // OK to compare floating point, notify on any change 177 @Override 178 public void setSpeedSetting(float speed) { 179 float oldSpeed; 180 synchronized(this) { 181 oldSpeed = this.speedSetting; 182 this.speedSetting = speed; 183 } 184 MrcMessage m; 185 int value; 186 if (super.speedStepMode == jmri.SpeedStepMode.NMRA_DCC_128) { 187 log.debug("setSpeedSetting= {}", speed); // NOI18N 188 //MRC use a value between 0-127 no matter what the controller is set to 189 value = (int) ((127 - 1) * speed); // -1 for rescale to avoid estop 190 if (value > 0) { 191 value = value + 1; // skip estop 192 } 193 if (value > 127) { 194 value = 127; // max possible speed 195 } 196 if (value < 0) { 197 value = 1; // emergency stop 198 } 199 if (isForward) { 200 value = value + 128; 201 } 202 m = MrcMessage.getSendSpeed128(addressLo, addressHi, value); 203 } else { 204 value = (int) ((28) * speed); // -1 for rescale to avoid estop 205 if (value > 0) { 206 value = value + 3; // skip estop 207 } 208 if (value > 32) { 209 value = 31; // max possible speed 210 } 211 if (value < 0) { 212 value = 2; // emergency stop 213 } 214 m = MrcMessage.getSendSpeed28(addressLo, addressHi, value, isForward); 215 } 216 tc.sendMrcMessage(m); 217 synchronized(this) { 218 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 219 } 220 record(speed); 221 } 222 223 @Override 224 public void setIsForward(boolean forward) { 225 boolean old = isForward; 226 isForward = forward; 227 synchronized(this) { 228 setSpeedSetting(speedSetting); // send the command 229 } 230 log.debug("setIsForward= {}", forward); 231 firePropertyChange(ISFORWARD, old, isForward); 232 } 233 234 @Override 235 public void throttleDispose() { 236 finishRecord(); 237 } 238 239 @Override 240 public String toString() { 241 return getLocoAddress().toString(); 242 } 243 244 //Might need to look at other packets from handsets to see if they also have control of our loco and adjust from that. 245 @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY", justification = "fixed number of possible values") 246 @Override 247 public void notifyRcv(Date timestamp, MrcMessage m) { 248 if (m.getMessageClass() != MrcInterface.THROTTLEINFO 249 || (m.getMessageClass() == MrcInterface.THROTTLEINFO && (m.getElement(0) == MrcPackets.LOCOSOLECONTROLCODE 250 || m.getElement(0) == MrcPackets.LOCODBLCONTROLCODE))) { 251 return; 252 } 253 if (m.getLocoAddress() == address.getNumber()) { 254 if (MrcPackets.startsWith(m, MrcPackets.THROTTLEPACKETHEADER)) { 255 synchronized(this) { 256 if (m.getElement(10) == 0x02) { 257 //128 258 log.debug("speed Packet from another controller for our loco"); 259 int speed = m.getElement(8); 260 if ((m.getElement(8) & 0x80) == 0x80) { 261 //Forward 262 if (!this.isForward) { 263 this.isForward = true; 264 firePropertyChange(ISFORWARD, !isForward, isForward); 265 } 266 //speed = m.getElement(8); 267 } else if (this.isForward) { 268 //reverse 269 this.isForward = false; 270 firePropertyChange(ISFORWARD, !isForward, isForward); 271 //speed = m.getElement(8); 272 } 273 // does this handle emergency stop in any way? 274 speed = (speed & 0x7f) - 1; 275 if (speed < 0) { 276 speed = 0; 277 } 278 float val = speed / 126.0f; 279 280 // next line is the FE_FLOATING_POINT_EQUALITY annotated above 281 if (val != this.speedSetting) { 282 float old = this.speedSetting; 283 this.speedSetting = val; 284 firePropertyChange(SPEEDSETTING, old, this.speedSetting); 285 record(val); 286 } 287 } else if (m.getElement(10) == 0x00) { 288 int value = m.getElement(8) & 0xff; 289 //28 Speed Steps 290 if ((m.getElement(8) & 0x60) == 0x60) { 291 //Forward 292 value = value - 0x60; 293 } else { 294 value = value - 0x40; 295 } 296 if (((value >> 4) & 0x01) == 0x01) { 297 value = value - 0x10; 298 value = (value << 1) + 1; 299 } else { 300 value = value << 1; 301 } 302 value = value - 3; //Turn into user expected 0-28 303 float val = -1; 304 if (value != -1) { 305 if (value < 1) { 306 value = 0; 307 } 308 val = value / 28.0f; 309 } 310 311 if (val != this.speedSetting) { 312 firePropertyChange(SPEEDSETTING, this.speedSetting, val); 313 this.speedSetting = val; 314 record(val); 315 } 316 } 317 } 318 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP1PACKETHEADER)) { 319 int data = m.getElement(8) & 0xff; 320 updateFunction(0,((data & 0x10) == 0x10)); 321 updateFunction(1,((data & 0x01) == 0x01)); 322 updateFunction(2,((data & 0x02) == 0x02)); 323 updateFunction(3,((data & 0x04) == 0x04)); 324 updateFunction(4,((data & 0x08) == 0x08)); 325 326 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP2PACKETHEADER)) { 327 int data = m.getElement(8) & 0xff; 328 updateFunction(5,((data & 0x01) == 0x01)); 329 updateFunction(6,((data & 0x02) == 0x02)); 330 updateFunction(7,((data & 0x04) == 0x04)); 331 updateFunction(8,((data & 0x08) == 0x08)); 332 333 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP3PACKETHEADER)) { 334 int data = m.getElement(8) & 0xff; 335 updateFunction(9,((data & 0x01) == 0x01)); 336 updateFunction(10,((data & 0x02) == 0x02)); 337 updateFunction(11,((data & 0x04) == 0x04)); 338 updateFunction(12,((data & 0x08) == 0x08)); 339 340 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP4PACKETHEADER)) { 341 int data = m.getElement(8) & 0xff; 342 updateFunction(13,((data & 0x01) == 0x01)); 343 updateFunction(14,((data & 0x02) == 0x02)); 344 updateFunction(15,((data & 0x04) == 0x04)); 345 updateFunction(16,((data & 0x08) == 0x08)); 346 347 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP5PACKETHEADER)) { 348 int data = m.getElement(8) & 0xff; 349 updateFunction(17,((data & 0x01) == 0x01)); 350 updateFunction(18,((data & 0x02) == 0x02)); 351 updateFunction(19,((data & 0x04) == 0x04)); 352 updateFunction(20,((data & 0x08) == 0x08)); 353 354 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP6PACKETHEADER)) { 355 int data = m.getElement(8) & 0xff; 356 updateFunction(21,((data & 0x01) == 0x01)); 357 updateFunction(22,((data & 0x02) == 0x02)); 358 updateFunction(23,((data & 0x04) == 0x04)); 359 updateFunction(24,((data & 0x08) == 0x08)); 360 361 updateFunction(25,((data & 0x10) == 0x10)); 362 updateFunction(26,((data & 0x20) == 0x20)); 363 updateFunction(27,((data & 0x40) == 0x40)); 364 updateFunction(28,((data & 0x80) == 0x80)); 365 366 } 367 } 368 } 369 370 @Override 371 public void notifyXmit(Date timestamp, MrcMessage m) {/* message(m); */ 372 373 } 374 375 @Override 376 public void notifyFailedXmit(Date timestamp, MrcMessage m) { /*message(m);*/ } 377 378 // initialize logging 379 private final static Logger log = LoggerFactory.getLogger(MrcThrottle.class); 380 381}