001package jmri.jmrix.zimo; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import jmri.DccLocoAddress; 005import jmri.LocoAddress; 006import jmri.jmrix.AbstractThrottle; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * An implementation of DccThrottle with code specific to an Mx1 connection. 012 * <p> 013 * Based on Glen Oberhauser's original LnThrottleManager implementation 014 * 015 * @author Bob Jacobsen Copyright (C) 2001 016 */ 017public class Mx1Throttle extends AbstractThrottle implements Mx1Listener { 018 019 private Mx1TrafficController tc = null; 020 //private Mx1Interface network; 021 022 /** 023 * Create a new throttle. 024 * 025 * @param memo the system connection the throttle is associated with 026 * @param address the address for the throttle 027 */ 028 public Mx1Throttle(Mx1SystemConnectionMemo memo, DccLocoAddress address) { 029 super(memo); 030 this.tc = memo.getMx1TrafficController(); 031 super.speedStepMode = jmri.SpeedStepMode.NMRA_DCC_128; 032 033 // cache settings. It would be better to read the 034 // actual state, but I don't know how to do this 035 synchronized(this) { 036 this.speedSetting = 0; 037 } 038 // Functions default to false 039 this.address = address; 040 this.isForward = true; 041 if (address.isLongAddress()) { 042 addressLo = address.getNumber(); 043 addressHi = address.getNumber() >> 8; 044 addressHi = addressHi + 0xc0; //We add 0xc0 to the high byte. 045 } else { 046 addressLo = address.getNumber(); 047 } 048 tc.addMx1Listener(~0, this); 049 } 050 051 DccLocoAddress address; 052 053 int addressLo = 0x00; 054 int addressHi = 0x00; 055 056 @Override 057 public LocoAddress getLocoAddress() { 058 return address; 059 } 060 061 @Override 062 protected void sendFunctionGroup1() { 063 sendSpeedCmd(); 064 /*int data = 0x00 | 065 ( f0 ? 0x10 : 0) | 066 ( f1 ? 0x01 : 0) | 067 ( f2 ? 0x02 : 0) | 068 ( f3 ? 0x04 : 0) | 069 ( f4 ? 0x08 : 0); 070 071 data = data + 0x80;*/ 072 /*Mx1Message m = Mx1Message.getSendFunction(1, addressLo, addressHi, data); 073 if(m!=null) 074 tc.sendMx1Message(m);*/ 075 } 076 077 /** 078 * Send the message to set the state of functions F5, F6, F7, F8. 079 */ 080 @Override 081 protected void sendFunctionGroup2() { 082 sendSpeedCmd(); 083 // Always need speed command before function group command to reset consist pointer 084 /*int data = 0x00 | 085 (f8 ? 0x08 : 0) | 086 (f7 ? 0x04 : 0) | 087 (f6 ? 0x02 : 0) | 088 (f5 ? 0x01 : 0); 089 090 data = data + 0xB0;*/ 091 } 092 093 /** 094 * Send the message to set the state of functions F9, F12, F11, F12. 095 */ 096 @Override 097 protected void sendFunctionGroup3() { 098 sendSpeedCmd(); 099 /*int data = 0x00 | 100 ( f9 ? 0x01 : 0) | 101 ( f10 ? 0x02 : 0) | 102 ( f11 ? 0x04 : 0) | 103 ( f12 ? 0x08 : 0); 104 105 data = data + 0xA0;*/ 106 } 107 108 /** 109 * Send the message to set the state of functions F13 to F20 in function 110 * Group 4 and 5 111 */ 112 @Override 113 protected void sendFunctionGroup4() { 114 // The NCE USB doesn't support the NMRA packet format 115 // Always need speed command before function group command to reset consist pointer 116// int data = 0x00 117// | (f16 ? 0x08 : 0) 118// | (f15 ? 0x04 : 0) 119// | (f14 ? 0x02 : 0) 120// | (f13 ? 0x01 : 0); 121// 122// data = data + 0xD0; 123 124 /*Mx1Message m = Mx1Message.getSendFunction(4, addressLo, addressHi, data); 125 if(m!=null) 126 tc.sendMx1Message(m);*/ 127// data = 0x00 128// | (f20 ? 0x08 : 0) 129// | (f19 ? 0x04 : 0) 130// | (f18 ? 0x02 : 0) 131// | (f17 ? 0x01 : 0); 132// data = data + 0xC0; 133 134 /*m = Mx1Message.getSendFunction(5, addressLo, addressHi, data); 135 if(m!=null) 136 tc.sendMx1Message(m);*/ 137 } 138 139 /** 140 * Send the message to set the state of functions F21 to F28. MRC Group 6 141 */ 142 @Override 143 protected void sendFunctionGroup5() { 144 /* int data = 0x00 | 145 (f28 ? 0x80 : 0) | 146 (f27 ? 0x40 : 0) | 147 (f26 ? 0x20 : 0) | 148 (f25 ? 0x10 : 0) | 149 (f24 ? 0x08 : 0) | 150 (f23 ? 0x04 : 0) | 151 (f22 ? 0x02 : 0) | 152 (f21 ? 0x01 : 0); */ 153 154 /*Mx1Message m = Mx1Message.getSendFunction(6, addressLo, addressHi, data); 155 if(m!=null) 156 tc.sendMx1Message(m); */ 157 } 158 159 /** 160 * Set the speed and direction. 161 * 162 * @param speed Number from 0 to 1; less than zero is emergency stop 163 */ 164 @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY") // OK to compare floating point, notify on any change 165 @Override 166 public synchronized void setSpeedSetting(float speed) { 167 float oldSpeed = this.speedSetting; 168 this.speedSetting = speed; 169 sendSpeedCmd(); 170 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 171 record(speed); 172 } 173 174 void sendSpeedCmd() { 175 Mx1Message m; 176 int value = 0; 177 int cData1 = (isForward ? 0x00 : 0x20); 178 cData1 = cData1 + (getFunction(0) ? 0x10 : 0x00); 179 synchronized(this) { 180 if (super.speedStepMode == jmri.SpeedStepMode.NMRA_DCC_128) { 181 //m = Mx1Message.getSendSpeed128(addressLo, addressHi, value); 182 value = Math.round((127 - 1) * speedSetting); // -1 for rescale to avoid estop 183 if (speedSetting > 0 && value == 0) { 184 value = 1; // ensure non-zero speed for non-zero input 185 } 186 if (value > 0) { 187 value = value + 1; // skip estop 188 } 189 if (value > 127) { 190 value = 127; // max possible speed 191 } 192 if (value < 0) { 193 value = 1; // emergency stop 194 } 195 value = (value & 0x7F); 196 cData1 = cData1 + 0xc; 197 } else if (super.speedStepMode == jmri.SpeedStepMode.NMRA_DCC_28) { 198 value = Math.round((28) * speedSetting); // -1 for rescale to avoid estop 199 if (speedSetting > 0 && value == 0) { 200 value = 1; // ensure non-zero speed for non-zero input 201 } 202 if (value > 0) { 203 value = value + 3; // skip estop 204 } 205 if (value > 32) { 206 value = 31; // max possible speed 207 } 208 if (value < 0) { 209 value = 2; // emergency stop 210 } 211 int speedC = (value & 0x1F) >> 1; 212 int c = (value & 0x01) << 4; // intermediate speed step 213 214 speedC = speedC + c; 215 value = (isForward ? 0x60 : 0x40) | speedC; 216 cData1 = cData1 + 0x8; 217 } 218 } 219 m = Mx1Message.getLocoControl(address.getNumber(), value, true, cData1, getFunction1to8(), getFunction9to12()); 220 tc.sendMx1Message(m, this); 221 } 222 223 int getFunction1to8() { 224 225 int data = 0x00 226 | (getFunction(1) ? 0x01 : 0) 227 | (getFunction(2) ? 0x02 : 0) 228 | (getFunction(3) ? 0x04 : 0) 229 | (getFunction(4) ? 0x08 : 0) 230 | (getFunction(5) ? 0x10 : 0) 231 | (getFunction(6) ? 0x20 : 0) 232 | (getFunction(7) ? 0x40 : 0) 233 | (getFunction(8) ? 0x80 : 0); 234 235 return data; 236 } 237 238 int getFunction9to12() { 239 int data = 0x00 240 | (getFunction(9) ? 0x01 : 0) 241 | (getFunction(10) ? 0x02 : 0) 242 | (getFunction(11) ? 0x04 : 0) 243 | (getFunction(12) ? 0x08 : 0); 244 return data; 245 } 246 247 @Override 248 public void setIsForward(boolean forward) { 249 boolean old = isForward; 250 isForward = forward; 251 synchronized(this) { 252 setSpeedSetting(speedSetting); // send the command 253 } 254 log.debug("setIsForward= {}", forward); 255 firePropertyChange(ISFORWARD, old, isForward); 256 } 257 258 @Override 259 public void throttleDispose() { 260 finishRecord(); 261 } 262 263 @Override 264 public void message(Mx1Message m) { 265 266 } 267 268 // initialize logging 269 private final static Logger log = LoggerFactory.getLogger(Mx1Throttle.class); 270 271}