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