001package jmri.jmrix.tmcc; 002 003import jmri.DccLocoAddress; 004import jmri.LocoAddress; 005import jmri.SpeedStepMode; 006import jmri.jmrix.AbstractThrottle; 007 008/** 009 * An implementation of DccThrottle. 010 * <p> 011 * Addresses of 99 and below are considered short addresses, and over 100 are 012 * considered long addresses. 013 * 014 * @author Bob Jacobsen Copyright (C) 2001, 2006 015 */ 016public class SerialThrottle extends AbstractThrottle { 017 018 /** 019 * Constructor. 020 * 021 * @param memo the connected SerialTrafficController 022 * @param address Loco ID 023 */ 024 public SerialThrottle(TmccSystemConnectionMemo memo, DccLocoAddress address) { 025 super(memo, 69); // supports 69 functions 026 tc = memo.getTrafficController(); 027 028 // cache settings. It would be better to read the 029 // actual state, but I don't know how to do this 030 synchronized(this) { 031 this.speedSetting = 0; 032 } 033 // Functions default to false 034 this.address = address; 035 this.isForward = true; 036 this.speedStepMode = SpeedStepMode.TMCC_32; 037 } 038 039 private final DccLocoAddress address; 040 private final SerialTrafficController tc; 041 042 /** 043 * {@inheritDoc} 044 */ 045 @Override 046 public LocoAddress getLocoAddress() { 047 return address; 048 } 049 050 /** 051 * {@inheritDoc} 052 */ 053 @Override 054 public void setFunction(int func, boolean value) { 055 updateFunction(func, value); 056 if (func>=0 && func < SERIAL_FUNCTION_CODES.length) { 057 if ( SERIAL_FUNCTION_CODES[func] > 0xFFFF ) { 058 // TMCC 2 format 059 if (SERIAL_FUNCTION_CODES[func] > 0xFFFFFF ) { 060 int first = (int)(SERIAL_FUNCTION_CODES[func] >> 24); 061 int second = (int)(SERIAL_FUNCTION_CODES[func] & 0xFFFFFF); 062 // doubles are only sent once, not repeating 063 sendOneWordOnce(first + address.getNumber() * 512); 064 sendOneWordOnce(second + address.getNumber() * 512); 065 } else { 066 // single message 067 sendFnToLayout((int)SERIAL_FUNCTION_CODES[func] + address.getNumber() * 512, func); 068 } 069 } else { 070 // TMCC 1 format 071 sendFnToLayout((int)SERIAL_FUNCTION_CODES[func] + address.getNumber() * 128, func); 072 } 073 } 074 else { 075 super.setFunction(func, value); 076 } 077 } 078 079 // the argument is a long containing 3 bytes. 080 // The first byte is the message opcode 081 private void sendOneWordOnce(int word) { 082 SerialMessage m = new SerialMessage(word); 083 tc.sendSerialMessage(m, null); 084 } 085 086 // Translate function number to line characters. 087 // If the upper byte is zero, it will be replaces by 0xF8 088 // and the address will be set in the low position. 089 // If the upper byte is non-zero, that value will be sent, 090 // and the address will be set in the upper (TMCC2) position. 091 // If six bytes are specified (with the upper one non-zero), 092 // this will be interpreted as two commands to be sequentially sent, 093 // with the upper bytes sent first. 094 private final static long[] SERIAL_FUNCTION_CODES = new long[] { 095 0x00000D, 0x00001D, 0x00001C, 0x000005, 0x000006, /* Fn0-4 */ 096 0x000010, 0x000011, 0x000012, 0x000013, 0x000014, /* Fn5-9 */ 097 0x000015, 0x000016, 0x000017, 0x000018, 0x000019, /* Fn10-14 */ 098 0x000009, 0x00001E, 0x000000, 0x000003, 0x000001, /* Fn15-19 */ 099 0x000004, 0x000007, 0x000047, 0x000042, 0x000028, /* Fn20-24 */ 100 0x000029, 0x00002A, 0x00002B, /* 25-27 */ 101 // start of TMCC 2 functions 102 0xF801FBF801FCL, // Fn28 Start Up Sequence 1 (Delayed Prime Mover, then Immediate Start Up) 103 0xF801FC, // Fn29 Start Up Sequence 2 (Immediate Start Up) 104 0xF801FDF801FEL, // Fn30 Shut Down Sequence 1 (Delay w/ Announcement then Immediate Shut Down) 105 0xF801FE, // Fn31 Shut down Sequence 2 (Immediate Shut Down) 106 0xF90000, // Fn32 107 0xF90000, // Fn33 108 0xF90000, // Fn34 109 0xF90000, // Fn35 110 0xF90000, // Fn36 111 0xF90000, // Fn37 112 0xF90000, // Fn38 113 0xF90000, // Fn39 114 0xF90000, // Fn40 115 0xF90000, // Fn41 116 0xF90000, // Fn42 117 0xF90000, // Fn43 118 0xF90000, // Fn44 119 0xF90000, // Fn45 120 0xF90000, // Fn46 121 0xF90000, // Fn47 122 0xF90000, // Fn48 123 0xF90000, // Fn49 124 0xF90000, // Fn50 125 0xF90000, // Fn51 126 0xF90000, // Fn52 127 0xF90000, // Fn53 128 0xF90000, // Fn54 129 0xF90000, // Fn55 130 0xF90000, // Fn56 131 0xF90000, // Fn57 132 0xF90000, // Fn58 133 0xF90000, // Fn59 134 0xF90000, // Fn60 135 0xF90000, // Fn61 136 0xF90000, // Fn62 137 0xF90000, // Fn63 138 0xF90000, // Fn64 139 0xF90000, // Fn65 140 0xF90000, // Fn66 141 0xF90000, // Fn67 142 }; 143 144 /** 145 * Set the speed. 146 * 147 * @param speed Number from 0 to 1; less than zero is emergency stop 148 */ 149 @Override 150 public void setSpeedSetting(float speed) { 151 float oldSpeed; 152 synchronized(this) { 153 oldSpeed = this.speedSetting; 154 this.speedSetting = speed; 155 } 156 157 // send to layout 158 if (speedStepMode == jmri.SpeedStepMode.TMCC_200) { 159 160 // TMCC2 Legacy 200 step mode 161 int value = (int) (199 * speed); // max value to send is 199 in 200 step mode 162 if (value > 199) { 163 value = 199; // max possible speed 164 } 165 SerialMessage m = new SerialMessage(); 166 m.setOpCode(0xF8); 167 168 if (value < 0) { 169 // immediate stop 170 m.putAsWord(0x0000 + (address.getNumber() << 9) + 0); 171 } else { 172 // normal speed setting 173 m.putAsWord(0x0000 + (address.getNumber() << 9) + value); 174 } 175 // only send twice to advanced command station 176 tc.sendSerialMessage(m, null); 177 tc.sendSerialMessage(m, null); 178 179 } else { 180 181 // assume TMCC 32 step mode 182 int value = (int) (32 * speed); 183 if (value > 31) { 184 value = 31; // max possible speed 185 } 186 SerialMessage m = new SerialMessage(); 187 188 if (value < 0) { 189 // immediate stop 190 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 191 } else { 192 // normal speed setting 193 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 194 } 195 196 tc.sendSerialMessage(m, null); 197 tc.sendSerialMessage(m, null); 198 tc.sendSerialMessage(m, null); 199 tc.sendSerialMessage(m, null); 200 } 201 202 synchronized(this) { 203 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 204 } 205 record(speed); 206 } 207 208 /** 209 * {@inheritDoc} 210 */ 211 @Override 212 public void setIsForward(boolean forward) { 213 boolean old = isForward; 214 isForward = forward; 215 216 // notify layout 217 SerialMessage m = new SerialMessage(); 218 if (forward) { 219 m.putAsWord(0x0000 + address.getNumber() * 128); 220 } else { 221 m.putAsWord(0x0003 + address.getNumber() * 128); 222 } 223 tc.sendSerialMessage(m, null); 224 tc.sendSerialMessage(m, null); 225 tc.sendSerialMessage(m, null); 226 tc.sendSerialMessage(m, null); 227 firePropertyChange(ISFORWARD, old, isForward); 228 } 229 230 /** 231 * Send these messages to the layout four times 232 * to make sure they're accepted. 233 * @param value Content of message to be sent in three bytes 234 * @param func The number of the function being addressed 235 */ 236 protected void sendFnToLayout(int value, int func) { 237 tc.sendSerialMessage(new SerialMessage(value), null); 238 tc.sendSerialMessage(new SerialMessage(value), null); 239 tc.sendSerialMessage(new SerialMessage(value), null); 240 241 repeatFunctionSendWhileOn(value, func); // 4th send is here 242 } 243 244 static final int REPEAT_TIME = 150; 245 246 protected void repeatFunctionSendWhileOn(int value, int func) { 247 // Send again if function is still on and repeat in a short while 248 if (getFunction(func)) { 249 tc.sendSerialMessage(new SerialMessage(value), null); 250 jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> { 251 repeatFunctionSendWhileOn(value, func); 252 }, REPEAT_TIME); 253 } 254 } 255 256 /* 257 * Set the speed step value. 258 * <p> 259 * Only 32 steps is available 260 * 261 * @param mode only TMCC 30 and TMCC 200 are allowed 262 */ 263 @Override 264 public void setSpeedStepMode(jmri.SpeedStepMode mode) { 265 if (mode == jmri.SpeedStepMode.TMCC_32 || mode == jmri.SpeedStepMode.TMCC_200) { 266 super.setSpeedStepMode(mode); 267 } 268 } 269 270 /** 271 * {@inheritDoc} 272 */ 273 @Override 274 public void throttleDispose() { 275 finishRecord(); 276 } 277 278}