001package jmri.jmrix.marklin; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import jmri.LocoAddress; 005import jmri.SpeedStepMode; 006import jmri.jmrix.AbstractThrottle; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * An implementation of DccThrottle with code specific to an TAMS connection. 012 * <p> 013 * Based on Glen Oberhauser's original LnThrottle implementation 014 * 015 * @author Kevin Dickerson Copyright (C) 2012 016 */ 017public class MarklinThrottle extends AbstractThrottle implements MarklinListener { 018 019 /** 020 * Constructor. 021 * @param memo system connection. 022 * @param address loco address. 023 */ 024 public MarklinThrottle(MarklinSystemConnectionMemo memo, LocoAddress address) { 025 super(memo); 026 tc = memo.getTrafficController(); 027 028 synchronized(this) { 029 this.speedSetting = 0; 030 } 031 // Functions default to false 032 this.address = address; 033 this.isForward = true; 034 035 setSpeedStepMode(jmri.SpeedStepMode.NMRA_DCC_128); 036 tc.addMarklinListener(this); 037 tc.sendMarklinMessage(MarklinMessage.getQryLocoSpeed(getCANAddress()), this); 038 tc.sendMarklinMessage(MarklinMessage.getQryLocoDirection(getCANAddress()), this); 039 for (int i = 0; i <= 28; i++) { 040 tc.sendMarklinMessage(MarklinMessage.getQryLocoFunction(getCANAddress(), i), this); 041 } 042 } 043 044 /** 045 * Send the message to set the state of functions F0, F1, F2, F3, F4. To 046 * send function group 1 we have to also send speed, direction etc. 047 */ 048 @Override 049 protected void sendFunctionGroup1() { 050 051 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 0, (getFunction(0) ? 0x01 : 0x00)), this); 052 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 1, (getFunction(1) ? 0x01 : 0x00)), this); 053 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 2, (getFunction(2) ? 0x01 : 0x00)), this); 054 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 3, (getFunction(3) ? 0x01 : 0x00)), this); 055 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 4, (getFunction(4) ? 0x01 : 0x00)), this); 056 057 } 058 059 /** 060 * Send the message to set the state of functions F5, F6, F7, F8. 061 */ 062 @Override 063 protected void sendFunctionGroup2() { 064 065 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 5, (getFunction(5) ? 0x01 : 0x00)), this); 066 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 6, (getFunction(6) ? 0x01 : 0x00)), this); 067 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 7, (getFunction(7) ? 0x01 : 0x00)), this); 068 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 8, (getFunction(8) ? 0x01 : 0x00)), this); 069 070 } 071 072 @Override 073 protected void sendFunctionGroup3() { 074 075 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 9, (getFunction(9) ? 0x01 : 0x00)), this); 076 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 10, (getFunction(10) ? 0x01 : 0x00)), this); 077 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 11, (getFunction(11) ? 0x01 : 0x00)), this); 078 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 12, (getFunction(12) ? 0x01 : 0x00)), this); 079 080 } 081 082 @Override 083 protected void sendFunctionGroup4() { 084 085 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 13, (getFunction(13) ? 0x01 : 0x00)), this); 086 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 14, (getFunction(14) ? 0x01 : 0x00)), this); 087 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 15, (getFunction(15) ? 0x01 : 0x00)), this); 088 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 16, (getFunction(16) ? 0x01 : 0x00)), this); 089 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 17, (getFunction(17) ? 0x01 : 0x00)), this); 090 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 18, (getFunction(18) ? 0x01 : 0x00)), this); 091 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 19, (getFunction(19) ? 0x01 : 0x00)), this); 092 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 20, (getFunction(20) ? 0x01 : 0x00)), this); 093 094 } 095 096 @Override 097 protected void sendFunctionGroup5() { 098 099 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 21, (getFunction(21) ? 0x01 : 0x00)), this); 100 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 22, (getFunction(22) ? 0x01 : 0x00)), this); 101 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 23, (getFunction(23) ? 0x01 : 0x00)), this); 102 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 24, (getFunction(24) ? 0x01 : 0x00)), this); 103 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 25, (getFunction(25) ? 0x01 : 0x00)), this); 104 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 26, (getFunction(26) ? 0x01 : 0x00)), this); 105 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 27, (getFunction(27) ? 0x01 : 0x00)), this); 106 tc.sendMarklinMessage(MarklinMessage.setLocoFunction(getCANAddress(), 28, (getFunction(28) ? 0x01 : 0x00)), this); 107 108 } 109 110 /** 111 * Set the speed and direction. 112 * <p> 113 * This intentionally skips the emergency stop value of 1. 114 * 115 * @param speed Number from 0 to 1; less than zero is emergency stop 116 */ 117 @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY") // OK to compare floating point, notify on any change 118 @Override 119 public synchronized void setSpeedSetting(float speed) { 120 float oldSpeed = this.speedSetting; 121 this.speedSetting = speed; 122 123 int value = (int) ((1000) * this.speedSetting); 124 if (value > 1000) { 125 value = 1000; // max possible speed 126 } 127 128 if (this.speedSetting > 0 && value == 0) { 129 value = 1; // ensure non-zero input results in non-zero output 130 } 131 132 if (value < 0) { 133 //Emergency Stop 134 tc.sendMarklinMessage(MarklinMessage.setLocoEmergencyStop(getCANAddress()), this); 135 } else { 136 tc.sendMarklinMessage(MarklinMessage.setLocoSpeed(getCANAddress(), value), this); 137 } 138 log.debug("Float speed = {} Int speed = {}", speed, value); 139 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 140 record(speed); 141 } 142 143 /** 144 * Convert a Marklin speed integer to a float speed value 145 * @param lSpeed Marklin-format speed value 146 * @return 0.0 - 1.0 speed value 147 */ 148 protected float floatSpeed(int lSpeed) { 149 if (lSpeed == 0) { 150 return 0.f; 151 } 152 return ((lSpeed) / 1000.f); 153 } 154 155 @Override 156 public void setIsForward(boolean forward) { 157 boolean old = isForward; 158 isForward = forward; 159 setSpeedSetting(0.0f); //Stop the loco first before changing direction. 160 tc.sendMarklinMessage(MarklinMessage.setLocoDirection(getCANAddress(), (forward ? 0x01 : 0x02)), this); 161 firePropertyChange(ISFORWARD, old, isForward); 162 } 163 164 private LocoAddress address; 165 166 MarklinTrafficController tc; 167 168 @Override 169 public void setSpeedStepMode(SpeedStepMode Mode) { 170 if (log.isDebugEnabled()) { 171 log.debug("Speed Step Mode Change to Mode: {} Current mode is: {}", Mode, this.speedStepMode); 172 } 173 boolean isLong = adapterMemo.get(jmri.ThrottleManager.class).canBeLongAddress(address.getNumber()); 174 switch (address.getProtocol()) { 175 case DCC: 176 if (Mode == SpeedStepMode.NMRA_DCC_28 && isLong) { 177 tc.sendMarklinMessage(MarklinMessage.setLocoSpeedSteps(getCANAddress(), MarklinConstants.STEPLONG28), this); 178 } else if (Mode == SpeedStepMode.NMRA_DCC_28 && !isLong) { 179 tc.sendMarklinMessage(MarklinMessage.setLocoSpeedSteps(getCANAddress(), MarklinConstants.STEPSHORT28), this); 180 } else if (Mode == SpeedStepMode.NMRA_DCC_128 && isLong) { 181 tc.sendMarklinMessage(MarklinMessage.setLocoSpeedSteps(getCANAddress(), MarklinConstants.STEPLONG128), this); 182 } else if (Mode == SpeedStepMode.NMRA_DCC_128 && !isLong) { 183 tc.sendMarklinMessage(MarklinMessage.setLocoSpeedSteps(getCANAddress(), MarklinConstants.STEPSHORT128), this); 184 } 185 break; 186 default: 187 Mode = SpeedStepMode.NMRA_DCC_28; 188 break; 189 } 190 super.setSpeedStepMode(Mode); 191 } 192 193 @Override 194 public LocoAddress getLocoAddress() { 195 return address; 196 } 197 198 @Override 199 public void throttleDispose() { 200 active = false; 201 finishRecord(); 202 } 203 204 @Override 205 public void message(MarklinMessage m) { 206 // messages are ignored 207 } 208 209 @Override 210 public void reply(MarklinReply m) { 211 if (m.getPriority() == MarklinConstants.PRIO_1 && m.getCommand() >= MarklinConstants.MANCOMMANDSTART && m.getCommand() <= MarklinConstants.MANCOMMANDEND) { 212 if (m.getAddress() != getCANAddress()) { 213 if (log.isDebugEnabled()) { 214 log.debug("Addressed packet is not for us {} {}", m.getAddress(), getCANAddress()); 215 } 216 return; 217 } 218 if (m.getCommand() == MarklinConstants.LOCODIRECTION) { 219 if (log.isDebugEnabled()) { 220 log.debug("Loco Direction {}", m.getElement(9)); 221 } 222 //The CS2 sets the speed of the loco to Zero when changing direction, however it doesn't appear to broadcast it out. 223 synchronized(this) { 224 switch (m.getElement(9)) { 225 case 0x00: 226 return; //No change 227 case 0x01: 228 if (!isForward) { 229 speedSetting = 0.0f; 230 super.setSpeedSetting(speedSetting); 231 isForward = true; 232 firePropertyChange(ISFORWARD, false, isForward); 233 } 234 return; 235 case 0x02: 236 if (isForward) { 237 speedSetting = 0.0f; 238 super.setSpeedSetting(speedSetting); 239 isForward = false; 240 firePropertyChange(ISFORWARD, true, isForward); 241 } 242 return; 243 case 0x03: 244 speedSetting = 0.0f; 245 super.setSpeedSetting(speedSetting); 246 isForward = !isForward; 247 firePropertyChange(ISFORWARD, !isForward, isForward); 248 return; 249 default: 250 log.error("No Match Found for loco direction {}", m.getElement(9)); 251 return; 252 } 253 } 254 } 255 if (m.getCommand() == MarklinConstants.LOCOSPEED) { 256 int speed = m.getElement(9); 257 speed = (speed << 8) + (m.getElement(10)); 258 float newSpeed = floatSpeed(speed); 259 log.debug("Speed raw {} float {}", speed, newSpeed); 260 super.setSpeedSetting(newSpeed); 261 } 262 if (m.getCommand() == MarklinConstants.LOCOFUNCTION) { 263 updateFunction(m.getElement(9),!(m.getElement(10)==0)); 264 } 265 } 266 } 267 268 int getCANAddress() { 269 switch (address.getProtocol()) { 270 case DCC: 271 return MarklinConstants.DCCSTART + address.getNumber(); 272 case MOTOROLA: 273 return address.getNumber(); 274 case SELECTRIX: 275 return MarklinConstants.SX2START + address.getNumber(); 276 case MFX: 277 return MarklinConstants.MFXSTART + address.getNumber(); 278 default: 279 return MarklinConstants.DCCSTART + address.getNumber(); 280 } 281 } 282 283 // initialize logging 284 private final static Logger log = LoggerFactory.getLogger(MarklinThrottle.class); 285 286}