001package jmri.jmrix.sprog; 002 003import jmri.DccLocoAddress; 004import jmri.InstanceManager; 005import jmri.LocoAddress; 006import jmri.SpeedStepMode; 007import jmri.jmrix.AbstractThrottle; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * An implementation of DccThrottle with code specific to an SPROG connection. 013 * <p> 014 * Based on the {@link jmri.jmrix.nce.NceThrottle} implementation. 015 * <p> 016 * Updated by Andrew Crosland February 2012 to enable 28 step speed packets 017 * 018 * @author Bob Jacobsen Copyright (C) 2003 019 */ 020public class SprogThrottle extends AbstractThrottle { 021 022 /** 023 * Constructor. 024 * @param memo system connection. 025 * @param address Loco address. 026 */ 027 public SprogThrottle(SprogSystemConnectionMemo memo, DccLocoAddress address) { 028 super(memo, SprogConstants.MAX_FUNCTIONS); 029 station = memo.getCommandStation(); 030 031 // cache settings. 032 synchronized (this) { 033 this.speedSetting = 0; 034 } 035 // Functions default to false 036 this.address = address; 037 this.isForward = true; 038 this.speedStepMode = SpeedStepMode.NMRA_DCC_128; 039 040 } 041 042 SprogCommandStation station = null; 043 044 DccLocoAddress address; 045 046 @Override 047 public LocoAddress getLocoAddress() { 048 return address; 049 } 050 051 /** 052 * Send the message to set the state of functions F0, F1, F2, F3, F4. 053 */ 054 @Override 055 protected void sendFunctionGroup1() { 056 byte[] result = jmri.NmraPacket.function0Through4Packet( 057 address.getNumber(), address.isLongAddress(), 058 getFunction(0), getFunction(1), getFunction(2), getFunction(3), getFunction(4)); 059 060 station.sendPacket(result, 1); 061 } 062 063 /** 064 * Send the message to set the state of functions F5, F6, F7, F8. 065 */ 066 @Override 067 protected void sendFunctionGroup2() { 068 069 byte[] result = jmri.NmraPacket.function5Through8Packet( 070 address.getNumber(), address.isLongAddress(), 071 getFunction(5), getFunction(6), getFunction(7), getFunction(8)); 072 073 station.sendPacket(result, 1); 074 } 075 076 /** 077 * Send the message to set the state of functions F9, F10, F11, F12. 078 */ 079 @Override 080 protected void sendFunctionGroup3() { 081 082 byte[] result = jmri.NmraPacket.function9Through12Packet( 083 address.getNumber(), address.isLongAddress(), 084 getFunction(9), getFunction(10), getFunction(11), getFunction(12)); 085 086 station.sendPacket(result, 1); 087 } 088 089 /** 090 * Send the message to set the state of functions F13 - F20. 091 */ 092 @Override 093 protected void sendFunctionGroup4() { 094 095 byte[] result = jmri.NmraPacket.function13Through20Packet( 096 address.getNumber(), address.isLongAddress(), 097 getFunction(13), getFunction(14), getFunction(15), getFunction(16), 098 getFunction(17), getFunction(18), getFunction(19), getFunction(20)); 099 100 station.sendPacket(result, 1); 101 } 102 103 /** 104 * Send the message to set the state of functions F21 - F28. 105 */ 106 @Override 107 protected void sendFunctionGroup5() { 108 109 byte[] result = jmri.NmraPacket.function21Through28Packet( 110 address.getNumber(), address.isLongAddress(), 111 getFunction(21), getFunction(22), getFunction(23), getFunction(24), 112 getFunction(25), getFunction(26), getFunction(27), getFunction(28)); 113 114 station.sendPacket(result, 1); 115 } 116 117 /** 118 * Send the message to set the state of functions F29 - F36. 119 */ 120 @Override 121 protected void sendFunctionGroup6() { 122 123 byte[] result = jmri.NmraPacket.function29Through36Packet( 124 address.getNumber(), address.isLongAddress(), 125 getFunctionNoWarn(29), getFunctionNoWarn(30), getFunctionNoWarn(31), getFunctionNoWarn(32), 126 getFunctionNoWarn(33), getFunctionNoWarn(34), getFunctionNoWarn(35), getFunctionNoWarn(36) 127 ); 128 129 station.sendPacket(result, 1); 130 } 131 132 /** 133 * Send the message to set the state of functions F37 - F44. 134 */ 135 @Override 136 protected void sendFunctionGroup7() { 137 138 byte[] result = jmri.NmraPacket.function37Through44Packet( 139 address.getNumber(), address.isLongAddress(), 140 getFunctionNoWarn(37), getFunctionNoWarn(38), getFunctionNoWarn(39), getFunctionNoWarn(40), 141 getFunctionNoWarn(41), getFunctionNoWarn(42), getFunctionNoWarn(43), getFunctionNoWarn(44) 142 ); 143 144 station.sendPacket(result, 1); 145 } 146 147 /** 148 * Send the message to set the state of functions F45 - F52. 149 */ 150 @Override 151 protected void sendFunctionGroup8() { 152 153 byte[] result = jmri.NmraPacket.function45Through52Packet( 154 address.getNumber(), address.isLongAddress(), 155 getFunctionNoWarn(45), getFunctionNoWarn(46), getFunctionNoWarn(47), getFunctionNoWarn(48), 156 getFunctionNoWarn(49), getFunctionNoWarn(50), getFunctionNoWarn(51), getFunctionNoWarn(52) 157 ); 158 159 station.sendPacket(result, 1); 160 } 161 162 /** 163 * Send the message to set the state of functions F53 - F60. 164 */ 165 @Override 166 protected void sendFunctionGroup9() { 167 168 byte[] result = jmri.NmraPacket.function53Through60Packet( 169 address.getNumber(), address.isLongAddress(), 170 getFunctionNoWarn(53), getFunctionNoWarn(54), getFunctionNoWarn(55), getFunctionNoWarn(56), 171 getFunctionNoWarn(57), getFunctionNoWarn(58), getFunctionNoWarn(59), getFunctionNoWarn(60) 172 ); 173 174 station.sendPacket(result, 1); 175 } 176 177 /** 178 * Send the message to set the state of functions F61 - F68. 179 */ 180 @Override 181 protected void sendFunctionGroup10() { 182 183 byte[] result = jmri.NmraPacket.function61Through68Packet( 184 address.getNumber(), address.isLongAddress(), 185 getFunctionNoWarn(61), getFunctionNoWarn(62), getFunctionNoWarn(63), getFunctionNoWarn(64), 186 getFunctionNoWarn(65), getFunctionNoWarn(66), getFunctionNoWarn(67), getFunctionNoWarn(68) 187 ); 188 189 station.sendPacket(result, 1); 190 } 191 192 /** 193 * Set the speed step value and the related 194 * speedIncrement value. 195 * 196 * @param Mode the current speed step mode - default should be 128 speed 197 * step mode in most cases 198 */ 199 @Override 200 public void setSpeedStepMode(SpeedStepMode Mode) { 201 SprogMessage m; 202 int mode = address.isLongAddress() 203 ? SprogConstants.LONG_ADD : 0; 204 try { 205 mode |= (InstanceManager.getDefault(jmri.PowerManager.class).getPower() == SprogPowerManager.ON) 206 ? SprogConstants.POWER_BIT : 0; 207 } catch (Exception e) { 208 log.error("Exception from InstanceManager.getDefault(jmri.PowerManager.class)", e); 209 } 210 if (log.isDebugEnabled()) { 211 log.debug("Speed Step Mode Change to Mode: {} Current mode is: {}", Mode, this.speedStepMode); 212 } 213 if (Mode == SpeedStepMode.NMRA_DCC_14) { 214 mode += 0x200; 215 } else if (Mode == SpeedStepMode.NMRA_DCC_27) { 216 log.error("Requested Speed Step Mode 27 not supported Current mode is: {}", this.speedStepMode); 217 return; 218 } else if (Mode == SpeedStepMode.NMRA_DCC_28) { 219 mode += 0x400; 220 } else { // default to 128 speed step mode 221 mode += 0x800; 222 } 223 m = new SprogMessage("M h" + Integer.toHexString(mode)); 224 ((SprogSystemConnectionMemo)adapterMemo).getSprogTrafficController().sendSprogMessage(m, null); 225 if (Mode != SpeedStepMode.NMRA_DCC_27) { 226 firePropertyChange(SPEEDSTEPS, this.speedStepMode, this.speedStepMode = Mode); 227 } 228 } 229 230 /** 231 * Set the speed and direction. 232 * <p> 233 * This intentionally skips the emergency stop value of 1 in 128 step mode 234 * and the stop and estop values 1-3 in 28 step mode. 235 * 236 * @param speed Number from 0 to 1; less than zero is emergency stop 237 */ 238 @Override 239 public synchronized void setSpeedSetting(float speed) { 240 SpeedStepMode mode = getSpeedStepMode(); 241 if (mode == SpeedStepMode.NMRA_DCC_28) { 242 // 28 step mode speed commands are 243 // stop, estop, stop, estop, 4, 5, ..., 31 244 float oldSpeed = this.speedSetting; 245 this.speedSetting = speed; 246 int value = Math.round((31 - 3) * speed); // -3 for rescale to avoid estopx2 and stop 247 if (this.speedSetting > 0 && value == 0) { 248 value = 1; // ensure non-zero input results in non-zero output 249 } 250 251 log.debug("Speed: {} value: {}", speed, value); 252 253 if (value > 0) { 254 value = value + 3; // skip estopx2 and stop 255 } 256 if (value > 31) { 257 value = 31; // max possible speed 258 } 259 if (value < 0) { 260 value = 1; // emergency stop 261 } 262 String step = "" + value; 263 264 SprogMessage m = new SprogMessage(1 + step.length()); 265 int i = 0; // message index counter 266 if (isForward) { 267 m.setElement(i++, '>'); 268 } else { 269 m.setElement(i++, '<'); 270 } 271 272 for (int j = 0; j < step.length(); j++) { 273 m.setElement(i++, step.charAt(j)); 274 } 275 276 ((SprogSystemConnectionMemo)adapterMemo).getSprogTrafficController().sendSprogMessage(m, null); 277 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 278 } else { 279 // 128 step mode speed commands are 280 // stop, estop, 2, 3, ..., 127 281 float oldSpeed = this.speedSetting; 282 this.speedSetting = speed; 283 int value = Math.round((127 - 1) * speed); // -1 for rescale to avoid estop 284 if (this.speedSetting > 0 && value == 0) { 285 value = 1; // ensure non-zero input results in non-zero output 286 } 287 if (value > 0) { 288 value = value + 1; // skip estop 289 } 290 if (value > 127) { 291 value = 127; // max possible speed 292 } 293 if (value < 0) { 294 value = 1; // emergency stop 295 } 296 String step = "" + value; 297 298 SprogMessage m = new SprogMessage(1 + step.length()); 299 int i = 0; // message index counter 300 if (isForward) { 301 m.setElement(i++, '>'); 302 } else { 303 m.setElement(i++, '<'); 304 } 305 306 for (int j = 0; j < step.length(); j++) { 307 m.setElement(i++, step.charAt(j)); 308 } 309 310 ((SprogSystemConnectionMemo)adapterMemo).getSprogTrafficController().sendSprogMessage(m, null); 311 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 312 } 313 record(speed); 314 } 315 316 @Override 317 public void setIsForward(boolean forward) { 318 boolean old = isForward; 319 isForward = forward; 320 synchronized (this) { 321 setSpeedSetting(speedSetting); // send the command 322 } 323 firePropertyChange(ISFORWARD, old, isForward); 324 } 325 326 @Override 327 public void throttleDispose() { 328 finishRecord(); 329 } 330 331 // initialize logging 332 private final static Logger log = LoggerFactory.getLogger(SprogThrottle.class); 333 334}