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 @Override 177 public void setSpeedSetting(float speed) { 178 float oldSpeed; 179 synchronized(this) { 180 oldSpeed = this.speedSetting; 181 this.speedSetting = speed; 182 } 183 MrcMessage m; 184 int value; 185 if (super.speedStepMode == jmri.SpeedStepMode.NMRA_DCC_128) { 186 log.debug("setSpeedSetting= {}", speed); // NOI18N 187 //MRC use a value between 0-127 no matter what the controller is set to 188 value = (int) ((127 - 1) * speed); // -1 for rescale to avoid estop 189 if (value > 0) { 190 value = value + 1; // skip estop 191 } 192 if (value > 127) { 193 value = 127; // max possible speed 194 } 195 if (value < 0) { 196 value = 1; // emergency stop 197 } 198 if (isForward) { 199 value = value + 128; 200 } 201 m = MrcMessage.getSendSpeed128(addressLo, addressHi, value); 202 } else { 203 value = (int) ((28) * speed); // -1 for rescale to avoid estop 204 if (value > 0) { 205 value = value + 3; // skip estop 206 } 207 if (value > 32) { 208 value = 31; // max possible speed 209 } 210 if (value < 0) { 211 value = 2; // emergency stop 212 } 213 m = MrcMessage.getSendSpeed28(addressLo, addressHi, value, isForward); 214 } 215 tc.sendMrcMessage(m); 216 synchronized(this) { 217 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 218 } 219 record(speed); 220 } 221 222 @Override 223 public void setIsForward(boolean forward) { 224 boolean old = isForward; 225 isForward = forward; 226 synchronized(this) { 227 setSpeedSetting(speedSetting); // send the command 228 } 229 log.debug("setIsForward= {}", forward); 230 firePropertyChange(ISFORWARD, old, isForward); 231 } 232 233 @Override 234 public void throttleDispose() { 235 finishRecord(); 236 } 237 238 @Override 239 public String toString() { 240 return getLocoAddress().toString(); 241 } 242 243 //Might need to look at other packets from handsets to see if they also have control of our loco and adjust from that. 244 @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY", justification = "fixed number of possible values") 245 @Override 246 public void notifyRcv(Date timestamp, MrcMessage m) { 247 if (m.getMessageClass() != MrcInterface.THROTTLEINFO 248 || (m.getMessageClass() == MrcInterface.THROTTLEINFO && (m.getElement(0) == MrcPackets.LOCOSOLECONTROLCODE 249 || m.getElement(0) == MrcPackets.LOCODBLCONTROLCODE))) { 250 return; 251 } 252 if (m.getLocoAddress() == address.getNumber()) { 253 if (MrcPackets.startsWith(m, MrcPackets.THROTTLEPACKETHEADER)) { 254 synchronized(this) { 255 if (m.getElement(10) == 0x02) { 256 //128 257 log.debug("speed Packet from another controller for our loco"); 258 int speed = m.getElement(8); 259 if ((m.getElement(8) & 0x80) == 0x80) { 260 //Forward 261 if (!this.isForward) { 262 this.isForward = true; 263 firePropertyChange(ISFORWARD, !isForward, isForward); 264 } 265 //speed = m.getElement(8); 266 } else if (this.isForward) { 267 //reverse 268 this.isForward = false; 269 firePropertyChange(ISFORWARD, !isForward, isForward); 270 //speed = m.getElement(8); 271 } 272 // does this handle emergency stop in any way? 273 speed = (speed & 0x7f) - 1; 274 if (speed < 0) { 275 speed = 0; 276 } 277 float val = speed / 126.0f; 278 279 // next line is the FE_FLOATING_POINT_EQUALITY annotated above 280 if (val != this.speedSetting) { 281 float old = this.speedSetting; 282 this.speedSetting = val; 283 firePropertyChange(SPEEDSETTING, old, this.speedSetting); 284 record(val); 285 } 286 } else if (m.getElement(10) == 0x00) { 287 int value = m.getElement(8) & 0xff; 288 //28 Speed Steps 289 if ((m.getElement(8) & 0x60) == 0x60) { 290 //Forward 291 value = value - 0x60; 292 } else { 293 value = value - 0x40; 294 } 295 if (((value >> 4) & 0x01) == 0x01) { 296 value = value - 0x10; 297 value = (value << 1) + 1; 298 } else { 299 value = value << 1; 300 } 301 value = value - 3; //Turn into user expected 0-28 302 float val = -1; 303 if (value != -1) { 304 if (value < 1) { 305 value = 0; 306 } 307 val = value / 28.0f; 308 } 309 310 if (val != this.speedSetting) { 311 firePropertyChange(SPEEDSETTING, this.speedSetting, val); 312 this.speedSetting = val; 313 record(val); 314 } 315 } 316 } 317 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP1PACKETHEADER)) { 318 int data = m.getElement(8) & 0xff; 319 updateFunction(0,((data & 0x10) == 0x10)); 320 updateFunction(1,((data & 0x01) == 0x01)); 321 updateFunction(2,((data & 0x02) == 0x02)); 322 updateFunction(3,((data & 0x04) == 0x04)); 323 updateFunction(4,((data & 0x08) == 0x08)); 324 325 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP2PACKETHEADER)) { 326 int data = m.getElement(8) & 0xff; 327 updateFunction(5,((data & 0x01) == 0x01)); 328 updateFunction(6,((data & 0x02) == 0x02)); 329 updateFunction(7,((data & 0x04) == 0x04)); 330 updateFunction(8,((data & 0x08) == 0x08)); 331 332 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP3PACKETHEADER)) { 333 int data = m.getElement(8) & 0xff; 334 updateFunction(9,((data & 0x01) == 0x01)); 335 updateFunction(10,((data & 0x02) == 0x02)); 336 updateFunction(11,((data & 0x04) == 0x04)); 337 updateFunction(12,((data & 0x08) == 0x08)); 338 339 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP4PACKETHEADER)) { 340 int data = m.getElement(8) & 0xff; 341 updateFunction(13,((data & 0x01) == 0x01)); 342 updateFunction(14,((data & 0x02) == 0x02)); 343 updateFunction(15,((data & 0x04) == 0x04)); 344 updateFunction(16,((data & 0x08) == 0x08)); 345 346 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP5PACKETHEADER)) { 347 int data = m.getElement(8) & 0xff; 348 updateFunction(17,((data & 0x01) == 0x01)); 349 updateFunction(18,((data & 0x02) == 0x02)); 350 updateFunction(19,((data & 0x04) == 0x04)); 351 updateFunction(20,((data & 0x08) == 0x08)); 352 353 } else if (MrcPackets.startsWith(m, MrcPackets.FUNCTIONGROUP6PACKETHEADER)) { 354 int data = m.getElement(8) & 0xff; 355 updateFunction(21,((data & 0x01) == 0x01)); 356 updateFunction(22,((data & 0x02) == 0x02)); 357 updateFunction(23,((data & 0x04) == 0x04)); 358 updateFunction(24,((data & 0x08) == 0x08)); 359 360 updateFunction(25,((data & 0x10) == 0x10)); 361 updateFunction(26,((data & 0x20) == 0x20)); 362 updateFunction(27,((data & 0x40) == 0x40)); 363 updateFunction(28,((data & 0x80) == 0x80)); 364 365 } 366 } 367 } 368 369 @Override 370 public void notifyXmit(Date timestamp, MrcMessage m) {/* message(m); */ 371 372 } 373 374 @Override 375 public void notifyFailedXmit(Date timestamp, MrcMessage m) { /*message(m);*/ } 376 377 // initialize logging 378 private final static Logger log = LoggerFactory.getLogger(MrcThrottle.class); 379 380}