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 * with edits/additions by 016 * @author Timothy Jump Copyright (C) 2025 017 */ 018public class SerialThrottle extends AbstractThrottle { 019 020 /** 021 * Constructor. 022 * 023 * @param memo the connected SerialTrafficController 024 * @param address Loco ID 025 */ 026 public SerialThrottle(TmccSystemConnectionMemo memo, DccLocoAddress address) { 027 super(memo, 85); // supports 85 functions 028 tc = memo.getTrafficController(); 029 030 // cache settings. It would be better to read the 031 // actual state, but I don't know how to do this 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.TMCC1_32; 039 } 040 041 private final DccLocoAddress address; 042 private final SerialTrafficController tc; 043 044 /** 045 * {@inheritDoc} 046 */ 047 @Override 048 public LocoAddress getLocoAddress() { 049 return address; 050 } 051 052 // Relating SERIAL_FUNCTION_CODES_TMCC1 to SpeedStepMode.TMCC1_32 and TMCC1_100; 053 // and SERIAL_FUNCTION_CODES_TMCC2 to SpeedStepMode.TMCC2_32 and TMCC2_200. 054 private long[] getFnValueArray(int number) { 055 056 if (number < 0) return new long[]{}; 057 058 if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_32 || getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_100) { 059 if (number < SERIAL_FUNCTION_CODES_TMCC1.length) { 060 return SERIAL_FUNCTION_CODES_TMCC1[number]; 061 } else { 062 return new long[]{}; 063 } 064 } else if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_32) { 065 if (number < SERIAL_FUNCTION_CODES_TMCC2_32.length) { 066 return SERIAL_FUNCTION_CODES_TMCC2_32[number]; 067 } else { 068 return new long[]{}; 069 } 070 } else if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_200) { 071 if (number < SERIAL_FUNCTION_CODES_TMCC2_200.length) { 072 return SERIAL_FUNCTION_CODES_TMCC2_200[number]; 073 } else { 074 return new long[]{}; 075 } 076 } 077 return new long[]{}; 078 } 079 080 081 /** 082 * {@inheritDoc} 083 */ 084 @Override 085 public void setFunction(int func, boolean value) { 086 updateFunction(func, value); 087 088 int numberOfTriple = 0; // ordinal number of triple being processed, e.g. 1, 2, 3, ... 089 checksumAccumulator = 0; // zeroes out accumulated checksum after each instance 090 091 if (getFnValueArray(func).length >0) { 092 for (long triple : getFnValueArray(func)) { 093 numberOfTriple = numberOfTriple+1; 094 095 // process each returned command 096 if (func>=0 && func < SERIAL_FUNCTION_CODES_TMCC1.length) { 097 if ( triple > 0xFFFF ) { 098 // TMCC 2 format 099 if (triple > 0xFFFFFF ) { 100 int first = (int)(triple >> 24); 101 int second = (int)(triple & 0xFFFFFF); 102 accumulateChecksum(first); 103 accumulateChecksum(second); 104 105 // if this is the third triple, place the 106 // checksum in its lowest byte 107 if (numberOfTriple == 3) { 108 second = (second &0xFFFFFF00) | ( (~checksumAccumulator) & 0xFF); 109 } 110 111 // doubles are only sent once, not repeating 112 sendOneWordOnce(first + address.getNumber() * 512); 113 sendOneWordOnce(second + address.getNumber() * 512); 114 115 } else { 116 // single message 117 int content = (int)triple + address.getNumber() * 512; 118 accumulateChecksum(content); 119 120 // if this is the third triple, place the 121 // checksum in its lowest byte 122 if (numberOfTriple == 3) { 123 content = (content &0xFFFFFF00) | ( (~checksumAccumulator) & 0xFF); 124 } 125 126 sendFnToLayout(content, func); 127 } 128 } else { 129 // TMCC 1 format 130 sendFnToLayout((int)triple + address.getNumber() * 128, func); 131 } 132 } else { 133 super.setFunction(func, value); 134 } 135 } 136 } else { 137 super.setFunction(func, value); 138 } 139 } 140 141 // accumulate the checksum values into 142 // the checksumAccumulator variable 143 // 144 // Takes the two bottom bytes _only_ and adds them to the accumulation. 145 int checksumAccumulator = 0; 146 private void accumulateChecksum(int input) { 147 int byte1 = input&0xFF; 148 int byte2 = (input >> 8)&0xFF; 149 checksumAccumulator = (checksumAccumulator + byte1+byte2) & 0xFF; 150 } 151 152 // the argument is a long containing 3 bytes. 153 // The first byte is the message opcode 154 private void sendOneWordOnce(int word) { 155 SerialMessage m = new SerialMessage(word); 156 tc.sendSerialMessage(m, null); 157 } 158 159 // TMCC 1 Function Keys to trigger with TMCC1_32 and TMCC1_100 speed steps. 160 private final static long[][] SERIAL_FUNCTION_CODES_TMCC1 = new long[][] { 161 162 // TMCC1 Remote - Defined FnKeys 163 {0x00000D}, // Fn0 (Headlamp) 164 {0x00001D}, // Fn1 (Bell) 165 {0x00001C}, // Fn2 (Horn/Whistle) 166 {0x000005}, // Fn3 (F - Open Front Coupler) 167 {0x000006}, // Fn4 (R - Open Rear Coupler) 168 169 // TMCC1 Remote - Defined KeyPad FnKeys 170 {0x000011}, {0x000012}, {0x000013}, /* Fn5-7 */ // 1-2-3 171 {0x000014}, {0x000015}, {0x000016}, /* Fn8-10 */ // 4-5-6 172 {0x000017}, {0x000018}, {0x000019}, /* Fn11-13 */ // 7-8-9 173 {0x000010}, /* Fn14 */ // 0 174 175 // TMCC1 Remote - Defined FnKeys 176 {0x000009}, // Fn15 (Aux1) 177 {0x00001E}, // Fn16 (Letoff Sound) 178 {0x000004}, // Fn17 (Boost) 179 {0x000007}, // Fn18 (Brake) 180 {0x000028}, // Fn19 (Momentum Low) 181 {0x000029}, // Fn20 (Momentum Medium) 182 {0x00002A}, // Fn21 (Momentum High) 183 {0x00002B}, // Fn22 (Set) 184 {0x00001F}, // Fn23 (Horn 2) 185 186 // TMCC1 RR Speed FnKeys 187 {0x000064}, // Fn24 ( 4) 5mph 188 {0x00006A}, // Fn25 (10) 20mph 189 {0x00006E}, // Fn26 (14) 35mph 190 {0x000072}, // Fn27 (18) 50mph 191 {0x000078}, // Fn28 (24) 70mph 192 {0x00007F}, // Fn29 (31) Full 193 194 // TMCC1 ERR - Set SpeedSteps 195 {0x000009, 0x000010, 0x000009, 0x000010, 0x000004}, // Fn30 (Set ERR 100 SpeedSteps) 196 {0x000009, 0x000010, 0x000009, 0x000010, 0x000007}, // Fn31 (Set ERR 32 SpeedSteps) 197 198 // TMCC1 Acela/Subway FnKeys 199 {0x000009, 0x000005}, // Fn32 (Open Doors - Left) 200 {0x00000D, 0x000005, 0x00000D}, // Fn33 (Close Doors - Left) 201 {0x000009, 0x000006}, // Fn34 (Open Doors - Right) 202 {0x00000D, 0x000006, 0x00000D}, // Fn35 (Close Doors - Right) 203 {0x000009, 0x000013}, // Fn36 (Pantagraph - Automatic/Prototypical) 204 {0x000009, 0x000015}, // Fn37 (Pantagraph - Down) 205 {0x000009, 0x000016}, // Fn38 (Pantagraph - Manual Mode/Cycles Through Positions) 206 {0x000009, 0x00001C}, // Fn39 (Toggle Horn - City/Country) 207 {0x000009, 0x000018}, // Fn40 (Cab Light - Off) 208 {0x000009, 0x000019}, // Fn41 (Cab Light - On) 209 {0x000009, 0x00000D, 0x000018, 0x00000D}, // Fn42 (Interior Lights - Off) 210 {0x000009, 0x00000D, 0x000019, 0x00000D}, // Fn43 (Interior Lights - On) 211 212 // TMCC1 Break-Down B Unit 213 {0x000009, 0x000004}, // Fn44 Start Breakdown Sequence 214 {0x000009, 0x000015}, // Fn45 Made It Back to the Yard 215 {0x000009, 0x000013}, // Fn46 Restart Unit/Repairs Complete 216 217 // TMCC1 Boxcar/LiveStock Car 218 {0x000009, 0x000013}, // Fn47 Load 219 {0x000009, 0x000012}, // Fn48 Flat Wheel Sound 220 221 // TMCC1 Passenger/Dining Cars 222 {0x000009, 0x000017}, // Fn49 Station PA Arrival Dialog 223 {0x000009, 0x000012}, // Fn50 Conductor Arrival Dialog 224 225 // TMCC1 Crane/Boom Car 226 {0x000009, 0x000011, 0x000007}, // Fn51 Lower the Boom 227 {0x000009, 0x000011, 0x000004}, // Fn52 Raises the Boom 228 {0x000009, 0x000012, 0x000007}, // Fn53 Lowers Main/Large Hook 229 {0x000009, 0x000012, 0x000004}, // Fn54 Raises Main/Large Hook 230 {0x000017, 0x000013, 0x000007}, // Fn55 Lowers Small Hook 231 {0x000017, 0x000013, 0x000004}, // Fn56 Raises Small Hook 232 {0x000004, 0x000014}, // Fn57 Front Work Lights (Toggles On/Off) 233 {0x000004, 0x000015}, // Fn58 Rear Work Lights (Toggles On/Off) 234 {0x000004, 0x000016}, // Fn59 Launches Outriggers 235 {0x000009, 0x000017}, // Fn60 Crew Dialog Off 236 {0x000009, 0x000018}, // Fn61 All Sounds Off 237 {0x000009, 0x000019}, // Fn62 All Sounds On 238 239 // TMCC1 Unassigned FnKeys 240 {0x00002E}, // Fn63 Code to Trigger SerialMonFrame Message/Unassigned FnKey 241 {0x00002E}, // Fn64 Code to Trigger SerialMonFrame Message/Unassigned FnKey 242 {0x00002E}, // Fn65 Code to Trigger SerialMonFrame Message/Unassigned FnKey 243 {0x00002E}, // Fn66 Code to Trigger SerialMonFrame Message/Unassigned FnKey 244 {0x00002E}, // Fn67 Code to Trigger SerialMonFrame Message/Unassigned FnKey 245 {0x00002E}, // Fn68 Code to Trigger SerialMonFrame Message/Unassigned FnKey 246 247 // TMCC1 Aux FnKeys 248 {0x000008}, // Fnxx (Aux1 Off) 249 {0x000009}, // Fnxx (Aux1 Option 1 - On While Held) 250 {0x00000A}, // Fnxx (Aux1 Option 2 - Toggle On/Toggle Off) 251 {0x00000B}, // Fnxx (Aux1 On) 252 {0x00000C}, // Fnxx (Aux2 Off) 253 {0x00000D}, // Fnxx (Aux2 Option 1 - Toggle On/Toggle Off) 254 {0x00000E}, // Fnxx (Aux2 Option 2 - On While Held) 255 {0x00000F}, // Fnxx (Aux2 On) 256 257 }; 258 259 /** 260 * Translate TMCC1 function numbers to line characters. 261 * If the upper byte is zero, it will be replaced by 0xF8 262 * and the address will be set in the low position. 263 * If the upper byte is non-zero, that value will be sent, 264 * and the address will be set in the upper (TMCC2) position. 265 * If six bytes are specified (with the upper one non-zero), 266 * this will be interpreted as two commands to be sequentially sent, 267 * with the upper bytes sent first. 268 */ 269 270 // TMCC 2 Legacy Function Keys to trigger with TMCC2_32 speed steps. 271 private final static long[][] SERIAL_FUNCTION_CODES_TMCC2_32 = new long[][] { 272 273 // TMCC2_32 Remote - Defined FnKeys 274 {0xF8010D}, // Fn0 (Headlamp) 275 {0xF8011D}, // Fn1 (Bell) 276 {0xF8011C}, // Fn2 (Horn/Whistle) 277 {0xF80105}, // Fn3 (F - Open Front Coupler) 278 {0xF80106}, // Fn4 (R - Open Rear Coupler) 279 280 // TMCC2_32 Remote - Defined KeyPad FnKeys 281 {0xF80111}, {0xF80112}, {0xF80113}, /* Fn5-7 */ // 1-2-3 282 {0xF80114}, {0xF80115}, {0xF80116}, /* Fn8-10 */ // 4-5-6 283 {0xF80117}, {0xF80118}, {0xF80119}, /* Fn11-13 */ // 7-8-9 284 {0xF80110}, /* Fn14 */ // 0 285 286 // TMCC2_32 Remote - Defined FnKeys 287 {0xF80109}, // Fn15 (Aux1) 288 {0xF8011E}, // Fn16 (Letoff Sound) 289 {0xF80104}, // Fn17 (Boost) 290 {0xF80107}, // Fn18 (Brake) 291 {0xF80128}, // Fn19 (Momentum Low) 292 {0xF80129}, // Fn20 (Momentum Medium) 293 {0xF8012A}, // Fn21 (Momentum High) 294 {0xF8012B}, // Fn22 (Set) 295 {0xF8011F}, // Fn23 (Horn 2) 296 297 // TMCC2_32 RR Speed FnKeys 298 {0xF80164}, // Fn24 ( 4) 5mph 299 {0xF8016A}, // Fn25 (10) 20mph 300 {0xF8016E}, // Fn26 (14) 35mph 301 {0xF80172}, // Fn27 (18) 50mph 302 {0xF80178}, // Fn28 (24) 70mph 303 {0xF8017F}, // Fn29 (31) Full 304 305 // TMCC2_32 Extended Lighting FnKeys 306 {0xF8017D, 0xFB00E8, 0xFB0000}, // Fn30 (Mars Lt On) 307 {0xF8017D, 0xFB00E9, 0xFB0000}, // Fn31 (Mars Lt Off) 308 309 {0xF8017D, 0xFB00D0, 0xFB0000}, // Fn32 (Ground Lt On) 310 {0xF8017D, 0xFB00D1, 0xFB0000}, // Fn33 (Ground Lt Off) 311 {0xF8017D, 0xFB00D2, 0xFB0000}, // Fn34 (Ground Lt Auto) 312 313 {0xF8017D, 0xFB00A0, 0xFB0000}, // Fn35 (DogHouse On) 314 {0xF8017D, 0xFB00A1, 0xFB0000}, // Fn36 (DogHouse Off) 315 316 {0xF8017D, 0xFB00CC, 0xFB0000}, // Fn37 (Tender Marker On) 317 {0xF8017D, 0xFB00CD, 0xFB0000}, // Fn38 (Tender Marker Off) 318 319 {0xF8017D, 0xFB00F4, 0xFB0000}, // Fn39 (Rule 17 On) 320 {0xF8017D, 0xFB00F5, 0xFB0000}, // Fn40 (Rule 17 Off) 321 {0xF8017D, 0xFB00F6, 0xFB0000}, // Fn41 (Rule 17 Auto) 322 323 {0xF8017D, 0xFB00C0, 0xFB0000}, // Fn42 (Ditch Lt On) 324 {0xF8017D, 0xFB00C1, 0xFB0000}, // Fn43 (Ditch Lt On; Pulse Off with Horn) 325 {0xF8017D, 0xFB00C2, 0xFB0000}, // Fn44 (Ditch Lt Off; Pulse On with Horn) 326 {0xF8017D, 0xFB00C3, 0xFB0000}, // Fn45 (Ditch Lt Off) 327 328 {0xF8017D, 0xFB00F0, 0xFB0000}, // Fn46 (Cab Lt On) 329 {0xF8017D, 0xFB00F1, 0xFB0000}, // Fn47 (Cab Lt Off) 330 {0xF8017D, 0xFB00F2, 0xFB0000}, // Fn48 (Cab Lt Auto) 331 332 {0xF8017D, 0xFB00C8, 0xFB0000}, // Fn49 (Loco Marker On) 333 {0xF8017D, 0xFB00C9, 0xFB0000}, // Fn50 (Loco Marker Off) 334 335 {0xF8017D, 0xFB00B0, 0xFB0000}, // Fn51 (Hazard Lt On) 336 {0xF8017D, 0xFB00B1, 0xFB0000}, // Fn52 (Hazard Lt Off) 337 {0xF8017D, 0xFB00B2, 0xFB0000}, // Fn53 (Hazard Lt Auto) 338 339 {0xF8017D, 0xFB00E0, 0xFB0000}, // Fn54 (Strobe Lt On - Single Flash) 340 {0xF8017D, 0xFB00E1, 0xFB0000}, // Fn55 (Strobe Lt On - Double Flash) 341 {0xF8017D, 0xFB00E2, 0xFB0000}, // Fn56 (Strobe Lt Off) 342 343 {0xF8017D, 0xFB00F8, 0xFB0000}, // Fn57 (Car Cabin Lt On) 344 {0xF8017D, 0xFB00F9, 0xFB0000}, // Fn58 (Car Cabin Lt Off) 345 {0xF8017D, 0xFB00FA, 0xFB0000}, // Fn59 (Car Cabin Lt Auto) 346 347 // TMCC2 Acela/Subway FnKeys 348 {0xF80109, 0xF80112}, // Fn60 (Crew: Report Speed - Moving) 349 {0xF80109, 0xF80115}, // Fn61 (Tower: Emergency Stop/Crew: Ack - Moving) 350 {0xF8017C, 0xFB0020, 0xFB0000}, // Fn62 (Open Doors - Left) 351 {0xF8017C, 0xFB0021, 0xFB0000}, // Fn63 (Close Doors - Left) 352 {0xF8017C, 0xFB0022, 0xFB0000}, // Fn64 (Open Doors - Right) 353 {0xF8017C, 0xFB0023, 0xFB0000}, // Fn65 (Close Doors - Right) 354 {0xF8017C, 0xFB0010, 0xFB0000}, // Fn66 (Pantagraph - Up/F) 355 {0xF8017C, 0xFB0011, 0xFB0000}, // Fn67 (Pantagraph - Down/F) 356 {0xF8017C, 0xFB0012, 0xFB0000}, // Fn68 (Pantagraph - Up/R) 357 {0xF8017C, 0xFB0013, 0xFB0000}, // Fn69 (Pantagraph - Down/R) 358 359 // Only TMCC1 Break-Down B Unit 360 361 // TMCC2 Boxcar/Livestock Car 362 {0xF8017C, 0xFB0030, 0xFB0000}, // Fn70 (Option1 On) 363 {0xF8017C, 0xFB0031, 0xFB0000}, // Fn71 (Opiton1 Off) 364 {0xF8017C, 0xFB0032, 0xFB0000}, // Fn72 (Option2 On) 365 {0xF8017C, 0xFB0033, 0xFB0000}, // Fn73 (Option2 Off) 366 {0xF8017C, 0xFB0034, 0xFB0000}, // Fn74 (Load) 367 {0xF8017C, 0xFB0035, 0xFB0000}, // Fn75 (Unload) 368 {0xF8017C, 0xFB0036, 0xFB0000}, // Fn76 (FRED On) 369 {0xF8017C, 0xFB0037, 0xFB0000}, // Fn77 (FRED Off) 370 {0xF8017C, 0xFB0038, 0xFB0000}, // Fn78 (Flat Wheel On) 371 {0xF8017C, 0xFB0039, 0xFB0000}, // Fn79 (Flat Wheel Off) 372 {0xF8017C, 0xFB003A, 0xFB0000}, // Fn80 (Game On) 373 {0xF8017C, 0xFB003B, 0xFB0000}, // Fn81 (Game Off) 374 375 // Only TMCC1 Passenger/Dining Cars 376 377 // Only TMCC1 Crane/Boom Car 378 379 // TMCC2 Smoke System 380 {0xF8017C, 0xFB0000, 0xFB0000}, // Fn82 (Smoke System Off) 381 {0xF8017C, 0xFB0001, 0xFB0000}, // Fn83 (Smoke System Low) 382 {0xF8017C, 0xFB0002, 0xFB0000}, // Fn84 (Smoke System Med) 383 {0xF8017C, 0xFB0003, 0xFB0000}, // Fn85 (Smoke System High) 384 385 // TRMCC2_32 Unassigned FnKeys 386 {0xF8012E}, // Fnxx Code to Trigger SerialMonFrame Message/Unassigned FnKey 387 {0xF8012E}, // Fnxx Code to Trigger SerialMonFrame Message/Unassigned FnKey 388 389 // TRMCC2_32 Aux FnKeys 390 {0xF80108}, // Fnxx (Aux1 Off) 391 {0xF80109}, // Fnxx (Aux1 Option 1 - On While Held) 392 {0xF8010A}, // Fnxx (Aux1 Option 2 - Toggle On/Toggle Off) 393 {0xF8010B}, // Fnxx (Aux1 On) 394 {0xF8010C}, // Fnxx (Aux2 Off) 395 {0xF8010D}, // Fnxx (Aux2 Option 1 - Toggle On/Toggle Off) 396 {0xF8010E}, // Fnxx (Aux2 Option 2 - On While Held) 397 {0xF8010F}, // Fnxx (Aux2 On) 398 399}; 400 401 // TMCC 2 Legacy Function Keys to trigger with TMCC2_200 speed steps. 402 private final static long[][] SERIAL_FUNCTION_CODES_TMCC2_200 = new long[][] { 403 404 // TMCC2_200 Remote - Defined FnKeys 405 {0xF8010D}, // Fn0 (Headlamp) 406 {0xF8011D}, // Fn1 (Bell) 407 {0xF8011C}, // Fn2 (Horn/Whistle) 408 {0xF80105}, // Fn3 (F - Open Front Coupler) 409 {0xF80106}, // Fn4 (R - Open Rear Coupler) 410 411 // TMCC2_200 Remote - Defined KeyPad FnKeys 412 {0xF80111}, {0xF80112}, {0xF80113}, /* Fn5-7 */ // 1-2-3 413 {0xF80114}, {0xF80115}, {0xF80116}, /* Fn8-10 */ // 4-5-6 414 {0xF80117}, {0xF80118}, {0xF80119}, /* Fn11-13 */ // 7-8-9 415 {0xF80110}, /* Fn14 */ // 0 416 417 // TMCC2_200 Remote - Defined FnKeys 418 {0xF80109}, // Fn15 (Aux1) 419 {0xF8011E}, // Fn16 (Letoff Sound) 420 {0xF80104}, // Fn17 (Boost) 421 {0xF80107}, // Fn18 (Brake) 422 {0xF80128}, // Fn19 (Momentum Low) 423 {0xF80129}, // Fn20 (Momentum Medium) 424 {0xF8012A}, // Fn21 (Momentum High) 425 {0xF8012B}, // Fn22 (Set) 426 {0xF8011F}, // Fn23 (Horn 2) 427 428 // TMCC2_200 RR Speed FnKeys 429 {0xF8000A}, // Fn24 ( 10) 5mph 430 {0xF80028}, // Fn25 ( 40) 20mph 431 {0xF80046}, // Fn26 ( 70) 35mph 432 {0xF80064}, // Fn27 (100) 50mph 433 {0xF8008C}, // Fn28 (140) 70mph 434 {0xF800C7}, // Fn29 (199) Full 435 436 // TMCC2_200 Extended Lighting FnKeys 437 {0xF8017D, 0xFB00E8, 0xFB0000}, // Fn30 (Mars Lt On) 438 {0xF8017D, 0xFB00E9, 0xFB0000}, // Fn31 (Mars Lt Off) 439 440 {0xF8017D, 0xFB00D0, 0xFB0000}, // Fn32 (Ground Lt On) 441 {0xF8017D, 0xFB00D1, 0xFB0000}, // Fn33 (Ground Lt Off) 442 {0xF8017D, 0xFB00D2, 0xFB0000}, // Fn34 (Ground Lt Auto) 443 444 {0xF8017D, 0xFB00A0, 0xFB0000}, // Fn35 (DogHouse On) 445 {0xF8017D, 0xFB00A1, 0xFB0000}, // Fn36 (DogHouse Off) 446 447 {0xF8017D, 0xFB00CC, 0xFB0000}, // Fn37 (Tender Marker On) 448 {0xF8017D, 0xFB00CD, 0xFB0000}, // Fn38 (Tender Marker Off) 449 450 {0xF8017D, 0xFB00F4, 0xFB0000}, // Fn39 (Rule 17 On) 451 {0xF8017D, 0xFB00F5, 0xFB0000}, // Fn40 (Rule 17 Off) 452 {0xF8017D, 0xFB00F6, 0xFB0000}, // Fn41 (Rule 17 Auto) 453 454 {0xF8017D, 0xFB00C0, 0xFB0000}, // Fn42 (Ditch Lt On) 455 {0xF8017D, 0xFB00C1, 0xFB0000}, // Fn43 (Ditch Lt On; Pulse Off with Horn) 456 {0xF8017D, 0xFB00C2, 0xFB0000}, // Fn44 (Ditch Lt Off; Pulse On with Horn) 457 {0xF8017D, 0xFB00C3, 0xFB0000}, // Fn45 (Ditch Lt Off) 458 459 {0xF8017D, 0xFB00F0, 0xFB0000}, // Fn46 (Cab Lt On) 460 {0xF8017D, 0xFB00F1, 0xFB0000}, // Fn47 (Cab Lt Off) 461 {0xF8017D, 0xFB00F2, 0xFB0000}, // Fn48 (Cab Lt Auto) 462 463 {0xF8017D, 0xFB00C8, 0xFB0000}, // Fn49 (Loco Marker On) 464 {0xF8017D, 0xFB00C9, 0xFB0000}, // Fn50 (Loco Marker Off) 465 466 {0xF8017D, 0xFB00B0, 0xFB0000}, // Fn51 (Hazard Lt On) 467 {0xF8017D, 0xFB00B1, 0xFB0000}, // Fn52 (Hazard Lt Off) 468 {0xF8017D, 0xFB00B2, 0xFB0000}, // Fn53 (Hazard Lt Auto) 469 470 {0xF8017D, 0xFB00E0, 0xFB0000}, // Fn54 (Strobe Lt On - SingleFlash) 471 {0xF8017D, 0xFB00E1, 0xFB0000}, // Fn55 (Strobe Lt On - DoubleFlash) 472 {0xF8017D, 0xFB00E2, 0xFB0000}, // Fn56 (Strobe Lt Off) 473 474 {0xF8017D, 0xFB00F8, 0xFB0000}, // Fn57 (Car Cabin Lt On) 475 {0xF8017D, 0xFB00F9, 0xFB0000}, // Fn58 (Car Cabin Lt Off) 476 {0xF8017D, 0xFB00FA, 0xFB0000}, // Fn59 (Car Cabin Lt Auto) 477 478 // TMCC2 Acela/Subway FnKeys 479 {0xF80109, 0xF80112}, // Fn60 (Crew: Report Speed - Moving) 480 {0xF80109, 0xF80115}, // Fn61 (Tower: Emergency Stop/Crew: Ack - Moving) 481 {0xF8017C, 0xFB0020, 0xFB0000}, // Fn62 (Open Doors - Left) 482 {0xF8017C, 0xFB0021, 0xFB0000}, // Fn63 (Close Doors - Left) 483 {0xF8017C, 0xFB0022, 0xFB0000}, // Fn64 (Open Doors - Right) 484 {0xF8017C, 0xFB0023, 0xFB0000}, // Fn65 (Close Doors - Right) 485 {0xF8017C, 0xFB0010, 0xFB0000}, // Fn66 (Pantagraph - Up/F) 486 {0xF8017C, 0xFB0011, 0xFB0000}, // Fn67 (Pantagraph - Down/F) 487 {0xF8017C, 0xFB0012, 0xFB0000}, // Fn68 (Pantagraph - Up/R) 488 {0xF8017C, 0xFB0013, 0xFB0000}, // Fn69 (Pantagraph - Down/R) 489 490 // Only TMCC1 Break-Down B Unit 491 492 // TMCC2 Boxcar/Livestock Car 493 {0xF8017C, 0xFB0030, 0xFB0000}, // Fn70 (Option1 On) 494 {0xF8017C, 0xFB0031, 0xFB0000}, // Fn71 (Opiton1 Off) 495 {0xF8017C, 0xFB0032, 0xFB0000}, // Fn72 (Option2 On) 496 {0xF8017C, 0xFB0033, 0xFB0000}, // Fn73 (Option2 Off) 497 {0xF8017C, 0xFB0034, 0xFB0000}, // Fn74 (Load) 498 {0xF8017C, 0xFB0035, 0xFB0000}, // Fn75 (Unload) 499 {0xF8017C, 0xFB0036, 0xFB0000}, // Fn76 (FRED On) 500 {0xF8017C, 0xFB0037, 0xFB0000}, // Fn77 (FRED Off) 501 {0xF8017C, 0xFB0038, 0xFB0000}, // Fn78 (Flat Wheel On) 502 {0xF8017C, 0xFB0039, 0xFB0000}, // Fn79 (Flat Wheel Off) 503 {0xF8017C, 0xFB003A, 0xFB0000}, // Fn80 (Game On) 504 {0xF8017C, 0xFB003B, 0xFB0000}, // Fn81 (Game Off) 505 506 // Only TMCC1 Passenger/Dining Cars 507 508 // Only TMCC1 Crane/Boom Car 509 510 // TMCC2 Smoke System 511 {0xF8017C, 0xFB0000, 0xFB0000}, // Fn82 (Smoke System Off) 512 {0xF8017C, 0xFB0001, 0xFB0000}, // Fn83 (Smoke System Low) 513 {0xF8017C, 0xFB0002, 0xFB0000}, // Fn84 (Smoke System Med) 514 {0xF8017C, 0xFB0003, 0xFB0000}, // Fn85 (Smoke System High) 515 516 // TRMCC2_200 Unassigned FnKeys 517 {0xF8012E}, // Fnxx Code to Trigger SerialMonFrame Message/Unassigned FnKey 518 {0xF8012E}, // Fnxx Code to Trigger SerialMonFrame Message/Unassigned FnKey 519 520 // TMCC2_200 Aux FnKeys 521 {0xF80108}, // Fnxx (Aux1 Off) 522 {0xF80109}, // Fnxx (Aux1 Option 1 - On While Held) 523 {0xF8010A}, // Fnxx (Aux1 Option 2 - Toggle On/Toggle Off) 524 {0xF8010B}, // Fnxx (Aux1 On) 525 {0xF8010C}, // Fnxx (Aux2 Off) 526 {0xF8010D}, // Fnxx (Aux2 Option 1 - Toggle On/Toggle Off) 527 {0xF8010E}, // Fnxx (Aux2 Option 2 - On While Held) 528 {0xF8010F}, // Fnxx (Aux2 On) 529 530 }; 531 532 533 int previousValue; 534 int newValue; 535 536 /** 537 * Set the speed. 538 * 539 * @param speed Number from 0 to 1; less than zero is emergency stop 540 */ 541 542 @Override 543 public void setSpeedSetting(float speed) { 544 float oldSpeed; 545 synchronized(this) { 546 oldSpeed = this.speedSetting; 547 this.speedSetting = speed; 548 } 549 550 // Option TMCC2_200 "Absolute" speed steps 551 if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 552 553 // TMCC2 Legacy 200 speed step mode 554 int value = (int) (199 * speed); // max value to send is 199 in 200 step mode 555 if (value > 199) { 556 // max possible speed 557 value = 199; 558 } 559 SerialMessage m = new SerialMessage(); 560 m.setOpCode(0xF8); 561 562 if (value < 0) { 563 // System HALT (immediate stop; ALL) 564 m.putAsWord(0xFF8B); 565 566 // send to layout (send 4 times to ensure received) 567 tc.sendSerialMessage(m, null); 568 tc.sendSerialMessage(m, null); 569 tc.sendSerialMessage(m, null); 570 tc.sendSerialMessage(m, null); 571 572 } else { 573 // normal speed setting 574 m.putAsWord(0x0000 + (address.getNumber() << 9) + value); 575 576 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 577 tc.sendSerialMessage(m, null); 578 tc.sendSerialMessage(m, null); 579 } 580 } 581 582 // Option TMCC1_100 "Relative" speed steps 583 if (speedStepMode == jmri.SpeedStepMode.TMCC1_100) { 584 585 /** 586 * TMCC1 MedMomentum, HighMomentum and ERR 100 speed step mode 587 * purpose is to increase resolution of 32 bits 588 * across 100 throttle 'clicks' by dividing value by 3 589 * and setting top speed at 32 590 */ 591 592 int value = (int) (99 * speed); // max value to send is 99 in 100 step mode 593 if (value > 99) { 594 // max possible speed step 595 value = 99; 596 } 597 SerialMessage m = new SerialMessage(); 598 m.setOpCode(0xFE); 599 600 if (value < 0) { 601 // System HALT (immediate stop; ALL) 602 m.putAsWord(0xFFFF); 603 604 // send to layout (send 4 times to ensure received) 605 tc.sendSerialMessage(m, null); 606 tc.sendSerialMessage(m, null); 607 tc.sendSerialMessage(m, null); 608 tc.sendSerialMessage(m, null); 609 } 610 if (value == 0) { 611 // normal speed step setting 612 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 613 614 // send to layout (send twice is set, but number of sends may need to be adjusted depending on efficiency) 615 tc.sendSerialMessage(m, null); 616 tc.sendSerialMessage(m, null); 617 } 618 619 if (value > 0) { 620 newValue = value; 621 if (newValue > previousValue) { 622 // increase TMCC "Relative" speed +1 (repeat * valueChange46) 623 int valueChange46 = (newValue - previousValue); 624 for (int i = 0x0000; i < valueChange46; i++) { 625 m.putAsWord(0x0046 + address.getNumber() * 128); 626 tc.sendSerialMessage(m, null); 627 } 628 } 629 if (newValue < previousValue) { 630 // decrease TMCC "Relative" speed -1 (repeat * valueChange44) 631 int valueChange44 = (previousValue - newValue); 632 for (int j = 0x0000; j < valueChange44; j++) { 633 m.putAsWord(0x0044 + address.getNumber() * 128); 634 tc.sendSerialMessage(m, null); 635 } 636 } 637 previousValue = newValue; 638 } 639 } 640 641 // Option TMCC2_32 "Absolute" speed steps 642 if (speedStepMode == jmri.SpeedStepMode.TMCC2_32) { 643 644 // TMCC2 Legacy 32 speed step mode 645 int value = (int) (32 * speed); 646 if (value > 31) { 647 // max possible speed 648 value = 31; 649 } 650 SerialMessage m = new SerialMessage(); 651 m.setOpCode(0xF8); 652 653 if (value < 0) { 654 // System HALT (immediate stop; ALL) 655 m.putAsWord(0xFF8B); 656 657 // send to layout (send 4 times to ensure received) 658 tc.sendSerialMessage(m, null); 659 tc.sendSerialMessage(m, null); 660 tc.sendSerialMessage(m, null); 661 tc.sendSerialMessage(m, null); 662 663 } else { 664 // normal speed setting 665 m.putAsWord(0x0160 + address.getNumber() * 512 + value); 666 667 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 668 tc.sendSerialMessage(m, null); 669 tc.sendSerialMessage(m, null); 670 } 671 } 672 673 // Option TMCC1_32 "Absolute" speed steps 674 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32) { 675 676 // TMCC1 32 speed step mode 677 int value = (int) (32 * speed); 678 if (value > 31) { 679 // max possible speed 680 value = 31; 681 } 682 SerialMessage m = new SerialMessage(); 683 m.setOpCode(0xFE); 684 685 if (value < 0) { 686 // System HALT (immediate stop; ALL) 687 m.putAsWord(0xFFFF); 688 689 // send to layout (send 4 times to ensure received) 690 tc.sendSerialMessage(m, null); 691 tc.sendSerialMessage(m, null); 692 tc.sendSerialMessage(m, null); 693 tc.sendSerialMessage(m, null); 694 695 } else { 696 // normal speed setting 697 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 698 699 // send to layout (send twice is set, but number of sends may need to be adjusted depending on efficiency) 700 tc.sendSerialMessage(m, null); 701 tc.sendSerialMessage(m, null); 702 } 703 } 704 705 synchronized(this) { 706 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 707 } 708 record(speed); 709 } 710 711 /** 712 * {@inheritDoc} 713 */ 714 @Override 715 public void setIsForward(boolean forward) { 716 boolean old = isForward; 717 isForward = forward; 718 719 // notify layout 720 SerialMessage m = new SerialMessage(); 721 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32 || speedStepMode == jmri.SpeedStepMode.TMCC1_100) { 722 m.setOpCode(0xFE); 723 if (forward) { 724 m.putAsWord(0x0000 + address.getNumber() * 128); 725 setSpeedSetting(0.0f); 726 } else { 727 m.putAsWord(0x0003 + address.getNumber() * 128); 728 setSpeedSetting(0.0f); 729 } 730 } 731 732 if (speedStepMode == jmri.SpeedStepMode.TMCC2_32 || speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 733 m.setOpCode(0xF8); 734 if (forward) { 735 m.putAsWord(0x0100 + address.getNumber() * 512); 736 setSpeedSetting(0.0f); 737 } else { 738 m.putAsWord(0x0103 + address.getNumber() * 512); 739 setSpeedSetting(0.0f); 740 } 741 } 742 743 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 744 tc.sendSerialMessage(m, null); 745 tc.sendSerialMessage(m, null); 746 747 firePropertyChange(ISFORWARD, old, isForward); 748 } 749 750 /** 751 * Send these messages to the layout and repeat 752 * while button is pressed/on. 753 * @param value Content of message to be sent in three bytes 754 * @param func The number of the function being addressed 755 */ 756 protected void sendFnToLayout(int value, int func) { 757 758 if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 759 if (func == 24) { 760 setSpeedSetting(0.055f); 761 return; 762 } 763 if (func == 25) { 764 setSpeedSetting(0.205f); 765 return; 766 } 767 if (func == 26) { 768 setSpeedSetting(0.355f); 769 return; 770 } 771 if (func == 27) { 772 setSpeedSetting(0.505f); 773 return; 774 } 775 if (func == 28) { 776 setSpeedSetting(0.705f); 777 return; 778 } 779 if (func == 29) { 780 setSpeedSetting(1.0f); 781 return; 782 } 783 } 784 785 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32 || speedStepMode == jmri.SpeedStepMode.TMCC1_100 || speedStepMode == jmri.SpeedStepMode.TMCC2_32) { 786 if (func == 24) { 787 setSpeedSetting(0.130f); 788 return; 789 } 790 if (func == 25) { 791 setSpeedSetting(0.320f); 792 return; 793 } 794 if (func == 26) { 795 setSpeedSetting(0.450f); 796 return; 797 } 798 if (func == 27) { 799 setSpeedSetting(0.580f); 800 return; 801 } 802 if (func == 28) { 803 setSpeedSetting(0.775f); 804 return; 805 } 806 if (func == 29) { 807 setSpeedSetting(1.0f); 808 return; 809 } 810 } 811 812 /** 813 * This code sends FnKey presses to the command station. 814 * Send once is set, per the need of TMCC multi-key commands that 815 * do not work when a specific command sequence is not followed. 816 * If these multi-key commands are integrated into single FnKeys, 817 * this "send" section can be converted back to "send twice" as 818 * the other send sequences througout tmcc\SerialThrottle.java. 819 */ 820 821 repeatFunctionSendWhileOn(value, func); // Single FnKey Press, Single Send; FnKey Held, Repeats FnKey while pressed. 822 } 823 824 /** 825 * This code block is necessary to support the send repeats of 826 * the repeatFunctionSendWhileOn(value, func); code above. 827 * This code block "Sends Again" if FkKey is still pressed/on, and 828 * repeats per the interval set in static final int REPEAT_TIME. 829 */ 830 831 static final int REPEAT_TIME = 150; 832 833 protected void repeatFunctionSendWhileOn(int value, int func) { 834 if (getFunction(func)) { 835 tc.sendSerialMessage(new SerialMessage(value), null); 836 jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> { 837 repeatFunctionSendWhileOn(value, func); 838 }, REPEAT_TIME); 839 } 840 } 841 842 /* 843 * Set the speed step value. 844 * <p> 845 * The speed step range is from 32 steps, to 100 steps, to 200 steps 846 * 847 * @param mode only TMCC1_32, TMCC2_32, TMCC1_100 and TMCC2_200 are allowed 848 */ 849 @Override 850 public void setSpeedStepMode(jmri.SpeedStepMode mode) { 851 if (mode == jmri.SpeedStepMode.TMCC1_32 || mode == jmri.SpeedStepMode.TMCC2_32 || mode == jmri.SpeedStepMode.TMCC1_100 || mode == jmri.SpeedStepMode.TMCC2_200) { 852 super.setSpeedStepMode(mode); 853 } 854 } 855 856 /** 857 * {@inheritDoc} 858 */ 859 @Override 860 public void throttleDispose() { 861 finishRecord(); 862 } 863 864}