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, 0x000015}, // 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 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 Freight/Stock Cars 218 {0x000009, 0x000013}, // Fn47 Load 219 {0x000009, 0x000012}, // Fn48 Flat Wheel Sound 220 221 // TMCC1 Passenger 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 Subway FnKeys 348 {0xF8017C, 0xFB0020, 0xFB0000}, // Fn60 (Open Doors - Left) 349 {0xF8017C, 0xFB0021, 0xFB0000}, // Fn61 (Close Doors - Left) 350 {0xF8017C, 0xFB0022, 0xFB0000}, // Fn62 (Open Doors - Right) 351 {0xF8017C, 0xFB0023, 0xFB0000}, // Fn63 (Close Doors - Right) 352 {0xF8017C, 0xFB0010, 0xFB0000}, // Fn64 (Pantagraph - Up/F) 353 {0xF8017C, 0xFB0011, 0xFB0000}, // Fn65 (Pantagraph - Down/F) 354 {0xF8017C, 0xFB0012, 0xFB0000}, // Fn66 (Pantagraph - Up/R) 355 {0xF8017C, 0xFB0013, 0xFB0000}, // Fn67 (Pantagraph - Down/R) 356 357 // TMCC2 Freight/Stock Cars 358 {0xF8017C, 0xFB0030, 0xFB0000}, // Fn68 (Option1 On) 359 {0xF8017C, 0xFB0031, 0xFB0000}, // Fn69 (Opiton1 Off) 360 {0xF8017C, 0xFB0032, 0xFB0000}, // Fn70 (Option2 On) 361 {0xF8017C, 0xFB0033, 0xFB0000}, // Fn71 (Option2 Off) 362 {0xF8017C, 0xFB0034, 0xFB0000}, // Fn72 (Load) 363 {0xF8017C, 0xFB0035, 0xFB0000}, // Fn73 (Unload) 364 {0xF8017C, 0xFB0036, 0xFB0000}, // Fn74 (FRED On) 365 {0xF8017C, 0xFB0037, 0xFB0000}, // Fn75 (FRED Off) 366 {0xF8017C, 0xFB0038, 0xFB0000}, // Fn76 (Flat Wheel On) 367 {0xF8017C, 0xFB0039, 0xFB0000}, // Fn77 (Flat Wheel Off) 368 {0xF8017C, 0xFB003A, 0xFB0000}, // Fn78 (Game On) 369 {0xF8017C, 0xFB003B, 0xFB0000}, // Fn79 (Game Off) 370 371 // TMCC2 Smoke System 372 {0xF8017C, 0xFB0000, 0xFB0000}, // Fn80 (Smoke System Off) 373 {0xF8017C, 0xFB0001, 0xFB0000}, // Fn81 (Smoke System Low) 374 {0xF8017C, 0xFB0002, 0xFB0000}, // Fn82 (Smoke System Med) 375 {0xF8017C, 0xFB0003, 0xFB0000}, // Fn83 (Smoke System High) 376 377 // TRMCC2_32 Unassigned FnKeys 378 {0xF8012E}, // Fn84 Code to Trigger SerialMonFrame Message/Unassigned FnKey 379 {0xF8012E}, // Fn85 Code to Trigger SerialMonFrame Message/Unassigned FnKey 380 381 // TRMCC2_32 Aux FnKeys 382 {0xF80108}, // Fnxx (Aux1 Off) 383 {0xF80109}, // Fnxx (Aux1 Option 1 - On While Held) 384 {0xF8010A}, // Fnxx (Aux1 Option 2 - Toggle On/Toggle Off) 385 {0xF8010B}, // Fnxx (Aux1 On) 386 {0xF8010C}, // Fnxx (Aux2 Off) 387 {0xF8010D}, // Fnxx (Aux2 Option 1 - Toggle On/Toggle Off) 388 {0xF8010E}, // Fnxx (Aux2 Option 2 - On While Held) 389 {0xF8010F}, // Fnxx (Aux2 On) 390 391}; 392 393 // TMCC 2 Legacy Function Keys to trigger with TMCC2_200 speed steps. 394 private final static long[][] SERIAL_FUNCTION_CODES_TMCC2_200 = new long[][] { 395 396 // TMCC2_200 Remote - Defined FnKeys 397 {0xF8010D}, // Fn0 (Headlamp) 398 {0xF8011D}, // Fn1 (Bell) 399 {0xF8011C}, // Fn2 (Horn/Whistle) 400 {0xF80105}, // Fn3 (F - Open Front Coupler) 401 {0xF80106}, // Fn4 (R - Open Rear Coupler) 402 403 // TMCC2_200 Remote - Defined KeyPad FnKeys 404 {0xF80111}, {0xF80112}, {0xF80113}, /* Fn5-7 */ // 1-2-3 405 {0xF80114}, {0xF80115}, {0xF80116}, /* Fn8-10 */ // 4-5-6 406 {0xF80117}, {0xF80118}, {0xF80119}, /* Fn11-13 */ // 7-8-9 407 {0xF80110}, /* Fn14 */ // 0 408 409 // TMCC2_200 Remote - Defined FnKeys 410 {0xF80109}, // Fn15 (Aux1) 411 {0xF8011E}, // Fn16 (Letoff Sound) 412 {0xF80104}, // Fn17 (Boost) 413 {0xF80107}, // Fn18 (Brake) 414 {0xF80128}, // Fn19 (Momentum Low) 415 {0xF80129}, // Fn20 (Momentum Medium) 416 {0xF8012A}, // Fn21 (Momentum High) 417 {0xF8012B}, // Fn22 (Set) 418 {0xF8011F}, // Fn23 (Horn 2) 419 420 // TMCC2_200 RR Speed FnKeys 421 {0xF8000A}, // Fn24 ( 10) 5mph 422 {0xF80028}, // Fn25 ( 40) 20mph 423 {0xF80046}, // Fn26 ( 70) 35mph 424 {0xF80064}, // Fn27 (100) 50mph 425 {0xF8008C}, // Fn28 (140) 70mph 426 {0xF800C7}, // Fn29 (199) Full 427 428 // TMCC2_200 Extended Lighting FnKeys 429 {0xF8017D, 0xFB00E8, 0xFB0000}, // Fn30 (Mars Lt On) 430 {0xF8017D, 0xFB00E9, 0xFB0000}, // Fn31 (Mars Lt Off) 431 432 {0xF8017D, 0xFB00D0, 0xFB0000}, // Fn32 (Ground Lt On) 433 {0xF8017D, 0xFB00D1, 0xFB0000}, // Fn33 (Ground Lt Off) 434 {0xF8017D, 0xFB00D2, 0xFB0000}, // Fn34 (Ground Lt Auto) 435 436 {0xF8017D, 0xFB00A0, 0xFB0000}, // Fn35 (DogHouse On) 437 {0xF8017D, 0xFB00A1, 0xFB0000}, // Fn36 (DogHouse Off) 438 439 {0xF8017D, 0xFB00CC, 0xFB0000}, // Fn37 (Tender Marker On) 440 {0xF8017D, 0xFB00CD, 0xFB0000}, // Fn38 (Tender Marker Off) 441 442 {0xF8017D, 0xFB00F4, 0xFB0000}, // Fn39 (Rule 17 On) 443 {0xF8017D, 0xFB00F5, 0xFB0000}, // Fn40 (Rule 17 Off) 444 {0xF8017D, 0xFB00F6, 0xFB0000}, // Fn41 (Rule 17 Auto) 445 446 {0xF8017D, 0xFB00C0, 0xFB0000}, // Fn42 (Ditch Lt On) 447 {0xF8017D, 0xFB00C1, 0xFB0000}, // Fn43 (Ditch Lt On; Pulse Off with Horn) 448 {0xF8017D, 0xFB00C2, 0xFB0000}, // Fn44 (Ditch Lt Off; Pulse On with Horn) 449 {0xF8017D, 0xFB00C3, 0xFB0000}, // Fn45 (Ditch Lt Off) 450 451 {0xF8017D, 0xFB00F0, 0xFB0000}, // Fn46 (Cab Lt On) 452 {0xF8017D, 0xFB00F1, 0xFB0000}, // Fn47 (Cab Lt Off) 453 {0xF8017D, 0xFB00F2, 0xFB0000}, // Fn48 (Cab Lt Auto) 454 455 {0xF8017D, 0xFB00C8, 0xFB0000}, // Fn49 (Loco Marker On) 456 {0xF8017D, 0xFB00C9, 0xFB0000}, // Fn50 (Loco Marker Off) 457 458 {0xF8017D, 0xFB00B0, 0xFB0000}, // Fn51 (Hazard Lt On) 459 {0xF8017D, 0xFB00B1, 0xFB0000}, // Fn52 (Hazard Lt Off) 460 {0xF8017D, 0xFB00B2, 0xFB0000}, // Fn53 (Hazard Lt Auto) 461 462 {0xF8017D, 0xFB00E0, 0xFB0000}, // Fn54 (Strobe Lt On - SingleFlash) 463 {0xF8017D, 0xFB00E1, 0xFB0000}, // Fn55 (Strobe Lt On - DoubleFlash) 464 {0xF8017D, 0xFB00E2, 0xFB0000}, // Fn56 (Strobe Lt Off) 465 466 {0xF8017D, 0xFB00F8, 0xFB0000}, // Fn57 (Car Cabin Lt On) 467 {0xF8017D, 0xFB00F9, 0xFB0000}, // Fn58 (Car Cabin Lt Off) 468 {0xF8017D, 0xFB00FA, 0xFB0000}, // Fn59 (Car Cabin Lt Auto) 469 470 // TMCC2 Subway FnKeys 471 {0xF8017C, 0xFB0020, 0xFB0000}, // Fn60 (Open Doors - Left) 472 {0xF8017C, 0xFB0021, 0xFB0000}, // Fn61 (Close Doors - Left) 473 {0xF8017C, 0xFB0022, 0xFB0000}, // Fn62 (Open Doors - Right) 474 {0xF8017C, 0xFB0023, 0xFB0000}, // Fn63 (Close Doors - Right) 475 {0xF8017C, 0xFB0010, 0xFB0000}, // Fn64 (Pantagraph - Up/F) 476 {0xF8017C, 0xFB0011, 0xFB0000}, // Fn65 (Pantagraph - Down/F) 477 {0xF8017C, 0xFB0012, 0xFB0000}, // Fn66 (Pantagraph - Up/R) 478 {0xF8017C, 0xFB0013, 0xFB0000}, // Fn67 (Pantagraph - Down/R) 479 480 // TMCC2 Freight/Stock Cars 481 {0xF8017C, 0xFB0030, 0xFB0000}, // Fn68 (Option1 On) 482 {0xF8017C, 0xFB0031, 0xFB0000}, // Fn69 (Opiton1 Off) 483 {0xF8017C, 0xFB0032, 0xFB0000}, // Fn70 (Option2 On) 484 {0xF8017C, 0xFB0033, 0xFB0000}, // Fn71 (Option2 Off) 485 {0xF8017C, 0xFB0034, 0xFB0000}, // Fn72 (Load) 486 {0xF8017C, 0xFB0035, 0xFB0000}, // Fn73 (Unload) 487 {0xF8017C, 0xFB0036, 0xFB0000}, // Fn74 (FRED On) 488 {0xF8017C, 0xFB0037, 0xFB0000}, // Fn75 (FRED Off) 489 {0xF8017C, 0xFB0038, 0xFB0000}, // Fn76 (Flat Wheel On) 490 {0xF8017C, 0xFB0039, 0xFB0000}, // Fn77 (Flat Wheel Off) 491 {0xF8017C, 0xFB003A, 0xFB0000}, // Fn78 (Game On) 492 {0xF8017C, 0xFB003B, 0xFB0000}, // Fn79 (Game Off) 493 494 // TMCC2 Smoke System 495 {0xF8017C, 0xFB0000, 0xFB0000}, // Fn80 (Smoke System Off) 496 {0xF8017C, 0xFB0001, 0xFB0000}, // Fn81 (Smoke System Low) 497 {0xF8017C, 0xFB0002, 0xFB0000}, // Fn82 (Smoke System Med) 498 {0xF8017C, 0xFB0003, 0xFB0000}, // Fn83 (Smoke System High) 499 500 // TRMCC2_200 Unassigned FnKeys 501 {0xF8012E}, // Fn84 Code to Trigger SerialMonFrame Message/Unassigned FnKey 502 {0xF8012E}, // Fn85 Code to Trigger SerialMonFrame Message/Unassigned FnKey 503 504 // TMCC2_200 Aux FnKeys 505 {0xF80108}, // Fnxx (Aux1 Off) 506 {0xF80109}, // Fnxx (Aux1 Option 1 - On While Held) 507 {0xF8010A}, // Fnxx (Aux1 Option 2 - Toggle On/Toggle Off) 508 {0xF8010B}, // Fnxx (Aux1 On) 509 {0xF8010C}, // Fnxx (Aux2 Off) 510 {0xF8010D}, // Fnxx (Aux2 Option 1 - Toggle On/Toggle Off) 511 {0xF8010E}, // Fnxx (Aux2 Option 2 - On While Held) 512 {0xF8010F}, // Fnxx (Aux2 On) 513 514 }; 515 516 /** 517 * Set the speed. 518 * 519 * @param speed Number from 0 to 1; less than zero is emergency stop 520 */ 521 @Override 522 public void setSpeedSetting(float speed) { 523 float oldSpeed; 524 synchronized(this) { 525 oldSpeed = this.speedSetting; 526 this.speedSetting = speed; 527 } 528 529 // send to layout option 200 speed steps 530 if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 531 532 // TMCC2 Legacy 200 speed step mode 533 int value = (int) (199 * speed); // max value to send is 199 in 200 step mode 534 if (value > 199) { 535 // max possible speed 536 value = 199; 537 } 538 SerialMessage m = new SerialMessage(); 539 m.setOpCode(0xF8); 540 541 if (value < 0) { 542 // System HALT (immediate stop; ALL) 543 m.putAsWord(0xFF8B); 544 } else { 545 // normal speed setting 546 m.putAsWord(0x0000 + (address.getNumber() << 9) + value); 547 } 548 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 549 tc.sendSerialMessage(m, null); 550 tc.sendSerialMessage(m, null); 551 } 552 553 // send to layout option 100 speed steps 554 if (speedStepMode == jmri.SpeedStepMode.TMCC1_100) { 555 556 /** 557 * TMCC1 ERR 100 speed step mode 558 * purpose is to increase resolution of 32 bits 559 * across 100 throttle 'clicks' by dividing value by 3 560 * and setting top speed at 32 561 */ 562 int value = (int) (99 * speed); // max value to send is 99 in 100 step mode 563 if (value > 93) { 564 // max possible speed step 565 value = 93; 566 } 567 SerialMessage m = new SerialMessage(); 568 m.setOpCode(0xFE); 569 570 if (value < 0) { 571 // System HALT (immediate stop; ALL) 572 m.putAsWord(0xFFFF); 573 } 574 if (value >= 0) { 575 // normal speed step setting 576 m.putAsWord(0x0060 + address.getNumber() * 128 + value / 3); 577 } 578 579 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 580 tc.sendSerialMessage(m, null); 581 tc.sendSerialMessage(m, null); 582 } 583 584 // send to layout option TMCC2 32 speed steps 585 if (speedStepMode == jmri.SpeedStepMode.TMCC2_32) { 586 587 // TMCC2 Legacy 32 speed step mode 588 int value = (int) (32 * speed); 589 if (value > 31) { 590 // max possible speed 591 value = 31; 592 } 593 SerialMessage m = new SerialMessage(); 594 m.setOpCode(0xF8); 595 596 if (value < 0) { 597 // System HALT (immediate stop; ALL) 598 m.putAsWord(0xFF8B); 599 } else { 600 // normal speed setting 601 m.putAsWord(0x0160 + address.getNumber() * 512 + value); 602 } 603 604 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 605 tc.sendSerialMessage(m, null); 606 tc.sendSerialMessage(m, null); 607 } 608 609 // send to layout option TMCC1 32 speed steps 610 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32) { 611 612 // TMCC1 32 speed step mode 613 int value = (int) (32 * speed); 614 if (value > 31) { 615 // max possible speed 616 value = 31; 617 } 618 SerialMessage m = new SerialMessage(); 619 m.setOpCode(0xFE); 620 621 if (value < 0) { 622 // System HALT (immediate stop; ALL) 623 m.putAsWord(0xFFFF); 624 } else { 625 // normal speed setting 626 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 627 } 628 629 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 630 tc.sendSerialMessage(m, null); 631 tc.sendSerialMessage(m, null); 632 } 633 634 synchronized(this) { 635 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 636 } 637 record(speed); 638 } 639 640 /** 641 * {@inheritDoc} 642 */ 643 @Override 644 public void setIsForward(boolean forward) { 645 boolean old = isForward; 646 isForward = forward; 647 648 // notify layout 649 SerialMessage m = new SerialMessage(); 650 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32 || speedStepMode == jmri.SpeedStepMode.TMCC1_100) { 651 m.setOpCode(0xFE); 652 if (forward) { 653 m.putAsWord(0x0000 + address.getNumber() * 128); 654 setSpeedSetting(0.0f); 655 } else { 656 m.putAsWord(0x0003 + address.getNumber() * 128); 657 setSpeedSetting(0.0f); 658 } 659 } 660 661 if (speedStepMode == jmri.SpeedStepMode.TMCC2_32 || speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 662 m.setOpCode(0xF8); 663 if (forward) { 664 m.putAsWord(0x0100 + address.getNumber() * 512); 665 setSpeedSetting(0.0f); 666 } else { 667 m.putAsWord(0x0103 + address.getNumber() * 512); 668 setSpeedSetting(0.0f); 669 } 670 } 671 672 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 673 tc.sendSerialMessage(m, null); 674 tc.sendSerialMessage(m, null); 675 676 firePropertyChange(ISFORWARD, old, isForward); 677 } 678 679 /** 680 * Send these messages to the layout and repeat 681 * while button is pressed/on. 682 * @param value Content of message to be sent in three bytes 683 * @param func The number of the function being addressed 684 */ 685 protected void sendFnToLayout(int value, int func) { 686 687 if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 688 if (func == 24) { 689 setSpeedSetting(0.055f); 690 return; 691 } 692 if (func == 25) { 693 setSpeedSetting(0.205f); 694 return; 695 } 696 if (func == 26) { 697 setSpeedSetting(0.355f); 698 return; 699 } 700 if (func == 27) { 701 setSpeedSetting(0.505f); 702 return; 703 } 704 if (func == 28) { 705 setSpeedSetting(0.705f); 706 return; 707 } 708 if (func == 29) { 709 setSpeedSetting(1.0f); 710 return; 711 } 712 } 713 714 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32 || speedStepMode == jmri.SpeedStepMode.TMCC1_100 || speedStepMode == jmri.SpeedStepMode.TMCC2_32) { 715 if (func == 24) { 716 setSpeedSetting(0.130f); 717 return; 718 } 719 if (func == 25) { 720 setSpeedSetting(0.320f); 721 return; 722 } 723 if (func == 26) { 724 setSpeedSetting(0.450f); 725 return; 726 } 727 if (func == 27) { 728 setSpeedSetting(0.580f); 729 return; 730 } 731 if (func == 28) { 732 setSpeedSetting(0.775f); 733 return; 734 } 735 if (func == 29) { 736 setSpeedSetting(1.0f); 737 return; 738 } 739 } 740 741 /** 742 * This code sends FnKey presses to the command station. 743 * Send once is set, per the need of TMCC multi-key commands that 744 * do not work when a specific command sequence is not followed. 745 * If these multi-key commands are integrated into single FnKeys, 746 * this "send" section can be converted back to "send twice" as 747 * the other send sequences througout tmcc\SerialThrottle.java. 748 */ 749 750 repeatFunctionSendWhileOn(value, func); // Single FnKey Press, Single Send; FnKey Held, Repeats FnKey while pressed. 751 } 752 753 /** 754 * This code block is necessary to support the send repeats of 755 * the repeatFunctionSendWhileOn(value, func); code above. 756 * This code block "Sends Again" if FkKey is still pressed/on, and 757 * repeats per the interval set in static final int REPEAT_TIME. 758 */ 759 760 static final int REPEAT_TIME = 150; 761 762 protected void repeatFunctionSendWhileOn(int value, int func) { 763 if (getFunction(func)) { 764 tc.sendSerialMessage(new SerialMessage(value), null); 765 jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> { 766 repeatFunctionSendWhileOn(value, func); 767 }, REPEAT_TIME); 768 } 769 } 770 771 /* 772 * Set the speed step value. 773 * <p> 774 * Only 32 steps is available 775 * 776 * @param mode only TMCC1 32, TMCC2 32, TMCC1 100 and TMCC2 200 are allowed 777 */ 778 @Override 779 public void setSpeedStepMode(jmri.SpeedStepMode mode) { 780 if (mode == jmri.SpeedStepMode.TMCC1_32 || mode == jmri.SpeedStepMode.TMCC2_32 || mode == jmri.SpeedStepMode.TMCC1_100 || mode == jmri.SpeedStepMode.TMCC2_200) { 781 super.setSpeedStepMode(mode); 782 } 783 } 784 785 /** 786 * {@inheritDoc} 787 */ 788 @Override 789 public void throttleDispose() { 790 finishRecord(); 791 } 792 793}