001package jmri.jmrix.loconet.messageinterp; 002 003 004import java.time.LocalTime; 005import java.util.ArrayList; 006 007import jmri.InstanceManager; 008import jmri.NmraPacket; 009import jmri.Reporter; 010import jmri.ReporterManager; 011import jmri.Sensor; 012import jmri.SensorManager; 013import jmri.Turnout; 014import jmri.TurnoutManager; 015import jmri.jmrix.loconet.LnConstants; 016import jmri.jmrix.loconet.LocoNetMessage; 017import jmri.jmrix.loconet.lnsvf1.Lnsv1MessageContents; 018import jmri.jmrix.loconet.lnsvf2.Lnsv2MessageContents; 019import jmri.jmrix.loconet.uhlenbrock.LncvMessageContents; 020import jmri.util.StringUtil; 021 022import org.apache.commons.lang3.StringUtils; 023import org.slf4j.Logger; 024import org.slf4j.LoggerFactory; 025 026/** 027 * A utility class for formatting LocoNet packets into human-readable text. 028 * <p> 029 * Note that the formatted strings end in a \n, and may contain more than one 030 * line separated by \n. Someday this should be converted to proper Java line 031 * handling. 032 * <p> 033 * Much of this file is a Java-recoding of the display.c file from the llnmon 034 * package of John Jabour. Some of the conversions involve explicit decoding of 035 * structs defined in loconet.h in that same package. Those parts are (C) 036 * Copyright 2001 Ron W. Auld. Use of these parts is by direct permission of the 037 * author. 038 * <p> 039 * This class is derived from and replaces JMRI's 040 * jmri.jmrix.loconet.locomon.Llnmon.java . 041 * <p> 042 * Many major comment blocks here are quotes from the Digitrax LocoNet(r) OPCODE 043 * SUMMARY: found in the LocoNet(r) Personal Edition 1. 044 * <p> 045 * Some of the message formats used in this class are Copyright Digitrax, Inc. 046 * and used with permission as part of the JMRI project. That permission does 047 * not extend to uses in other software products. If you wish to use this code, 048 * algorithm or these message formats outside of JMRI, please contact Digitrax 049 * Inc for separate permission. 050 * <p> 051 * Reverse engineering of OPC_MULTI_SENSE was provided by Al Silverstein, used 052 * with permission. 053 * <p> 054 * Reverse engineering of the Duplex Group/Password/Channel management was 055 * provided by Leo Bicknell with help from B. Milhaupt, used with permission. 056 * <p> 057 * Reverse-engineering of device-specific OpSw messages, throttle text message, 058 * and throttle semaphore message was provided by B. Milhaupt, used with 059 * permission. 060 * <p> 061 * Reverse-engineering of device-specific LNSV messages was provided by K. Drenth, 062 * used with permission. 063 * 064 * @author Bob Jacobsen Copyright 2001, 2002, 2003 065 * @author B. Milhaupt Copyright 2015, 2016, 2018, 2022 066 * @author Randall Wood Copyright 2016 067 * @author Michael Richardson Copyright (C) 2021 068 */ 069public class LocoNetMessageInterpret { 070 private static final String Ln_Off = Bundle.getMessage("LN_MSG_OFF"); 071 private static final String Ln_On = Bundle.getMessage("LN_MSG_ON"); 072 073 /** 074 * Format the message into a text string. 075 * <p> 076 * Where the code is unable to determine a correct interpretation, the returned 077 * string contains a message indicating that the message is not decoded followed 078 * by the individual bytes of the message (in hexadecimal). 079 * <p> 080 * Note that many message types are "poorly defined" here, with many 081 * "reverse-engineered" messages, and many that do not define actual bits. This 082 * means that this code can give interpretations that may not actually "decode" 083 * an actual message. 084 * 085 * @param l Message to parse 086 * @param turnoutPrefix "System Name" + prefix which designates the connection's 087 * Turnouts, such as "LT" 088 * @param sensorPrefix "System Name" + prefix which designates the connection's 089 * Turnouts, such as "LS" 090 * @param reporterPrefix "System Name" + prefix which designates the connection's 091 * Turnouts, such as "LR" 092 * @return String representation of the interpretation of the message 093 */ 094 public static String interpretMessage(LocoNetMessage l, String turnoutPrefix, String sensorPrefix, String reporterPrefix) { 095 096 String result; 097 098 /* 099 * 2 Byte MESSAGE OPCODES 100 * ; FORMAT = <OPC>,<CKSUM> 101 * ; 102 * 103 * 4 byte MESSAGE OPCODES 104 * ; FORMAT = <OPC>,<ARG1>,<ARG2>,<CKSUM> 105 * : 106 * CODES 0xA8 to 0xAF have responses 107 * CODES 0xB8 to 0xBF have responses 108 * 109 * 6 byte MESSAGE OPCODES 110 * ; FORMAT = <OPC>,<ARG1>,<ARG2>,<ARG3>,<ARG4>,<CKSUM> 111 * : 112 * CODES 0xC8 to 0xCF have responses 113 * CODES 0xD8 to 0xDF have responses 114 */ 115 switch (l.getOpCode()) { 116 117 /* 118 * OPC_IDLE 0x85 ;FORCE IDLE state, Broadcast emergency STOP 119 * 120 * Page 8 of LocoNet Personal Edition v1.0. 121 */ 122 case LnConstants.OPC_IDLE: { 123 return Bundle.getMessage("LN_MSG_IDLE"); 124 } 125 126 /* 127 * OPC_GPON 0x83 ;GLOBAL power ON request 128 * 129 * Page 8 of LocoNet Personal Edition v1.0. 130 */ 131 case LnConstants.OPC_GPON: { 132 return Bundle.getMessage("LN_MSG_GPON"); 133 134 } 135 136 /* 137 * OPC_GPOFF 0x82 ;GLOBAL power OFF request 138 * 139 * Page 8 of LocoNet Personal Edition v1.0. 140 */ 141 case LnConstants.OPC_GPOFF: { 142 return Bundle.getMessage("LN_MSG_GPOFF"); 143 } 144 145 /* 146 * OPC_GPBUSY 0x81 ;MASTER busy code, NULL 147 * 148 * Page 8 of LocoNet Personal Edition v1.0. 149 */ 150 case LnConstants.OPC_GPBUSY: { 151 return Bundle.getMessage("LN_MSG_MASTER_BUSY"); 152 } 153 154 case LnConstants.OPC_RE_LOCORESET_BUTTON: { 155 return Bundle.getMessage("LN_MSG_RE_LOCO_RESET"); 156 157 } 158 159 /* 160 * OPC_LOCO_ADR 0xBF ; REQ loco ADR 161 * ; Follow on message: <E7>SLOT READ 162 * ; <0xBF>,<0>,<ADR>,<CHK> REQ loco ADR 163 * ; DATA return <E7>, is SLOT#, DATA that ADR was 164 * : found in. 165 * ; IF ADR not found, MASTER puts ADR in FREE slot 166 * ; and sends DATA/STATUS return <E7>...... 167 * ; IF no FREE slot, Fail LACK,0 is returned 168 * ; [<B4>,<3F>,<0>,<CHK>] 169 * 170 * Page 8 of LocoNet Personal Edition v1.0. 171 */ 172 case LnConstants.OPC_LOCO_ADR: { 173 String locoAddress = convertToMixed(l.getElement(2), l.getElement(1)); 174 return Bundle.getMessage("LN_MSG_REQ_SLOT_FOR_ADDR", 175 locoAddress); 176 } 177 178 case LnConstants.OPC_EXP_REQ_SLOT: { 179 String locoAddress = convertToMixed(l.getElement(2), l.getElement(1)); 180 return Bundle.getMessage("LN_MSG_REQ_EXP_SLOT_FOR_ADDR", 181 locoAddress); 182 } 183 184 /* 185 * OPC_SW_ACK 0xBD ; REQ SWITCH WITH acknowledge function (not DT200) 186 * ; Follow on message: LACK 187 * ; <0xBD>,<SW1>,<SW2>,<CHK> REQ SWITCH function 188 * ; <SW1> =<0,A6,A5,A4- A3,A2,A1,A0> 189 * ; 7 ls adr bits. 190 * ; A1,A0 select 1 of 4 input pairs 191 * ; in a DS54 192 * ; <SW2> =<0,0,DIR,ON- A10,A9,A8,A7> 193 * ; Control bits and 4 MS adr bits. 194 * ; DIR=1 for Closed/GREEN 195 * ; =0 for Thrown/RED 196 * ; ON=1 for Output ON 197 * ; =0 FOR output OFF 198 * ; response is: 199 * ; <0xB4><3D><00> if DCS100 FIFO is full, rejected. 200 * ; <0xB4><3D><7F> if DCS100 accepted 201 * 202 * Page 8 of LocoNet Personal Edition v1.0. 203 */ 204 case LnConstants.OPC_SW_ACK: { 205 result = interpretOpcSwAck(l, turnoutPrefix); 206 if (!result.isEmpty()) { 207 return result; 208 } 209 break; 210 } 211 212 /* 213 * OPC_SW_STATE 0xBC ; REQ state of SWITCH 214 * ; Follow on message: LACK 215 * ; <0xBC>,<SW1>,<SW2>,<CHK> REQ state of SWITCH 216 * 217 * Page 8 of LocoNet Personal Edition v1.0. 218 */ 219 case LnConstants.OPC_SW_STATE: { 220 result = interpretOpcSwState(l, turnoutPrefix); 221 if (!result.isEmpty()) { 222 return result; 223 } 224 break; 225 } 226 227 228 /* 229 * OPC_RQ_SL_DATA 0xBB ; Request SLOT DATA/status block 230 * ; Follow on message: <E7>SLOT READ 231 * ; <0xBB>,<SLOT>,<0>,<CHK> Request SLOT DATA/status block. 232 * 233 * Page 8 of LocoNet Personal Edition v1.0. 234 */ 235 case LnConstants.OPC_RQ_SL_DATA: { 236 result = interpretOpcRqSlData(l); 237 if (!result.isEmpty()) { 238 return result; 239 } 240 break; 241 } 242 243 /* 244 * OPC_MOVE_SLOTS 0xBA ; MOVE slot SRC to DEST 245 * ; Follow on message: <E7>SLOT READ 246 * ; <0xBA>,<SRC>,<DEST>,<CHK> Move SRC to DEST if 247 * ; SRC or LACK etc is NOT IN_USE, clr SRC 248 * ; SPECIAL CASES: 249 * ; If SRC=0 ( DISPATCH GET) , DEST=dont care, 250 * ; Return SLOT READ DATA of DISPATCH Slot 251 * ; IF SRC=DEST (NULL move) then SRC=DEST is set to 252 * ; IN_USE , if legal move. 253 * ; If DEST=0, is DISPATCH Put, mark SLOT as DISPATCH 254 * ; RETURN slot status <0xE7> of DESTINATION slot 255 * ; DEST if move legal 256 * ; RETURN Fail LACK code if illegal move 257 * ; <B4>,<3A>,<0>,<chk>, illegal to move to/from 258 * ; slots 120/127 259 * 260 * Page 8 of LocoNet Personal Edition v1.0. 261 */ 262 case LnConstants.OPC_MOVE_SLOTS: { 263 result = interpretOpcMoveSlots(l); 264 if (!result.isEmpty()) { 265 return result; 266 } 267 break; 268 } 269 270// case LnConstants.OPC_EXP_SLOT_MOVE: { 271// result = interpretOpcExpMoveSlots(l); 272// if (result.length() > 0) { 273// return result; 274// } 275// break; 276// } 277 278 /* 279 * OPC_LINK_SLOTS 0xB9 ; LINK slot ARG1 to slot ARG2= 280 * ; Follow on message: <E7>SLOT READ= 281 * ; <0xB9>,<SL1>,<SL2>,<CHK> SLAVE slot SL1 to slot SL2 282 * ; Master LINKER sets the SL_CONUP/DN flags 283 * ; appropriately. Reply is return of SLOT Status 284 * ; <0xE7>. Inspect to see result of Link, invalid 285 * ; Link will return Long Ack Fail <B4>,<39>,<0>,<CHK> 286 * 287 * Page 9 of LocoNet Personal Edition v1.0. 288 */ 289 case LnConstants.OPC_LINK_SLOTS: { 290 int src = l.getElement(1); 291 int dest = l.getElement(2); 292 return Bundle.getMessage("LN_MSG_LINK_SLOTS", src, dest); 293 } 294 295 /* 296 * OPC_UNLINK_SLOTS 0xB8 ;UNLINK slot ARG1 from slot ARG2 297 * ; Follow on message: <E7>SLOT READ 298 * ; <0xB8>,<SL1>,<SL2>,<CHK> UNLINK slot SL1 from SL2 299 * ; UNLINKER executes unlink STRATEGY and returns new SLOT# 300 * ; DATA/STATUS of unlinked LOCO . Inspect data to evaluate UNLINK 301 * 302 * Page 9 of LocoNet Personal Edition v1.0. 303 */ 304 case LnConstants.OPC_UNLINK_SLOTS: { 305 int src = l.getElement(1); 306 int dest = l.getElement(2); 307 return Bundle.getMessage("LN_MSG_UNLINK_SLOTS", src, dest); 308 } // case LnConstants.OPC_UNLINK_SLOTS 309 310 /* 311 * OPC_CONSIST_FUNC 0xB6 ; SET FUNC bits in a CONSIST uplink element 312 * ; <0xB6>,<SLOT>,<DIRF>,<CHK> UP consist FUNC bits 313 * ; NOTE this SLOT adr is considered in UPLINKED slot space. 314 * 315 * Page 9 of LocoNet Personal Edition v1.0. 316 */ 317 case LnConstants.OPC_CONSIST_FUNC: { 318 result = interpretOpcConsistFunc(l); 319 if (!result.isEmpty()) { 320 return result; 321 } 322 break; 323 } 324 325 /* 326 * OPC_SLOT_STAT1 0xB5 ; WRITE slot stat1 327 * ; <0xB5>,<SLOT>,<STAT1>,<CHK> WRITE stat1 328 * 329 * Page 9 of LocoNet Personal Edition v1.0. 330 */ 331 case LnConstants.OPC_SLOT_STAT1: { 332 int slot = l.getElement(1); 333 int stat = l.getElement(2); 334 return Bundle.getMessage("LN_MSG_SLOT_STAT1", slot, stat, 335 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 336 StringUtil.twoHexFromInt(stat)), LnConstants.CONSIST_STAT(stat), 337 LnConstants.LOCO_STAT(stat), LnConstants.DEC_MODE(stat)); 338 } 339 340 /* 341 * OPC_LONG_ACK 0xB4 ; Long acknowledge 342 * ; <0xB4>,<LOPC>,<ACK1>,<CHK> Long acknowledge 343 * ; <LOPC> is COPY of OPCODE responding to (msb=0). 344 * ; LOPC=0 (unused OPC) is also VALID fail code 345 * ; <ACK1> is appropriate response code for the OPCode 346 * 347 * Page 9 of LocoNet Personal Edition v1.0. 348 */ 349 case LnConstants.OPC_LONG_ACK: { 350 result = interpretLongAck(l); 351 if (!result.isEmpty()) { 352 return result; 353 } 354 break; 355 } 356 357 /* 358 * OPC_INPUT_REP 0xB2 ; General SENSOR Input codes 359 * ; <0xB2>, <IN1>, <IN2>, <CHK> 360 * ; <IN1> =<0,A6,A5,A4- A3,A2,A1,A0>, 361 * ; 7 ls adr bits. 362 * ; A1,A0 select 1 of 4 inputs pairs in a DS54. 363 * ; <IN2> =<0,X,I,L- A10,A9,A8,A7>, 364 * ; Report/status bits and 4 MS adr bits. 365 * ; "I"=0 for DS54 "aux" inputs 366 * ; =1 for "switch" inputs mapped to 4K SENSOR space. 367 * ; 368 * ; (This is effectively a least significant adr bit when 369 * ; using DS54 input configuration) 370 * ; 371 * ; "L"=0 for input SENSOR now 0V (LO), 372 * ; =1 for Input sensor >=+6V (HI) 373 * ; "X"=1, control bit, 374 * ; =0 is RESERVED for future! 375 * 376 * Page 9 of LocoNet Personal Edition v1.0. 377 */ 378 case LnConstants.OPC_INPUT_REP: { 379 result = interpretOpcInputRep(l, sensorPrefix); 380 if (!result.isEmpty()) { 381 return result; 382 } 383 break; 384 } // case LnConstants.OPC_INPUT_REP 385 386 /* 387 * OPC_SW_REP 0xB1 ; Turnout SENSOR state REPORT 388 * ; <0xB1>,<SN1>,<SN2>,<CHK> SENSOR state REPORT 389 * ; <SN1> =<0,A6,A5,A4- A3,A2,A1,A0>, 390 * ; 7 ls adr bits. 391 * ; A1,A0 select 1 of 4 input pairs in a DS54 392 * ; <SN2> =<0,1,I,L- A10,A9,A8,A7> 393 * ; Report/status bits and 4 MS adr bits. 394 * ; this <B1> opcode encodes input levels 395 * ; for turnout feedback 396 * ; "I" =0 for "aux" inputs (normally not feedback), 397 * ; =1 for "switch" input used for 398 * ; turnout feedback for DS54 399 * ; ouput/turnout # encoded by A0-A10 400 * ; "L" =0 for this input 0V (LO), 401 * ; =1 this input > +6V (HI) 402 * ; 403 * ; alternately; 404 * ; 405 * ; <SN2> =<0,0,C,T- A10,A9,A8,A7> 406 * ; Report/status bits and 4 MS adr bits. 407 * ; this <B1> opcode encodes current OUTPUT levels 408 * ; "C" =0 if "Closed" ouput line is OFF, 409 * ; =1 "closed" output line is ON 410 * ; (sink current) 411 * ; "T" =0 if "Thrown" output line is OFF, 412 * ; =1 "thrown" output line is ON 413 * ; (sink I) 414 * 415 * Page 9 of LocoNet Personal Edition v1.0. 416 */ 417 case LnConstants.OPC_SW_REP: { 418 result = interpretOpcSwRep(l, turnoutPrefix); 419 if (!result.isEmpty()) { 420 return result; 421 } 422 break; 423 } 424 425 /* 426 * OPC_SW_REQ 0xB0 ; REQ SWITCH function 427 * ; <0xB0>,<SW1>,<SW2>,<CHK> REQ SWITCH function 428 * ; <SW1> =<0,A6,A5,A4- A3,A2,A1,A0>, 429 * ; 7 ls adr bits. 430 * ; A1,A0 select 1 of 4 input pairs in a DS54 431 * ; <SW2> =<0,0,DIR,ON- A10,A9,A8,A7> 432 * ; Control bits and 4 MS adr bits. 433 * ; DIR =1 for Closed,/GREEN, 434 * ; =0 for Thrown/RED 435 * ; ON =1 for Output ON, 436 * ; =0 FOR output OFF 437 * ; 438 * ; Note-Immediate response of <0xB4><30><00> if command failed, 439 * ; otherwise no response "A" CLASS codes 440 * 441 * Page 9 of LocoNet Personal Edition v1.0. 442 * Page 12 special form Broadcast. 443 * Page 13 special form LocoNet interrogate. 444 */ 445 case LnConstants.OPC_SW_REQ: { 446 result = interpretOpcSwReq(l, turnoutPrefix); 447 if (!result.isEmpty()) { 448 return result; 449 } 450 break; 451 } 452 453 /* 454 * OPC_LOCO_SND 0xA2 ;SET SLOT sound functions 455 * 456 * Page 10 of LocoNet Personal Edition v1.0. 457 */ 458 case LnConstants.OPC_LOCO_SND: { 459 result = interpretOpcLocoSnd(l); 460 if (!result.isEmpty()) { 461 return result; 462 } 463 break; 464 } // case LnConstants.OPC_LOCO_SND 465 466 /* 467 * OPC_LOCO_DIRF 0xA1 ;SET SLOT dir, F0-4 state 468 * 469 * Page 10 of LocoNet Personal Edition v1.0. 470 */ 471 case LnConstants.OPC_LOCO_DIRF: { 472 result = interpretOpcLocoDirf(l); 473 if (!result.isEmpty()) { 474 return result; 475 } 476 break; 477 } 478 479 /* 480 * OPC_LOCO_SPD 0xA0 ;SET SLOT speed e.g. <0xA0><SLOT#><SPD><CHK> 481 * 482 * Page 10 of LocoNet Personal Edition v1.0. 483 */ 484 case LnConstants.OPC_LOCO_SPD: { 485 result = interpretOpcLocoSpd(l); 486 if (!result.isEmpty()) { 487 return result; 488 } 489 break; 490 } 491 492 case LnConstants.OPC_EXP_SEND_FUNCTION_OR_SPEED_AND_DIR: { 493 result = interpretPocExpLocoSpdDirFunction(l); 494 if (!result.isEmpty()) { 495 return result; 496 } 497 break; 498 } 499 500 /* 501 * OPC_PANEL_QUERY 0xDF messages used by throttles to discover 502 * panels 503 * 504 * This op code is not documented by Digitrax. Some reverse engineering 505 * performed by Leo Bicknell. The opcode "name" OPC_PANEL_QUERY 506 * is not necessarily the name used by Digitrax. 507 */ 508 case LnConstants.OPC_PANEL_QUERY: { 509 result = interpretOpcPanelQuery(l); 510 if (!result.isEmpty()) { 511 return result; 512 } 513 break; 514 515 } 516 517 /* 518 * OPC_PANEL_RESPONSE 0xD7 messages used by throttles to discover 519 * panels 520 * 521 * This op code is not documented by Digitrax. Reverse engineering 522 * performed by Leo Bicknell. The opcode "name" OPC_PANEL_RESPONSE 523 * is not necessarily the name used by Digitrax. 524 */ 525 case LnConstants.OPC_PANEL_RESPONSE: { 526 result = interpretOpcPanelResponse(l); 527 if (!result.isEmpty()) { 528 return result; 529 } 530 break; 531 532 } 533 534 /* 535 * OPC_MULTI_SENSE 0xD0 messages about power management and 536 * transponding 537 * 538 * If byte 1 high nibble is 0x20 or 0x00 this is a transponding 539 * message 540 * 541 * This op code is not documented by Digitrax. Reverse engineering 542 * performed by Al Silverstein, and corrections added by B. Milhaupt. 543 */ 544 case LnConstants.OPC_MULTI_SENSE: { 545 result = interpretOpcMultiSense(l, reporterPrefix); 546 if (!result.isEmpty()) { 547 return result; 548 } 549 break; 550 } 551 552 /* 553 * ******************************************************************************************** 554 * OPC_MULTI_SENSE_LONG 0xE0 messages about transponding. 555 * 556 * This op code is not documented by Digitrax. The use of this message was observed when using a 557 * Digikeijs 5088RC. With a capable decoder, this message contains additional Railcom information 558 * (direction, speed, QoS) compared to the standard OPC_MULTI_SENSE message. 559 * 560 * Reverse engineering performed by Michael Richardson. 561 * ******************************************************************************************** 562 */ 563 case LnConstants.OPC_MULTI_SENSE_LONG: { 564 result = interpretOpcMultiSenseLong(l, reporterPrefix); 565 if (!result.isEmpty()) { 566 return result; 567 } 568 break; 569 } 570 571 /* 572 * ******************************************************************************************** 573 * OPC_WR_SL_DATA 0xEF ; WRITE SLOT DATA, 10 bytes * ; Follow on 574 * message: LACK * ; <0xEF>,<0E>,<SLOT#>,<STAT>,<ADR>,<SPD>,<DIRF>, 575 * * ; <TRK>,<SS2>,<ADR2>,<SND>,<ID1>,<ID2>,<CHK> * ; SLOT DATA 576 * WRITE, 10 bytes data /14 byte MSG * 577 * ********************************************************************************************** 578 * OPC_SL_RD_DATA 0xE7 ; SLOT DATA return, 10 bytes * ; 579 * <0xE7>,<0E>,<SLOT#>,<STAT>,<ADR>,<SPD>,<DIRF>, * ; 580 * <TRK>,<SS2>,<ADR2>,<SND>,<ID1>,<ID2>,<CHK> * ; SLOT DATA READ, 10 581 * bytes data /14 byte MSG * ; * ; NOTE; If STAT2.2=0 EX1/EX2 582 * encodes an ID#, * ; [if STAT2.2=1 the STAT.3=0 means EX1/EX2 * ; 583 * are ALIAS] * ; * ; ID1/ID2 are two 7 bit values encoding a 14 bit 584 * * ; unique DEVICE usage ID. * ; * ; 00/00 - means NO ID being 585 * used * ; * ; 01/00 - ID shows PC usage. * ; to Lo nibble is TYP 586 * PC# * ; 7F/01 (PC can use hi values) * ; * ; 00/02 -SYSTEM 587 * reserved * ; to * ; 7F/03 * ; * ; 00/04 -NORMAL throttle RANGE * 588 * ; to * ; 7F/7E * 589 * ********************************************************************************************** 590 * Notes: * The SLOT DATA bytes are, in order of TRANSMISSION for 591 * <E7> READ or <EF> WRITE. * NOTE SLOT 0 <E7> read will return 592 * MASTER config information bytes. * * 0) SLOT NUMBER: * * ; 0-7FH, 593 * 0 is special SLOT, * ; 070H-07FH DIGITRAX reserved: * * 1) SLOT 594 * STATUS1: * * D7-SL_SPURGE ; 1=SLOT purge en, * ; ALSO adrSEL 595 * (INTERNAL use only) (not seen on NET!) * * D6-SL_CONUP ; 596 * CONDN/CONUP: bit encoding-Control double linked Consist List * ; 597 * 11=LOGICAL MID CONSIST , Linked up AND down * ; 10=LOGICAL 598 * CONSIST TOP, Only linked downwards * ; 01=LOGICAL CONSIST 599 * SUB-MEMBER, Only linked upwards * ; 00=FREE locomotive, no 600 * CONSIST indirection/linking * ; ALLOWS "CONSISTS of CONSISTS". 601 * Uplinked means that * ; Slot SPD number is now SLOT adr of 602 * SPD/DIR and STATUS * ; of consist. i.e. is ;an Indirect pointer. 603 * This Slot * ; has same BUSY/ACTIVE bits as TOP of Consist. TOP is 604 * * ; loco with SPD/DIR for whole consist. (top of list). * ; 605 * BUSY/ACTIVE: bit encoding for SLOT activity * * D5-SL_BUSY ; 606 * 11=IN_USE loco adr in SLOT -REFRESHED * * D4-SL_ACTIVE ; 10=IDLE 607 * loco adr in SLOT -NOT refreshed * ; 01=COMMON loco adr IN SLOT 608 * -refreshed * ; 00=FREE SLOT, no valid DATA -not refreshed * * 609 * D3-SL_CONDN ; shows other SLOT Consist linked INTO this slot, see 610 * SL_CONUP * * D2-SL_SPDEX ; 3 BITS for Decoder TYPE encoding for 611 * this SLOT * * D1-SL_SPD14 ; 011=send 128 speed mode packets * * 612 * D0-SL_SPD28 ; 010=14 step MODE * ; 001=28 step. Generate Trinary 613 * packets for this * ; Mobile ADR * ; 000=28 step. 3 BYTE PKT 614 * regular mode * ; 111=128 Step decoder, Allow Advanced DCC 615 * consisting * ; 100=28 Step decoder ,Allow Advanced DCC consisting 616 * * * 2) SLOT LOCO ADR: * * LOCO adr Low 7 bits (byte sent as ARG2 617 * in ADR req opcode <0xBF>) * * 3) SLOT SPEED: * 0x00=SPEED 0 ,STOP 618 * inertially * 0x01=SPEED 0 EMERGENCY stop * 0x02->0x7F increasing 619 * SPEED,0x7F=MAX speed * (byte also sent as ARG2 in SPD opcode 620 * <0xA0> ) * * 4) SLOT DIRF byte: (byte also sent as ARG2 in DIRF 621 * opcode <0xA1>) * * D7-0 ; always 0 * D6-SL_XCNT ; reserved , set 622 * 0 * D5-SL_DIR ; 1=loco direction FORWARD * D4-SL_F0 ; 623 * 1=Directional lighting ON * D3-SL_F4 ; 1=F4 ON * D2-SL_F3 ; 1=F3 624 * ON * D1-SL_F2 ; 1=F2 ON * D0-SL_F1 ; 1=F1 ON * * * * * 5) TRK 625 * byte: (GLOBAL system /track status) * * D7-D4 Reserved * D3 626 * GTRK_PROG_BUSY 1=Programming TRACK in this Master is BUSY. * D2 627 * GTRK_MLOK1 1=This Master IMPLEMENTS LocoNet 1.1 capability, * 628 * 0=Master is DT200 * D1 GTRK_IDLE 0=TRACK is PAUSED, B'cast EMERG 629 * STOP. * D0 GTRK_POWER 1=DCC packets are ON in MASTER, Global 630 * POWER up * * 6) SLOT STATUS: * * D3 1=expansion IN ID1/2, 631 * 0=ENCODED alias * D2 1=Expansion ID1/2 is NOT ID usage * D0 632 * 1=this slot has SUPPRESSED ADV consist-7) * * 7) SLOT LOCO ADR 633 * HIGH: * * Locomotive address high 7 bits. If this is 0 then Low 634 * address is normal 7 bit NMRA SHORT * address. If this is not zero 635 * then the most significant 6 bits of this address are used in * 636 * the first LONG address byte ( matching CV17). The second DCC LONG 637 * address byte matches CV18 * and includes the Adr Low 7 bit value 638 * with the LS bit of ADR high in the MS postion of this * track adr 639 * byte. * * Note a DT200 MASTER will always interpret this as 0. * 640 * * 8) SLOT SOUND: * * Slot sound/ Accesory Function mode II 641 * packets. F5-F8 * (byte also sent as ARG2 in SND opcode) * * D7-D4 642 * reserved * D3-SL_SND4/F8 * D2-SL_SND3/F7 * D1-SL_SND2/F6 * 643 * D0-SL_SND1/F5 1= SLOT Sound 1 function 1active (accessory 2) * * 644 * 9) EXPANSION RESERVED ID1: * * 7 bit ls ID code written by 645 * THROTTLE/PC when STAT2.4=1 * * 10) EXPANSION RESERVED ID2: * * 7 646 * bit ms ID code written by THROTTLE/PC when STAT2.4=1 * 647 * ******************************************************************************************** 648 * page 10 of LocoNet PE 649 */ 650 case LnConstants.OPC_WR_SL_DATA: 651 case LnConstants.OPC_SL_RD_DATA: { 652 result = interpretOpcWrSlDataOpcSlRdData(l); 653 if (!result.isEmpty()) { 654 return result; 655 } 656 break; 657 658 } 659 660 case LnConstants.OPC_ALM_WRITE: 661 case LnConstants.OPC_ALM_READ: { 662 // case OPC_EXP_RD_SL_DATA: // NOTE: Duplicate of definition of OPC_ALM_WRITE! 663 // case OPC_EXP_WR_SL_DATA: // NOTE: Duplicate of definition of OPC_ALM_READ! 664 665 result = interpretAlm(l); 666 if (!result.isEmpty()) { 667 return result; 668 } 669 break; 670 } 671 672 /* 673 * OPC_PEER_XFER 0xE5 ; move 8 bytes PEER to PEER, SRC->DST NO resp 674 * ; <0xE5>,<10>,<SRC>,<DSTL><DSTH>,<PXCT1>,<D1>,<D2>,<D3>,<D4>, 675 * ; <PXCT2>,<D5>,<D6>,<D7>,<D8>,<CHK> 676 * ; SRC/DST are 7 bit args. DSTL/H=0 is BROADCAST msg 677 * ; SRC=0 is MASTER 678 * ; SRC=0x70-0x7E are reserved 679 * 680 * Page 10 of LocoNet Personal Edition v1.0. 681 * 682 * Duplex group management reverse engineered by Leo Bicknell, with input from 683 * B. Milhaupt. 684 */ 685 case LnConstants.OPC_PEER_XFER: { 686 result = interpretOpcPeerXfer(l, reporterPrefix); 687 if (!result.isEmpty()) { 688 return result; 689 } 690 break; 691 } 692 693 // 0xE4 694 case LnConstants.OPC_LISSY_UPDATE: { 695 result = interpretOpcLissyUpdate(l); 696 if (!result.isEmpty()) { 697 return result; 698 } 699 break; 700 } 701 702 // 0xED 703 case LnConstants.OPC_IMM_PACKET: { 704 result = interpretOpcImmPacket(l); 705 if (!result.isEmpty()) { 706 return result; 707 } 708 break; 709 } 710 711 // 0xD3 712 case LnConstants.RE_OPC_PR3_MODE: { 713 result = interpretOpcPr3Mode(l); 714 if (!result.isEmpty()) { 715 return result; 716 } 717 break; 718 } 719 720 // 0xA3 721 case LnConstants.RE_OPC_IB2_F9_F12: { 722 result = interpretIb2F9_to_F12(l); 723 if (!result.isEmpty()) { 724 return result; 725 } 726 break; 727 } 728 729// TODO: put this back for intellibox cmd station. 730// it conflicts with loconet speed/dir etc. 731 // 0xD4 732 case LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL: { 733 result = interpretIb2Special(l); 734 if (!result.isEmpty()) { 735 return result; 736 } 737 result = interpretOpcExpMoveSlots(l); 738 if (!result.isEmpty()) { 739 return result; 740 } 741 break; 742 }// case LnConstants.RE_OPC_IB2_SPECIAL: { //0xD4 743 744 //$FALL-THROUGH$ 745 default: 746 break; 747 } // end switch over opcode type 748 749 return Bundle.getMessage("LN_MSG_UNKNOWN_MESSAGE") + 750 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 751 } 752 753 754 private static String interpretOpcPeerXfer20_1(LocoNetMessage l) { 755 switch (l.getElement(3)) { 756 case 0x08: { 757 return Bundle.getMessage("LN_MSG_DUPLEX_RECEIVER_QUERY"); 758 } 759 case 0x10: { 760 return Bundle.getMessage("LN_MSG_DUPLEX_RECEIVER_RESPONSE"); 761 } 762 default: { 763 break; 764 } 765 } 766 return ""; 767 } 768 769 private static String interpretOpcPeerXfer20_2(LocoNetMessage l) { 770 switch (l.getElement(3)) { 771 case 0x00: { 772 int channel = l.getElement(5) | ((l.getElement(4) & 0x01) << 7); 773 774 return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_SET", 775 Integer.toString(channel)); 776 } 777 case 0x08: { 778 return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_QUERY"); 779 } 780 case 0x10: { 781 int channel = l.getElement(5) | ((l.getElement(4) & 0x01) << 7); 782 783 return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_REPORT", 784 Integer.toString(channel)); 785 } 786 default: { 787 break; 788 } 789 } 790 return ""; 791 } 792 793 private static String interpretOpcPeerXfer20_3(LocoNetMessage l) { 794 // Characters appear to be 8 bit values, but transmitted over a 7 bit 795 // encoding, so high order bits are stashed in element 4 and 9. 796 char[] groupNameArray = {(char) (l.getElement(5) | ((l.getElement(4) & 0x01) << 7)), 797 (char) (l.getElement(6) | ((l.getElement(4) & 0x02) << 6)), 798 (char) (l.getElement(7) | ((l.getElement(4) & 0x04) << 5)), 799 (char) (l.getElement(8) | ((l.getElement(4) & 0x08) << 4)), 800 (char) (l.getElement(10) | ((l.getElement(9) & 0x01) << 7)), 801 (char) (l.getElement(11) | ((l.getElement(9) & 0x02) << 6)), 802 (char) (l.getElement(12) | ((l.getElement(9) & 0x04) << 5)), 803 (char) (l.getElement(13) | ((l.getElement(9) & 0x08) << 4))}; 804 String groupName = new String(groupNameArray); 805 806 // The pass code is stuffed in here, each digit in 4 bits. But again, it's a 807 // 7 bit encoding, so the MSB of the "upper" half is stuffed into byte 14. 808 int p1 = ((l.getElement(14) & 0x01) << 3) | ((l.getElement(15) & 0x70) >> 4); 809 int p2 = l.getElement(15) & 0x0F; 810 int p3 = ((l.getElement(14) & 0x02) << 2) | ((l.getElement(16) & 0x70) >> 4); 811 int p4 = l.getElement(16) & 0x0F; 812 813 // It's not clear you can set A-F from throttles or Digitrax's tools, but 814 // they do take and get returned if you send them on the wire... 815 String passcode = StringUtil.twoHexFromInt(p1) + StringUtil.twoHexFromInt(p2) 816 + StringUtil.twoHexFromInt(p3) + StringUtil.twoHexFromInt(p4); 817 818 // The MSB is stuffed elsewhere again... 819 int channel = l.getElement(17) | ((l.getElement(14) & 0x04) << 5); 820 821 // The MSB is stuffed elsewhere one last time. 822 int id = l.getElement(18) | ((l.getElement(14) & 0x08) << 4); 823 824 switch (l.getElement(3)) { 825 case 0x00: { 826 return Bundle.getMessage("LN_MSG_DUPLEX_NAME_WRITE", 827 groupName); 828 } 829 case 0x08: { 830 return Bundle.getMessage("LN_MSG_DUPLEX_NAME_QUERY"); 831 } 832 case 0x10: { 833 return Bundle.getMessage("LN_MSG_DUPLEX_NAME_REPORT", 834 groupName, passcode, channel, id); 835 } 836 default: { 837 break; 838 } 839 } 840 return ""; 841 } 842 843 private static String interpretOpcPeerXfer20_4(LocoNetMessage l) { 844 // The MSB is stuffed elsewhere again... 845 int id = l.getElement(5) | ((l.getElement(4) & 0x01) << 7); 846 847 switch (l.getElement(3)) { 848 case 0x00: { 849 return Bundle.getMessage("LN_MSG_DUPLEX_ID_SET", id); 850 } 851 case 0x08: { 852 return Bundle.getMessage("LN_MSG_DUPLEX_ID_QUERY"); 853 } 854 case 0x10: { 855 return Bundle.getMessage("LN_MSG_DUPLEX_ID_REPORT", id); 856 } 857 default: { 858 break; 859 } 860 } 861 return ""; 862 } 863 864 private static String interpretOpcPeerXfer20_7(LocoNetMessage l) { 865 if (l.getElement(3) == 0x08) { 866 return Bundle.getMessage("LN_MSG_DUPLEX_PASSWORD_QUERY"); 867 } 868 869 if ((l.getElement(5) < 0x30) || (l.getElement(5) > 0x3c) 870 || (l.getElement(6) < 0x30) || (l.getElement(6) > 0x3c) 871 || (l.getElement(7) < 0x30) || (l.getElement(7) > 0x3c) 872 || (l.getElement(8) < 0x30) || (l.getElement(8) > 0x3c)) { 873 return ""; 874 } 875 char[] groupPasswordArray = {(char) l.getElement(5), 876 (char) l.getElement(6), 877 (char) l.getElement(7), 878 (char) l.getElement(8)}; 879 if ((groupPasswordArray[0] > 0x39) && (groupPasswordArray[0] < 0x3d)) { 880 groupPasswordArray[0] += ('A' - '9' - 1); 881 } 882 if ((groupPasswordArray[1] > 0x39) && (groupPasswordArray[1] < 0x3d)) { 883 groupPasswordArray[1] += ('A' - '9' - 1); 884 } 885 if ((groupPasswordArray[2] > 0x39) && (groupPasswordArray[2] < 0x3d)) { 886 groupPasswordArray[2] += ('A' - '9' - 1); 887 } 888 if ((groupPasswordArray[3] > 0x39) && (groupPasswordArray[3] < 0x3d)) { 889 groupPasswordArray[3] += ('A' - '9' - 1); 890 } 891 String groupPassword = new String(groupPasswordArray); 892 893 switch (l.getElement(3)) { 894 case 0x00: { 895 return Bundle.getMessage("LN_MSG_DUPLEX_PASSWORD_SET", groupPassword); 896 } 897 case 0x10: { 898 return Bundle.getMessage("LN_MSG_DUPLEX_PASSWORD_REPORT", groupPassword); 899 } 900 default: { 901 break; 902 } 903 } 904 return ""; 905 } 906 907 private static String interpretOpcPeerXfer20_10(LocoNetMessage l) { 908 switch (l.getElement(3)) { 909 case 0x08: { 910 return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_SCAN_QUERY", l.getElement(5)); 911 } 912 case 0x10: { 913 // High order bit stashed in another element again. 914 int level = (l.getElement(6) & 0x7F) + ((l.getElement(4) & 0x02) << 6); 915 916 return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_SCAN_REPORT", l.getElement(5), 917 level); 918 } 919 default: { 920 break; 921 } 922 } 923 return ""; 924 } 925 926 private static String interpretOpcPeerXfer20_8(LocoNetMessage l) { 927 /* 928 * ********************************************************************************** 929 * IPL-capable device ping - OPC_RE_IPL (Device Ping Operations) * The 930 * message bytes as assigned as follows: 931 * <p> 932 * <E5> <14> <08> <GR_OP_T> <DI_F2> <DI_Ss0> 933 * <DI_Ss1> ... 934 * <p> 935 * <DI_Ss2> <DI_Ss3> <DI_U1> <00> <00> <DI_U2> 936 * <DI_U3> ... 937 * <p> 938 * <00> <00><00> <00><00> <CHK> * where: 939 * <p> 940 * <DI_F2> encodes additional bits for the Slave device serial number. * 941 * bits 7-4 always 0000b * bit 3 Bit 31 of Slave Device Serial Number * 942 * bit 2 Bit 23 of Slave Device Serial Number * bit 1 Bit 15 of Slave 943 * device Serial Number * bit 0 Bit 7 of Slave device Serial Number 944 * <p> 945 * <DI_Ss0> encodes 7 bits of the 32 bit Host device serial number: * 946 * bit 7 always 0 * bits 6-0 Bits 6:0 of Slave device serial number 947 * <p> 948 * <DI_Ss1> encodes 7 bits of the 32 bit Host device serial number: * 949 * bit 7 always 0 * bits 6-0 Bits 14:8 of Slave device serial number 950 * <p> 951 * <DI_Ss2> encodes 7 bits of the 32 bit Host device serial number: * 952 * bit 7 always 0 * bits 6-0 Bits 22:16 of Slave device serial number 953 * <p> 954 * <DI_Ss3> encodes 7 bits of the 32 bit Host device serial number: * 955 * bit 7 always 0 * bits 6-0 Bits 30:24 of Slave device serial number 956 * <p> 957 * <DI_U1> unknown data * when <GR_OP_T> = 0x08 * is always 0 * when 958 * <GR_OP_T> = 0x10 * is not reverse-engineered and may be non-zero. 959 * <p> 960 * <DI_U2> unknown data * when <GR_OP_T> = 0x08 * is always 0 * when 961 * <GR_OP_T> = 0x10 * is not reverse-engineered and may be non-zero. 962 * <p> 963 * <DI_U3> unknown data * when <GR_OP_T> = 0x08 * is always 0 * when 964 * <GR_OP_T> = 0x10 * is not reverse-engineered and may be non-zero. * * 965 * Information reverse-engineered by B. Milhaupt and used with 966 * permission * 967 * ********************************************************************************** 968 */ 969 /* OPC_RE_IPL (IPL Ping Operation) */ 970 // Operations related to DigiIPL Device "Ping" operations 971 // 972 // "Ping" request issued from DigiIPL ver 1.09 issues this message on LocoNet. 973 // The LocoNet request message encodes a serial number but NOT a device type. 974 // 975 // Depending on which devices are selected in DigiIPL when the "ping" 976 // is selected, (and probably the S/Ns of the devices attached to the LocoNet), 977 // the response is as follows: 978 // DT402D LocoNet message includes the serial number from the DT402D's 979 // Slave (RF24) serial number. If a UR92 is attached to LocoNet, 980 // it will send the message via its RF link to the addressed 981 // DT402D. (UR92 apparantly assumes that the long 802.15.4 982 // address of the DT402D is based on the serial number embedded 983 // in the LocoNet message, with the MS 32 bits based on the UR92 984 // long address MS 32 bits). If more than one UR92 is attached 985 // to LocoNet, all will pass the message to the RF interface. 986 // UR92 LocoNet message includes the Slave serial number from the UR92. 987 // These messages are not passed to the RF link by the addressed 988 // UR92. If more than one UR92 is attached to LocoNet, and the 989 // addressed UR92 hears the RF version of the LocoNet message, it 990 // will respond via the RF interface with an acknowledge packet, 991 // and a UR92 (not sure which one) responds on LocoNet with a 992 // Ping report <e5><14><08><10>. 993 // PR3 LocoNet message includes an effective serial number of all 994 // zeros. There is no LocoNet message reply generated to a 995 // request to a PR3 S/N, but there will be a reply on the PR3's 996 // computer interface if the ping request was sent via the PR3's 997 // computer interface (i.e. not from some other LocoNet agent). 998 // UT4D While it has been suggested that the UT4D supports firmware 999 // updates, the UT4D does not respond to the Ping message. 1000 // LNRP While it has been suggested that the LNRP supports firmware 1001 // updates, the LNRP does not respond to the Ping message. 1002 // 1003 // Ping Report values: 1004 // <unkn1> Seems always to be <0C>. None of the bytes relate to 1005 // Duplex Channel Number. 1006 // <unkn2> Matches byte 15 of the MAC payload of the reply sent by the 1007 // targeted UR92. 1008 // <unkn3> Unclear what this byte means. 1009 // 1010 // Information reverse-engineered by B. Milhaupt and used with permission 1011 switch (l.getElement(3)) { 1012 case 0x08: 1013 /* OPC_RE_IPL (IPL Ping Query) */ 1014 // Ping Request: <e5><14><08><08><msBits><Sn0><Sn1><Sn2><Sn3><0><0><0><0><0><0><0><0><0><0><0><Chk> 1015 1016 if ((((l.getElement(4) & 0xF) != 0) || (l.getElement(5) != 0) 1017 || (l.getElement(6) != 0) || (l.getElement(7) != 0) || (l.getElement(8) != 0)) 1018 && (l.getElement(9) == 0) && (l.getElement(10) == 0) 1019 && (l.getElement(11) == 0) && (l.getElement(12) == 0) 1020 && (l.getElement(13) == 0) && (l.getElement(14) == 0) 1021 && (l.getElement(15) == 0) && (l.getElement(16) == 0) 1022 && (l.getElement(17) == 0) && (l.getElement(18) == 0)) { 1023 1024 int hostSnInt; 1025 hostSnInt = (l.getElement(5) + (((l.getElement(4) & 0x1) == 1) ? 128 : 0)) 1026 + ((l.getElement(6) + (((l.getElement(4) & 0x2) == 2) ? 128 : 0)) * 256) 1027 + ((l.getElement(7) + (((l.getElement(4) & 0x4) == 4) ? 128 : 0)) * 256 * 256) 1028 + ((l.getElement(8) + (((l.getElement(4) & 0x8) == 8) ? 128 : 0)) * 256 * 256 * 256); 1029 return Bundle.getMessage("LN_MSG_DUPLEX_PING_REQUEST", 1030 Integer.toHexString(hostSnInt).toUpperCase()); 1031 } 1032 break; 1033 case 0x10: 1034 /* OPC_RE_IPL (IPL Ping Report) */ 1035 1036 // Ping Report: <e5><14><08><10><msbits><Sn0><Sn1><Sn2><Sn3><unkn1><0><0><Unkn2><Unkn3><0><0><0><0><0><Chk> 1037 if (((l.getElement(4) & 0xF) != 0) || (l.getElement(5) != 0) || (l.getElement(6) != 0) 1038 || (l.getElement(7) != 0) || (l.getElement(8) != 0)) { // if any serial number bit is non-zero // 1039 int hostSnInt = (l.getElement(5) + (((l.getElement(4) & 0x1) == 1) ? 128 : 0)) 1040 + ((l.getElement(6) + (((l.getElement(4) & 0x2) == 2) ? 128 : 0)) * 256) 1041 + ((l.getElement(7) + (((l.getElement(4) & 0x4) == 4) ? 128 : 0)) * 256 * 256) 1042 + ((l.getElement(8) + (((l.getElement(4) & 0x8) == 8) ? 128 : 0)) * 256 * 256 * 256); 1043 return Bundle.getMessage("LN_MSG_DUPLEX_PING_REPORT", 1044 Integer.toHexString(hostSnInt).toUpperCase(), 1045 StringUtil.twoHexFromInt(l.getElement(12) + (((l.getElement(9)) & 0x4) == 0x4 ? 128 : 0)).toUpperCase(), 1046 StringUtil.twoHexFromInt(l.getElement(13) + (((l.getElement(9)) & 0x8) == 0x8 ? 128 : 0)).toUpperCase() 1047 ); 1048 } 1049 break; 1050 default: 1051 break; 1052 } 1053 return ""; 1054 } 1055 1056 private static String interpretOpcPeerXfer20_0f(LocoNetMessage l) { 1057 String device; 1058 1059 switch (l.getElement(3)) { 1060 case 0x08: { 1061 if ((l.getElement(4) == 0) 1062 && (l.getElement(5) == 0) && (l.getElement(6) == 0) 1063 && (l.getElement(7) == 0) && (l.getElement(8) == 0) 1064 && (l.getElement(9) == 0) && (l.getElement(10) == 0) 1065 && (l.getElement(11) == 1) && (l.getElement(12) == 0) 1066 && (l.getElement(13) == 0) && (l.getElement(14) == 0) 1067 && (l.getElement(15) == 0) && (l.getElement(16) == 0) 1068 && (l.getElement(17) == 0) && (l.getElement(18) == 0)) { 1069 /* 1070 * ********************************************************************************** 1071 * IPL capable device query - RE_IPL_IDENTITY_OPERATION 1072 * (Device Query) * The message bytes are assigned as 1073 * follows: 1074 * <p> 1075 * <E5> <14> <0F> <08> <00> <00> 1076 * <00> <00> <00> <00> <00> <01> 1077 * <00> <00> ... 1078 * <p> 1079 * <00> <00> <00> <00> <00> <CHK> * * Information 1080 * reverse-engineered by B. Milhaupt and used with 1081 * permission * 1082 * ********************************************************************************** 1083 */ 1084 // Request for all IPL-queryable devices to report their presence 1085 // 1086 // Information reverse-engineered by B. Milhaupt and used with permission 1087 1088 return Bundle.getMessage("LN_MSG_IPL_DISCOVER_ALL_DEVICES"); 1089 } else if (((l.getElement(5) != 0) || (l.getElement(6) != 0))) { 1090 /* 1091 * ********************************************************************************** 1092 * IPL device query by type - RE_IPL_IDENTITY_OPERATION 1093 * (Device Query) * The message bytes are assigned as 1094 * follows: 1095 * <p> 1096 * <E5> <14> <0F> <08> <DI_Hmf> 1097 * <DI_Hst> <DI_Slv> <00> <00> <00> 1098 * <00> <01> ... 1099 * <p> 1100 * <00> <00> <00> <00> <00> <00> 1101 * <00> <CHK> * where: 1102 * <p> 1103 * <DI_Hmf> DigiIPL-capable Host device manufacturer number. 1104 * This is not * the same as an NMRA Manufacturer ID. * 0x00 1105 * Digitrax * Others No other Host device manufacturer * 1106 * numbers have been reverse- * engineered 1107 * <p> 1108 * <DI_Hst> encodes the DigiIPL-capable Host device type as 1109 * follows: * When <DI_Hmf> = 0x00 * 0x00 (0 decimal) No 1110 * Host device type reported * 0x04 (4 decimal) UT4D (Note 1111 * that UT4D, UT4 and UT4R do * not respond to this DigiIPL 1112 * * request) * 0x18 (24 decimal) RF24 - not typically a 1113 * Host device * 0x23 (35 decimal) PR3 * 0x2A (42 decimal) 1114 * DT402 (or DT402R or DT402D) * 0x33 (51 decimal) DCS51 * 1115 * 0x5C (92 decimal) UR92 * Others No other Host device 1116 * types have been * reverse-engineered * When 1117 * <DI_Hmf> is not 0x00 * All values Not reverse-engineered 1118 * <p> 1119 * <DI_Slv> encodes the DigiIPL-capable Slave device type as 1120 * follows: * When <DI_Smf> = 0x00 * 0x00 (0 decimal) Report 1121 * for all Slave device types * 0x18 (24 decimal) RF24 * 1122 * Others No other Slave device types have been * 1123 * reverse-engineered * * Information reverse-engineered by 1124 * B. Milhaupt and used with permission * 1125 * ********************************************************************************** 1126 */ 1127 // Request for IPL-queryable devices of given manufacturer and type to report 1128 // their presence 1129 // 1130 // Note that standard definitions are provided for UT4D and RF24, even though these 1131 // devices do not respond to this query. Note that UT4D will respond to IPL capable 1132 // device query with DI_Hmf = 0, DI_Hst = 0, DI_Slv = 0, and DI_Smf = 0. 1133 // 1134 // Information reverse-engineered by B. Milhaupt and used with permission 1135 1136 device = getDeviceNameFromIPLInfo(l.getElement(4), l.getElement(5)); 1137 String slave = getSlaveNameFromIPLInfo(l.getElement(4), l.getElement(6)); 1138 return Bundle.getMessage("LN_MSG_IPL_DISCOVER_SPECIFIC_DEVICES", 1139 device, slave); 1140 } 1141 break; 1142 } // end case 0x08, which decodes 0xe5 0x14 0x0f 0x08 1143 case 0x10: { 1144 return interpretOpcPeerXfer20Sub10(l); 1145 } // end case 0x10, which decodes 0xe5 0x14 0x0f 0x10 1146 default: { 1147 break; 1148 } 1149 1150 } // end of switch (l.getElement(3)), which decodes 0xe5 0x14 0x0f 0x?? 1151 1152 return ""; 1153 } 1154 1155 private static String interpretOpcPeerXfer20(LocoNetMessage l) { 1156 // Duplex Radio Management 1157 // DigiIPL messages 1158 // LocoIO, LocoServo, LocoBuffer, LocoBooster configuration messages 1159 1160 switch (l.getElement(2)) { 1161 case 0x01: { 1162 // Seems to be a query for just duplex devices. 1163 String result = interpretOpcPeerXfer20_1(l); 1164 if (!result.isEmpty()) { 1165 return result; 1166 } 1167 break; 1168 } 1169 case 0x02: { 1170 // Request Duplex Radio Channel 1171 String result = interpretOpcPeerXfer20_2(l); 1172 if (!result.isEmpty()) { 1173 return result; 1174 } 1175 break; 1176 } 1177 1178 case 0x03: { 1179 // Duplex Group Name 1180 String result = interpretOpcPeerXfer20_3(l); 1181 if (!result.isEmpty()) { 1182 return result; 1183 } 1184 break; 1185 } 1186 case 0x04: { 1187 // Duplex Group ID 1188 String result = interpretOpcPeerXfer20_4(l); 1189 if (!result.isEmpty()) { 1190 return result; 1191 } 1192 break; 1193 } 1194 case 0x07: { 1195 // Duplex Group Password 1196 String result = interpretOpcPeerXfer20_7(l); 1197 if (!result.isEmpty()) { 1198 return result; 1199 } 1200 break; 1201 } 1202 case 0x10: { 1203 // Radio Channel Noise/Activity 1204 String result = interpretOpcPeerXfer20_10(l); 1205 if (!result.isEmpty()) { 1206 return result; 1207 } 1208 break; 1209 } 1210 1211 case LnConstants.RE_IPL_PING_OPERATION: { // case 0x08, which decodes 0xe5 0x14 0x08 1212 String result = interpretOpcPeerXfer20_8(l); 1213 if (!result.isEmpty()) { 1214 return result; 1215 } 1216 break; 1217 } 1218 1219 case LnConstants.RE_IPL_IDENTITY_OPERATION: { // case 0x0f, which decodes 0xe5 0x14 0x0f 1220 // Operations related to DigiIPL "Ping", "Identify" and "Discover" 1221 String result = interpretOpcPeerXfer20_0f(l); 1222 if (!result.isEmpty()) { 1223 return result; 1224 } 1225 break; 1226 1227 } 1228 1229 default: { 1230 break; 1231 } 1232 } 1233 return ""; 1234 } 1235 1236 private static String interpretOpcPeerXfer20Sub10(LocoNetMessage l) { 1237 /* 1238 ********************************************************************************** 1239 IPL device identity report - RE_IPL_IDENTITY_OPERATION (Device 1240 Report) * The message bytes are assigned as follows: 1241 <p> 1242 <E5> <14> <0F> <08> <DI_Hmf> <DI_Hst> 1243 <DI_Slv> <DI_Smf> <DI_Hsw> ... 1244 <p> 1245 <DI_F1> <DI_Ssw> <DI_Hs0> <DI_Hs1> 1246 <DI_Hs2> <DI_F2> <DI_Ss0> ... 1247 <p> 1248 <DI_Ss1> <DI_Ss2> <DI_Ss3> <CHK> * where: 1249 <p> 1250 <DI_Hmf> DigiIPL-capable Host device manufacturer number. This is not 1251 * the same as an NMRA Manufacturer ID. * 0x00 Digitrax * Others No 1252 other Host device manufacturer * numbers have been reverse- * 1253 engineered 1254 <p> 1255 <DI_Hst> encodes the DigiIPL-capable Host device type as follows: * 1256 When 1257 <DI_Hmf> = 0x00 * 0x00 (0 decimal) No Host device type reported * 1258 0x04 (4 decimal) UT4D * 0x23 (35 decimal) PR3 * 0x2A (42 decimal) 1259 DT402 (or DT402R or DT402D) * 0x33 (51 decimal) DCS51 * 0x5C (92 1260 decimal) UR92 * Others No other Host device types have been * 1261 reverse-engineered * When <DI_Hmf> is not 0x00 * All values Not 1262 reverse-engineered 1263 <p> 1264 <DI_Slv> encodes the DigiIPL-capable Slave device type as follows: * 1265 When 1266 <DI_Smf> = 0x00 * 0x00 (0 decimal) Report for all Slave device types 1267 * 0x18 (24 decimal) RF24 * Others No other Slave device types have 1268 been * reverse-engineered 1269 <p> 1270 <DI_Smf> DigiIPL-capable Slave device manufacturer number. This is 1271 not * the same as an NMRA Manufacturer ID. * 0x00 Digitrax * Others 1272 No other Slave device manufacturer * numbers have been reverse- * 1273 engineered 1274 <p> 1275 <DI_Hsw> encodes the DigiIPL-capable Host device firmware revision * 1276 number as follows: * bit 7 always 0 * bits 6-3 Host device firmware 1277 major revision number * bits 2-0 Host device firmware minor revision 1278 number 1279 <p> 1280 <DI_F1> encodes additional bits for the Slave device firmware major * 1281 revision number and for the Host device serial number. * bits 7-4 1282 always 0000b * bit 3 Bit 23 of Host Device Serial Number * bit 2 Bit 1283 15 of Host Device Serial Number * bit 1 Bit 7 of Host Device Serial 1284 Number * bit 0 bit 4 of Slave device firmware Major number 1285 <p> 1286 <DI_Ssw> encodes the DigiIPL-capable Slave device firmware revision * 1287 number as follows: * bit 7 always 0 * bits 6-3 Host device firmware 1288 major revision number * bits 6-3 4 least-significant bits of Slave 1289 device firmware major * revision number (see also <DI_F1>[0]) * bits 1290 2-0 Slave device firmware minor revision number 1291 <p> 1292 <DI_Hs0> encodes 7 bits of the 24 bit Host device serial number: * 1293 bit 7 always 0 * bits 6-3 Bits 6-0 of Host device serial number 1294 <p> 1295 <DI_Hs1> encodes 7 bits of the 24 bit Host device serial number: * 1296 bit 7 always 0 * bits 6-3 Bits 14-9 of Host device serial number 1297 <p> 1298 <DI_Hs2> encodes 7 bits of the 24 bit Host device serial number: * 1299 bit 7 always 0 * bits 6-3 Bits 22-16 of Host device serial number 1300 <p> 1301 <DI_F2> encodes additional bits for the Slave device serial number. * 1302 bits 7-4 always 0000b * bit 3 Bit 31 of Slave Device Serial Number * 1303 bit 2 Bit 23 of Slave Device Serial Number * bit 1 Bit 15 of Slave 1304 Device Serial Number * bit 0 Bit 7 of Slave Device Serial Number 1305 <p> 1306 <DI_Ss0> encodes 7 bits of the 32 bit Slave device serial number: * 1307 bit 7 always 0 * bits 6-3 Bits 6-0 of Slave device serial number 1308 <p> 1309 <DI_Ss1> encodes 7 bits of the 32 bit Slave device serial number: * 1310 bit 7 always 0 * bits 6-3 Bits 14-9 of Slave device serial number 1311 <p> 1312 <DI_Ss2> encodes 7 bits of the 32 bit Slave device serial number: * 1313 bit 7 always 0 * bits 6-3 Bits 22-16 of Slave device serial number 1314 <p> 1315 <DI_Ss3> encodes 7 bits of the 32 bit Slave device serial number: * 1316 bit 7 always 0 * bits 6-3 Bits 30-24 of Slave device serial number * 1317 * Information reverse-engineered by B. Milhaupt and used with 1318 permission * 1319 ********************************************************************************** 1320 */ 1321 1322 // Request for one specific IPL-queryable device to return its identity information. 1323 // Expected response is of type <E5><14><10>... 1324 // 1325 // Note that standard definitions are provided for RF24, even though these 1326 // devices do not generate this report. 1327 // 1328 // Information reverse-engineered by B. Milhaupt and used with permission 1329 String hostType = getDeviceNameFromIPLInfo(l.getElement(4), l.getElement(5)); 1330 1331 String hostVer = ((l.getElement(8) & 0x78) >> 3) + "." + ((l.getElement(8) & 0x7)); 1332 1333 int hostSnInt = ((l.getElement(13) + (((l.getElement(9) & 0x8) == 8) ? 128 : 0)) * 256 * 256) 1334 + ((l.getElement(12) + (((l.getElement(9) & 0x4) == 4) ? 128 : 0)) * 256) 1335 + (l.getElement(11) + (((l.getElement(9) & 0x2) == 2) ? 128 : 0)); 1336 String hostSN = Integer.toHexString(hostSnInt).toUpperCase(); 1337 String hostInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_HOST_DETAILS", 1338 hostType, hostSN, hostVer); 1339 1340 String slaveType = getSlaveNameFromIPLInfo(l.getElement(4), l.getElement(6)); 1341 String slaveInfo; 1342 if (l.getElement(6) != 0) { 1343 String slaveVer = (((l.getElement(10) & 0x78) >> 3) + ((l.getElement(9) & 1) << 4)) + "." + ((l.getElement(10) & 0x7)); 1344 int slaveSnInt 1345 = ((l.getElement(15) + (((l.getElement(14) & 0x1) == 1) ? 128 : 0))) 1346 + ((l.getElement(16) + (((l.getElement(14) & 0x2) == 2) ? 128 : 0)) * 256) 1347 + ((l.getElement(17) + (((l.getElement(14) & 0x4) == 4) ? 128 : 0)) * 256 * 256) 1348 + ((l.getElement(18) + (((l.getElement(14) & 0x8) == 8) ? 128 : 0)) * 256 * 256 * 256); 1349 slaveInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_SLAVE_DETAILS", slaveType, 1350 Integer.toHexString(slaveSnInt).toUpperCase(), 1351 slaveVer); 1352 } else { 1353 slaveInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_SLAVE_NO_SLAVE"); 1354 } 1355 return Bundle.getMessage("LN_MSG_IPL_DEVICE_IDENTITY_REPORT", 1356 hostInfo, 1357 slaveInfo); 1358 } 1359 1360 private static String interpretOpcPeerXfer16(LocoNetMessage l) { 1361 /* 1362 * SRC=7F is THROTTLE msg xfer 1363 * ; <DSTL><DSTH> encode ID#, 1364 * ; <0><0> is THROT B'CAST 1365 * ; <PXCT1>=<0,XC2,XC1,XC0 - D4.7,D3.7,D2.7,D1.7> 1366 * ; XC0-XC2=ADR type CODE-0=7 bit Peer 1367 * TO Peer adrs * 1368 * ; 1=<D1>is SRC HI,<D2>is DST HI 1369 * ; <PXCT2>=<0,XC5,XC4,XC3 - D8.7,D7.7,D6.7,D5.7> 1370 * ; XC3-XC5=data type CODE- 0=ANSI TEXT string, 1371 * ; balance RESERVED * 1372 * **************************************************** 1373 * SV programming format 1 1374 * 1375 * This is the message format as implemented by the certain 1376 * existing devices. New designs should not use this format. The 1377 * message bytes are assigned as follows: 1378 * ; <0xE5> <0x10> <SRC> <DST> <0x01> <PXCT1> 1379 * ; <D1> <D2> <D3> <D4> <PXCT2> 1380 * ; <D5> <D6> <D7> <D8> <CHK> 1381 * 1382 * The upper nibble of PXCT1 must be 0, 1383 * and the upper nibble of PXCT2 must be 1. The meanings of the 1384 * remaining bytes are as defined in the LocoNet Personal 1385 * Edition specification. 1386 * ********************************************* 1387 * SV programming format 2 1388 * 1389 * This is the recommended format for new designs. 1390 * The message bytes as assigned as follows: * 1391 * ; <0xE5> <0x10> <SRC> <SV_CMD> <SV_TYPE> <SVX1> 1392 * ; <DST_L> <DST_H> <SV_ADRL> <SV_ADRH> <SVX2> 1393 * ; <D1> <D2> <D3> <D4> <CHK> 1394 * 1395 * The upper nibble of both SVX1 (PXCT1) and SVX2 (PXCT2) must be 1. 1396 */ 1397 1398 int src = l.getElement(2); // source of transfer 1399 int dst_l = l.getElement(3); // ls 7 bits of destination 1400 int dst_h = l.getElement(4); // ms 7 bits of destination 1401 int pxct1 = l.getElement(5); 1402 int pxct2 = l.getElement(10); 1403 1404 int[] d = l.getPeerXfrData(); 1405 1406 if ((src == 0x7F) && (dst_l == 0x7F) && (dst_h == 0x7F) 1407 && ((pxct1 & 0x70) == 0x40)) { 1408 // Download (firmware?) messages. 1409 int sub = pxct2 & 0x70; 1410 switch (sub) { 1411 case 0x00: // setup 1412 return Bundle.getMessage("LN_MSG_IPL_SETUP", 1413 l.getElement(6), 1414 l.getElement(8), 1415 l.getElement(9), 1416 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1417 StringUtil.twoHexFromInt(l.getElement(7))), 1418 l.getElement(11)); 1419 case 0x10: // set address 1420 return Bundle.getMessage("LN_MSG_IPL_SET_ADDRESS", 1421 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1422 StringUtil.twoHexFromInt(d[0]) 1423 + StringUtil.twoHexFromInt(d[1]) 1424 + StringUtil.twoHexFromInt(d[2]))); 1425 case 0x20: // send data 1426 case 0x30: // verify 1427 return Bundle.getMessage((sub == 0x20) ? "LN_MSG_IPL_SEND_DATA" : "LN_MSG_IPL_VERIFY_REQUEST", 1428 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[0])), 1429 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[1])), 1430 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[2])), 1431 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[3])), 1432 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[4])), 1433 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[5])), 1434 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[6])), 1435 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[7]))); 1436 case 0x40: // end op 1437 return Bundle.getMessage("LN_MSG_IPL_END"); 1438 default: // everything else isn't understood, go to default 1439 break; 1440 } 1441 } 1442 1443 if ((src == 0x7F) && (dst_l == 0x0) && (dst_h == 0x0) 1444 && ((pxct1 & 0x3) == 0x00) && ((pxct2 & 0x70) == 0x70)) { 1445 // throttle semaphore symbol message 1446 return Bundle.getMessage("LN_MSG_THROTTLE_SEMAPHORE", 1447 ((d[0] * 128) + d[1]), 1448 Bundle.getMessage(((d[2] & 0x10) == 0x10) 1449 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" 1450 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), 1451 Bundle.getMessage(((d[2] & 0x08) == 0x08) 1452 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" 1453 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), 1454 Bundle.getMessage(((d[2] & 0x04) == 0x04) 1455 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" 1456 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), 1457 Bundle.getMessage(((d[2] & 0x02) == 0x02) 1458 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" 1459 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), 1460 Bundle.getMessage(((d[2] & 0x01) == 0x01) 1461 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_BLINKING" 1462 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNBLINKING") 1463 ); 1464 } 1465 1466 if ((src == 0x7F) && ((pxct1 & 0x70) == 0x00)) { 1467 1468 if ((dst_l == 0x00) && (dst_h == 0x00)) { 1469 char[] c = new char[]{0, 0, 0, 0, 0, 0, 0, 0}; 1470 c[0] = (char) d[0]; 1471 c[1] = (char) d[1]; 1472 c[2] = (char) d[2]; 1473 c[3] = (char) d[3]; 1474 c[4] = (char) d[4]; 1475 c[5] = (char) d[5]; 1476 c[6] = (char) d[6]; 1477 c[7] = (char) d[7]; 1478 return Bundle.getMessage("LN_MSG_THROTTLE_TEXT_MESSAGE_ALL_THROTTLES", 1479 c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); 1480 } else { 1481 return Bundle.getMessage("LN_MSG_THROTTLE_TEXT_MESSAGE_SPECIFIC_THROTTLE", 1482 (char) d[0], (char) d[1], 1483 (char) d[2], (char) d[3], 1484 (char) d[4], (char) d[5], 1485 (char) d[6], (char) d[7], 1486 convertToMixed(dst_l, dst_h)); 1487 } 1488 } 1489 1490 String result = interpretSV1Message(l); 1491 if (!result.isEmpty()) { 1492 return result; 1493 } 1494 1495 result = interpretSV0Message(l); 1496 if (!result.isEmpty()) { 1497 return result; 1498 } 1499 1500 // check for a specific type - SV Programming messages format 2 1501 result = interpretSV2Message(l); 1502 if (!result.isEmpty()) { 1503 return result; 1504 } 1505 1506 return ""; 1507 } 1508 1509 private static String interpretOpcPeerXfer15(LocoNetMessage l) { 1510 /* 1511 * see interpretOpcImm15 1512 * and jmri.jmrix.loconet.uhlenbrock.LncvMessageContents.java 1513 */ 1514 1515 // check for a specific type - Uhlenbrock LNCV Programming messages format 1516 String result = interpretLncvMessage(l); 1517 if (!result.isEmpty()) { 1518 return result; 1519 } 1520 1521 return ""; 1522 } 1523 1524 /** Try to decode an LNSV type 1 message as sent in replies from LocoIO boards 1525 * in response to a read or write request from LocoBuffer/PC. 1526 * 1527 * @param l The LocoNet message to decode 1528 * @return a localised string for display to the user in a monitor 1529 */ 1530 private static String interpretSV0Message(LocoNetMessage l) { 1531 String svReply = ""; 1532 Lnsv1MessageContents sv1mc = null; 1533 try { 1534 // assume the message is an SV1 message 1535 sv1mc = new Lnsv1MessageContents(l); 1536 } catch (IllegalArgumentException e) { 1537 // message is not an SV1 message. Ignore the exception. 1538 } 1539 if (sv1mc != null) { 1540 // the message was indeed an SV1 message 1541 try { 1542 // get string representation of the message from an 1543 // available translation which is best suited to 1544 // the currently-active "locale" 1545 svReply = sv1mc.toString(); 1546 } catch (IllegalArgumentException e) { 1547 // message is not a properly-formatted SV1 read reply message. Ignore the exception. 1548 } 1549 } 1550 return svReply; 1551 } 1552 1553 /** Try to decode an LNSV type 1 message as sent to LocoIO boards 1554 * containing a read or write request from LocoBuffer/PC. 1555 * 1556 * @param l The LocoNet message to decode 1557 * @return a localised string for display to the user in a monitor 1558 */ 1559 private static String interpretSV1Message(LocoNetMessage l) { 1560 // (New Designs) 1561 String svReply = ""; 1562 Lnsv1MessageContents sv1mc = null; 1563 try { 1564 // assume the message is an SV1 message 1565 sv1mc = new Lnsv1MessageContents(l); 1566 } catch (IllegalArgumentException e) { 1567 // message is not an SV1 message. Ignore the exception. 1568 } 1569 if (sv1mc != null) { 1570 // the message was indeed an SV1 message 1571 try { 1572 // get string representation of the message from an 1573 // available translation which is best suited to 1574 // the currently-active "locale" 1575 svReply = sv1mc.toString(); 1576 } catch (IllegalArgumentException e) { 1577 // message is not a properly-formatted SV1 message. Ignore the exception. 1578 } 1579 } 1580 return svReply; 1581 } 1582 1583 private static String interpretSV2Message(LocoNetMessage l) { 1584 // (New Designs) 1585 String svReply = ""; 1586 Lnsv2MessageContents sv2mc = null; 1587 try { 1588 // assume the message is an SV2 message 1589 sv2mc = new Lnsv2MessageContents(l); 1590 } catch (IllegalArgumentException e) { 1591 // message is not an SV2 message. Ignore the exception. 1592 } 1593 if (sv2mc != null) { 1594 // the message was indeed an SV2 message 1595 try { 1596 // get string representation of the message from an 1597 // available translation which is best suited to 1598 // the currently-active "locale" 1599 svReply = sv2mc.toString(); 1600 } catch (IllegalArgumentException e) { 1601 // message is not a properly-formatted SV2 message. Ignore the exception. 1602 } 1603 } 1604 return svReply; 1605 } 1606 1607 private static String interpretLncvMessage(LocoNetMessage l) { 1608 String lncvReply = ""; 1609 LncvMessageContents cvmc = null; 1610 try { 1611 // assume the message is an LNCV message 1612 cvmc = new LncvMessageContents(l); 1613 } catch (IllegalArgumentException e) { 1614 // message is not an LNCV message. Ignore the exception. 1615 } 1616 if (cvmc != null) { 1617 // the message was indeed an LNCV message 1618 try { 1619 // get string representation of the message from an 1620 // available translation which is best suited to 1621 // the currently-active "locale" 1622 lncvReply = cvmc.toString(); 1623 } catch (IllegalArgumentException e) { 1624 // message is not a properly-formatted LNCV message. Ignore the exception. 1625 } 1626 } 1627 return lncvReply; 1628 } 1629 1630 private static String interpretOpcPeerXfer10(LocoNetMessage l) { 1631 // throttle status 1632 int tcntrl = l.getElement(2); 1633 String stat; 1634 switch (tcntrl) { 1635 case 0x40: 1636 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_OK"); 1637 break; 1638 case 0x7F: 1639 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_NO_KEYPRESS"); 1640 break; 1641 case 0x43: 1642 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_PLUS_KEY"); 1643 break; 1644 case 0x42: 1645 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_MINUS_KEY"); 1646 break; 1647 case 0x41: 1648 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_RUNSTOP_KEY"); 1649 break; 1650 case 0x4e: 1651 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_RESP_SEM_DISP_CMD"); 1652 break; 1653 default: 1654 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_UNKONWN"); 1655 break; 1656 } 1657 1658 return Bundle.getMessage("LN_MSG_THROTTLE_STATUS", 1659 StringUtil.twoHexFromInt(tcntrl), 1660 stat, 1661 idString(l.getElement(3), l.getElement(4)), 1662 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1663 StringUtil.twoHexFromInt(l.getElement(7))), 1664 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1665 StringUtil.twoHexFromInt(l.getElement(8)))); 1666 } 1667 1668 private static String interpretOpcPeerXfer9(LocoNetMessage l, String reporterPrefix) { 1669 /* 1670 * Transponding "find" query and report messages. 1671 * Information reverse-engineered by B. Milhaupt and used with permission */ 1672 switch (l.getElement(2)) { 1673 case 0x40: { 1674 /* 1675 * ********************************************************************************** 1676 * Transponding "find" query message * The message bytes are 1677 * assigned as follows: 1678 * <p> 1679 * <0xE5> <0x09> <0x40> <AD_H> <AD_L> <0x00> 1680 * <0x00> <0x00> <CHK> * where: 1681 * <p> 1682 * <AD_H> is encoded as shown below: * When 1683 * <AD_H> = 0x7D, * Address is a 7 bit value defined solely by 1684 * <AD_L>. * When <AD_H> is not 0x7D, * Address is a 14 bit 1685 * value; AD_H{6:0} represent the upper 7 bits * of the 14 bit 1686 * address. 1687 * <p> 1688 * <AD_L> contains the least significant 7 bits of the 14 or 7 1689 * bit address. * * Information reverse-engineered by B. 1690 * Milhaupt and used with permission * 1691 * ********************************************************************************** 1692 */ 1693 String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); 1694 return Bundle.getMessage("LN_MSG_TRANSP_FIND_QUERY", 1695 locoAddr); 1696 } 1697 case 0x00: { 1698 /* 1699 * ********************************************************************************** 1700 * Transponding "find" report message * The message bytes are 1701 * assigned as follows: 1702 * <p> 1703 * <0xE5> <0x09> <0x00> <AD_H> <AD_L> <TR_ST> 1704 * <TR_ZS> <0x00> <CHK> * where: 1705 * <p> 1706 * <AD_H> is encoded as shown below: * When 1707 * <AD_H> = 0x7D, * Address is a 7 bit value defined solely by 1708 * <AD_L>. * When <AD_H> is not 0x7D, * Address is a 14 bit 1709 * value; AD_H{6:0} represent the upper 7 bits * of the 14 bit 1710 * address. 1711 * <p> 1712 * <AD_L> contains the least significant 7 bits of the 14 or 7 1713 * bit address. 1714 * <p> 1715 * <TR_ST> contains the transponding status for the addressed 1716 * equipment, * encoded as: * bits 7-6 always 00b * bit 5 1717 * encodes transponding presence * 0 = Addressed equipment is 1718 * absent * 1 = Addressed equipment is present * bits 4-0 encode 1719 * bits 7-3 of the Detection Section 1720 * <p> 1721 * <TR_ZS> contains the zone number and detection section, 1722 * encoded as: * bit 7 always 0 * bits 6-4 encode bits 2-0 of 1723 * the Detection Section * bits 3-1 encode the Transponding Zone 1724 * as shown below * 000b Zone A * 001b Zone B * 010b Zone C * 1725 * 011b Zone D * 100b Zone E * 101b Zone F * 110b Zone G * 111b 1726 * Zone H * bit 0 always 0 * * Information reverse-engineered by 1727 * B. Milhaupt and used with permission * 1728 * ********************************************************************************** 1729 */ 1730 1731 int section = ((l.getElement(5) & 0x1F) << 3) + ((l.getElement(6) & 0x70) >> 4) + 1; 1732 String zone; 1733 String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); 1734 1735 switch (l.getElement(6) & 0x0F) { 1736 case 0x00: 1737 zone = "A"; 1738 break; 1739 case 0x02: 1740 zone = "B"; 1741 break; 1742 case 0x04: 1743 zone = "C"; 1744 break; 1745 case 0x06: 1746 zone = "D"; 1747 break; 1748 case 0x08: 1749 zone = "E"; 1750 break; 1751 case 0x0A: 1752 zone = "F"; 1753 break; 1754 case 0x0C: 1755 zone = "G"; 1756 break; 1757 case 0x0E: 1758 zone = "H"; 1759 break; 1760 default: 1761 zone = Bundle.getMessage("LN_MSG_TRANSP_HELPER_UNKNOWN_ZONE", 1762 l.getElement(6) & 0x0F); 1763 break; 1764 } 1765 1766 // get system and user names 1767 String reporterSystemName = reporterPrefix 1768 + ((l.getElement(5) & 0x1F) * 128 + l.getElement(6) + 1); 1769 1770 Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName); 1771 1772 String uname = ""; 1773 if (reporter != null) { 1774 uname = reporter.getUserName(); 1775 } 1776 1777 if ((uname != null) && (!uname.isEmpty())) { 1778 return Bundle.getMessage("LN_MSG_TRANSP_REPORT_KNOWN_REPORTER_USERNAME", 1779 locoAddr, 1780 reporterSystemName, 1781 uname, 1782 section, 1783 zone); 1784 } 1785 return Bundle.getMessage("LN_MSG_TRANSP_REPORT_KNOWN_REPORTER_UNKNOWN_USERNAME", 1786 locoAddr, 1787 reporterSystemName, 1788 section, 1789 zone); 1790 } 1791 default: { 1792 break; 1793 } 1794 } 1795 return ""; 1796 } 1797 1798 private static String interpretOpcPeerXfer7(LocoNetMessage l) { 1799 // This might be Uhlenbrock IB-COM start/stop programming track 1800 if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42) { 1801 switch (l.getElement(5)) { 1802 case 0x40: { 1803 return Bundle.getMessage("LN_MSG_UHLENBROCK_STOP_PROGRAMMING_TRACK"); 1804 } 1805 case 0x41: { 1806 return Bundle.getMessage("LN_MSG_UHLENBROCK_START_PROGRAMMING_TRACK"); 1807 } 1808 default: 1809 break; 1810 } 1811 } 1812 return ""; 1813 } 1814 1815 private static String interpretOpcPeerXfer(LocoNetMessage l, String reporterPrefix) { 1816 String result; 1817 // The first byte seems to determine the type of message. 1818 switch (l.getElement(1)) { 1819 case 0x10: { //l.getZElement(1) 1820 result = interpretOpcPeerXfer16(l); 1821 if (!result.isEmpty()) { 1822 return result; 1823 } 1824 break; 1825 } 1826 case 0x0F: { 1827 result = interpretOpcPeerXfer15(l); 1828 if (!result.isEmpty()) { 1829 return result; 1830 } 1831 break; 1832 } 1833 case 0x0A: { 1834 result = interpretOpcPeerXfer10(l); 1835 if (!result.isEmpty()) { 1836 return result; 1837 } 1838 break; 1839 1840 } 1841 case 0x14: { 1842 result = interpretOpcPeerXfer20(l); 1843 if (!result.isEmpty()) { 1844 return result; 1845 } 1846 break; 1847 } 1848 case 0x09: { // l.getZElement(1) 1849 result = interpretOpcPeerXfer9(l, reporterPrefix); 1850 if (!result.isEmpty()) { 1851 return result; 1852 } 1853 break; 1854 } 1855 case 0x07: { 1856 result = interpretOpcPeerXfer7(l); 1857 if (!result.isEmpty()) { 1858 return result; 1859 } 1860 break; 1861 } 1862 default: { 1863 break; 1864 } 1865 } 1866 return ""; 1867 1868 } 1869 1870 private static String interpretLongAck(LocoNetMessage l) { 1871 int opcode = l.getElement(1); 1872 int ack1 = l.getElement(2); 1873 1874 switch (opcode | 0x80) { 1875 case (LnConstants.OPC_LOCO_ADR): 1876 // response for OPC_LOCO_ADR 1877 return Bundle.getMessage("LN_MSG_LONG_ACK_LOCO_ADR"); 1878 1879 case (LnConstants.OPC_LINK_SLOTS): 1880 // response for OPC_LINK_SLOTS 1881 return Bundle.getMessage("LN_MSG_LONG_ACK_LINK_SLOTS"); 1882 1883 case (LnConstants.OPC_SW_ACK): 1884 // response for OPC_SW_ACK 1885 switch (ack1) { 1886 case 0: 1887 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_FULL"); 1888 case 0x7f: 1889 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_ACCEPT"); 1890 default: 1891 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_UNKNOWN", 1892 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1893 StringUtil.twoHexFromInt(ack1)))+ 1894 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 1895 } 1896 case (LnConstants.OPC_SW_REQ): 1897 // response for OPC_SW_REQ 1898 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_REQ_FAIL"); 1899 1900 case (LnConstants.OPC_WR_SL_DATA): 1901 // response for OPC_WR_SL_DATA 1902 switch (ack1) { 1903 case 0: 1904 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_FAIL"); 1905 case 0x01: 1906 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_OK"); 1907 case 0x23: 1908 case 0x2b: 1909 case 0x6B: 1910 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_PROG_DCS51_OK"); 1911 case 0x40: 1912 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_BLIND"); 1913 case 0x7f: 1914 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_NOT_IMPL"); 1915 default: 1916 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_UNKNOWN", 1917 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1918 StringUtil.twoHexFromInt(ack1)))+ 1919 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 1920 1921 } 1922 1923 case (LnConstants.OPC_SW_STATE): 1924 // response for OPC_SW_STATE 1925 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_STATE", 1926 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1927 StringUtil.twoHexFromInt(ack1)), 1928 Bundle.getMessage((((ack1 & 0x20) != 0) 1929 ? "LN_MSG_SWITCH_STATE_CLOSED" 1930 : "LN_MSG_SWITCH_STATE_THROWN"))); 1931 1932 case (LnConstants.OPC_MOVE_SLOTS): 1933 // response for OPC_MOVE_SLOTS 1934 switch (ack1) { 1935 case 0: 1936 return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_REJECT"); 1937 case 0x7f: 1938 return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_ACCEPT"); 1939 default: 1940 return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_UNKNOWN", 1941 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1942 StringUtil.twoHexFromInt(ack1)))+ 1943 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 1944 1945 } 1946 1947 case LnConstants.OPC_IMM_PACKET: 1948 // response for OPC_IMM_PACKET 1949 if (ack1 == 0) { 1950 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_REJECT"); 1951 } else if (ack1 == 0x7f) { 1952 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_ACCEPT"); 1953 } else if (ack1 == 0x01) { // (l.getElement(1) == 0x6D) is same as case, same code for LNCV "unsupported CV" 1954 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_UHL_PROG"); 1955 } else if (ack1 == 0x02) { // LNCV Uhlenbrock programming reply 1956 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LNCV_READONLY"); 1957 } else if (ack1 == 0x03) { // LNCV Uhlenbrock programming reply 1958 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LNCV_ILLEGALVAL"); 1959 } else { 1960 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_UNKNOWN", 1961 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1962 StringUtil.twoHexFromInt(ack1)), 1963 128+ack1, 1964 StringUtil.twoHexFromInt(128+ack1) 1965 )+ 1966 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 1967 } 1968 1969 case LnConstants.OPC_IMM_PACKET_2: 1970 // response for OPC_IMM_PACKET_2 1971 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LIM_MASTER", 1972 ack1, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1973 StringUtil.twoHexFromInt(ack1))); 1974 1975 case (LnConstants.RE_LACK_SPEC_CASE1 | 0x80): // 0x50 plus opcode bit so can match the switch'd value: 1976 case (LnConstants.RE_LACK_SPEC_CASE2 | 0x80): //0x00 plus opcode bit so can match the switch'd value: 1977 // OpSwitch read response reverse-engineered by B. Milhaupt and 1978 // used with permission 1979 int responseValue = l.getElement(2); 1980 if (responseValue == 0x7f) { 1981 return Bundle.getMessage("LN_MSG_LONG_ACK_SPEC_CASE1_2_ACCEPTED"); 1982 } else { 1983 return Bundle.getMessage("LN_MSG_LONG_ACK_SPEC_CASE1_2_REPORT", 1984 (((responseValue & 0x20) == 0x20) ? 1 : 0), 1985 (((responseValue & 0x20) == 0x20) 1986 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED") 1987 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"))); 1988 } 1989 case LnConstants.OPC_EXP_SEND_FUNCTION_OR_SPEED_AND_DIR: 1990 return Bundle.getMessage("LN_MSG_LONG_ACK_WRONG_THROTTLE_ID", 1991 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1992 StringUtil.twoHexFromInt(l.getElement(2)))); 1993 1994 case LnConstants.OPC_ALM_READ: 1995 if (l.getElement(2) == 0) { 1996 return Bundle.getMessage("LN_MSG_LONG_ACK_SLOT_NOT_SUPPORTED", 1997 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1998 StringUtil.twoHexFromInt(opcode))); 1999 } 2000 //$FALL-THROUGH$ 2001 default: 2002 return Bundle.getMessage("LN_MSG_LONG_ACK_NOT_KNOWN", 2003 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 2004 StringUtil.twoHexFromInt(opcode | 0x80)), 2005 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 2006 StringUtil.twoHexFromInt(l.getElement(2)))); 2007 } 2008 } 2009 2010 private static String interpretPm4xPowerEvent(LocoNetMessage l) { 2011 int pCMD = (l.getElement(3) & 0xF0); 2012 2013 if ((pCMD == 0x30) || (pCMD == 0x10)) { 2014 // autoreverse 2015 int cm1 = l.getElement(3); 2016 int cm2 = l.getElement(4); 2017 String sect1Mode, sect1State; 2018 String sect2Mode, sect2State; 2019 String sect3Mode, sect3State; 2020 String sect4Mode, sect4State; 2021 2022 if ((cm1 & 1) != 0) { 2023 sect1Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); 2024 sect1State = ((cm2 & 1) != 0) 2025 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") 2026 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); 2027 } else { 2028 sect1Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); 2029 sect1State = ((cm2 & 1) != 0) 2030 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") 2031 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); 2032 } 2033 2034 if ((cm1 & 2) != 0) { 2035 sect2Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); 2036 sect2State = ((cm2 & 2) != 0) 2037 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") 2038 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); 2039 } else { 2040 sect2Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); 2041 sect2State = ((cm2 & 2) != 0) 2042 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") 2043 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); 2044 } 2045 2046 if ((cm1 & 4) != 0) { 2047 sect3Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); 2048 sect3State = ((cm2 & 4) != 0) 2049 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") 2050 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); 2051 } else { 2052 sect3Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); 2053 sect3State = ((cm2 & 4) != 0) 2054 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") 2055 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); 2056 } 2057 2058 if ((cm1 & 8) != 0) { 2059 sect4Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); 2060 sect4State = ((cm2 & 8) != 0) 2061 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") 2062 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); 2063 } else { 2064 sect4Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); 2065 sect4State = ((cm2 & 8) != 0) 2066 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") 2067 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); 2068 } 2069 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X", 2070 (l.getElement(2) + 1) + ((l.getElement(1) & 0x1) << 7), 2071 sect1Mode, sect1State, sect2Mode, sect2State, 2072 sect3Mode, sect3State, sect4Mode, sect4State); 2073 } 2074 if ((pCMD == 0x20) ) { //BXP88 2075 int cm1 = l.getElement(3); 2076 int cm2 = l.getElement(4); 2077 ArrayList<Integer> sectsShorted = new ArrayList<>(); 2078 ArrayList<Integer> sectsUnshorted = new ArrayList<>(); 2079 if ((cm2 & 0x01) != 0) { 2080 sectsShorted.add(1); 2081 } else { 2082 sectsUnshorted.add(1); 2083 } 2084 if ((cm2 & 0x02) != 0) { 2085 sectsShorted.add(2); 2086 } else { 2087 sectsUnshorted.add(2); 2088 } 2089 if ((cm2 & 0x04) != 0) { 2090 sectsShorted.add(3); 2091 } else { 2092 sectsUnshorted.add(3); 2093 } 2094 if ((cm2 & 0x08) != 0) { 2095 sectsShorted.add(4); 2096 } else { 2097 sectsUnshorted.add(4); 2098 } 2099 if ((cm1 & 0x01) != 0) { 2100 sectsShorted.add(5); 2101 } else { 2102 sectsUnshorted.add(5); 2103 } 2104 if ((cm1 & 0x02) != 0) { 2105 sectsShorted.add(6); 2106 } else { 2107 sectsUnshorted.add(6); 2108 } 2109 if ((cm1 & 0x04) != 0) { 2110 sectsShorted.add(7); 2111 } else { 2112 sectsUnshorted.add(7); 2113 } 2114 if ((cm1 & 0x08) != 0) { 2115 sectsShorted.add(8); 2116 } else { 2117 sectsUnshorted.add(8); 2118 } 2119 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXP88", 2120 (l.getElement(2) + 1) + ((l.getElement(1) & 0x1) << 7), 2121 StringUtils.join(sectsShorted, ','), StringUtils.join(sectsUnshorted, ',')); 2122 } 2123 if ( (pCMD == 0x50) || (pCMD == 0x40)) { //BXPA1 2124 int cm1 = l.getElement(3); 2125 String RevState = ""; 2126 String BreakState = ""; 2127 if ((cm1 & 0x10) != 0) { // reversing state 2128 if ((cm1 & 0x08) != 0) { 2129 RevState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_REV"); 2130 } else { 2131 RevState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_NORM"); 2132 } 2133 } else { 2134 // breaker state 2135 if ((cm1 & 0x08) != 0) { 2136 BreakState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_SHORT"); 2137 } else { 2138 BreakState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_NONSHORT"); 2139 } 2140 } 2141 int bxpa1_Id = ((l.getElement(2) << 3 ) + (l.getElement(3) & 0x07 ) + 1); 2142 // Due to a problem with the firmware messages from x and x+4 are identical 2143 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1", 2144 bxpa1_Id, bxpa1_Id +4, 2145 RevState, BreakState); 2146 } 2147 return ""; 2148 } 2149 2150 private static String interpretOpSws(LocoNetMessage l) { 2151 int pCMD = (l.getElement(3) & 0xF0); 2152 if (pCMD == 0x70) { 2153 // programming 2154 int deviceType = l.getElement(3) & 0x7; 2155 String device; 2156 switch (deviceType) { 2157 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_PM4X: 2158 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_PM4X"); 2159 break; 2160 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_BDL16X: 2161 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_BDL16X"); 2162 break; 2163 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_SE8: 2164 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_SE8C"); 2165 break; 2166 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_DS64: 2167 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_DS64"); 2168 break; 2169 default: 2170 return ""; 2171 } 2172 2173 int val = (l.getElement(4) & 0x01); 2174 int opsw = (l.getElement(4) & 0x7E) / 2 + 1; 2175 int bdaddr = l.getElement(2) + 1; 2176 if ((l.getElement(1) & 0x1) != 0) { 2177 bdaddr += 128; 2178 } 2179 2180 if ((deviceType == 0) && (bdaddr == 1) && (l.getElement(4) == 0)) { 2181 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_ACCESS_QUERY_ALL"); 2182 } 2183 2184 if ((l.getElement(1) & 0x10) != 0) { 2185 // write 2186 String valType = (val == 1) 2187 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED") 2188 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); 2189 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_WRITE_ACCESS", 2190 device, bdaddr, opsw, val, valType); 2191 } else { 2192 // query 2193 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_QUERY_ACCESS", 2194 device, bdaddr, opsw); 2195 } 2196 } 2197 return ""; 2198 } 2199 2200 private static String interpretDeviceType(LocoNetMessage l) { 2201 int pCMD = (l.getElement(3) & 0xF0); 2202 if (pCMD == 0x00) { 2203 /* 2204 * ************************************************** 2205 * Device type report * The message bytes as assigned as follows: 2206 * <p> 2207 * <0xD0> <DQT_REQ> <DQT_BRD> <DQT_B3> <DQT_B4> 2208 * <CHK> * * where: 2209 * <p> 2210 * <DQT_REQ> contains the device query request, * encoded as: * bits 2211 * 7-4 always 0110b * bits 3-1 always 001b * bit 0 (BoardID-1)<7> 2212 * <p> 2213 * <DQT_BRD> contains most the device board ID number, * encoded as: 2214 * * bit 7 always 0b * bits 6-0 (BoardID-1)<6:0> 2215 * <p> 2216 * <DQT_B3> contains the board type identification, * encoded as: * 2217 * bits 7-4 always 0000b * bits 3-0 contain the encoded device type, 2218 * * encoded as: * 0000b PM4x device * 0001b BDL16x device * 0010b 2219 * SE8C device * 0011b DS64 device * others Unknown device type 2220 * <p> 2221 * <DQT_B4> contains device version number: * bit 7 always 0b * bits 2222 * 6-0 VersionNumber(6:0) * * Information reverse-engineered by B. 2223 * Milhaupt and used with permission * 2224 * ************************************************** 2225 */ 2226 // This message is a report which is sent by a LocoNet device 2227 // in response to a query of attached devices 2228 // Note - this scheme is supported by only some Digitrax devices. 2229 // 2230 // A VersionNumber of 0 implies the hardware does not report 2231 // a valid version number. 2232 // 2233 // Device type report reverse-engineered by B. Milhaupt and 2234 // used with permission 2235 int deviceType = l.getElement(3) & 0x7; 2236 String device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_UNKNOWN"); 2237 switch (deviceType) { 2238 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_PM4X: 2239 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_PM4X"); 2240 break; 2241 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_BDL16X: 2242 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_BDL16X"); 2243 break; 2244 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_SE8: 2245 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_SE8C"); 2246 break; 2247 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_DS64: 2248 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_DS64"); 2249 break; 2250 default: 2251 log.warn("Unhandled device type: {}", deviceType); 2252 break; 2253 } 2254 2255 int bdaddr = l.getElement(2) + 1; 2256 if ((l.getElement(1) & 0x1) != 0) { 2257 bdaddr += 128; 2258 } 2259 String versionNumber = Integer.toString(l.getElement(4)); 2260 if (l.getElement(4) == 0) { 2261 versionNumber = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_VER_UNKNOWN"); 2262 } 2263 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_DEV_TYPE_RPT", 2264 device, bdaddr, versionNumber); 2265 } 2266 return ""; 2267 } 2268 2269 private static String interpretOpcMultiSense(LocoNetMessage l, String reporterPrefix) { 2270 int type = l.getElement(1) & LnConstants.OPC_MULTI_SENSE_MSG; 2271 String result; 2272 switch (type) { 2273 case LnConstants.OPC_MULTI_SENSE_POWER: 2274 // This is a PM42 power event. 2275 result = interpretPm4xPowerEvent(l); 2276 if (!result.isEmpty()) { 2277 return result; 2278 } 2279 result = interpretOpSws(l); 2280 if (!result.isEmpty()) { 2281 return result; 2282 } 2283 result = interpretDeviceType(l); 2284 if (!result.isEmpty()) { 2285 return result; 2286 } else { 2287 break; 2288 } 2289 2290 case LnConstants.OPC_MULTI_SENSE_PRESENT: 2291 case LnConstants.OPC_MULTI_SENSE_ABSENT: 2292 result = interpretOpcMultiSenseTranspPresence(l, reporterPrefix); 2293 if (!result.isEmpty()) { 2294 return result; 2295 } 2296 break; 2297 case LnConstants.OPC_MULTI_SENSE_RAILCOM_AD: 2298 result = interpretOpcMultiSenseRailcomAD(l, reporterPrefix); 2299 if (!result.isEmpty()) { 2300 return result; 2301 } 2302 break; 2303 default: 2304 break; 2305 } 2306 return ""; 2307 } 2308 2309 private static String interpretOpcMultiSenseTranspPresence(LocoNetMessage l, String reporterPrefix) { 2310 // Transponding Event 2311 // get system and user names 2312 String reporterSystemName; 2313 String reporterUserName; 2314 String zone; 2315 int bxp88Zone = 1 + (l.getElement(2) & 0x07); 2316 switch (l.getElement(2) & 0x0f) { // ignore bit 0 which seems to provide some unknown info from the BXP88 2317 case 0x00: 2318 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEA"); 2319 break; 2320 case 0x02: 2321 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEB"); 2322 break; 2323 case 0x04: 2324 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEC"); 2325 break; 2326 case 0x06: 2327 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONED"); 2328 break; 2329 case 0x08: 2330 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEE"); 2331 break; 2332 case 0x0A: 2333 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEF"); 2334 break; 2335 case 0x0C: 2336 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEG"); 2337 break; 2338 case 0x0E: 2339 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEH"); 2340 break; 2341 default: 2342 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONE_UNKNOWN", 2343 (l.getElement(2) & 0x0F)); 2344 break; 2345 } 2346 int type = l.getElement(1) & LnConstants.OPC_MULTI_SENSE_MSG; 2347 2348 reporterSystemName = reporterPrefix 2349 + ((l.getElement(1) & 0x1F) * 128 + l.getElement(2) + 1); 2350 2351 Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName); 2352 reporterUserName = ""; 2353 if (reporter != null) { 2354 String uname = reporter.getUserName(); 2355 if ((uname != null) && (!uname.isEmpty())) { 2356 reporterUserName = uname; 2357 } 2358 } 2359 int bxpa1Number = 1 + l.getElement(2) + (l.getElement(1) & 0x1F) * 128; 2360 int bxp88Number = 1 + (l.getElement(2)/8) + (l.getElement(1) & 0x1F) * 16; 2361 int section = 1 + (l.getElement(2) / 16) + (l.getElement(1) & 0x1F) * 8; 2362 2363 String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); 2364 String transpActivity = (type == LnConstants.OPC_MULTI_SENSE_PRESENT) 2365 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT") 2366 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_ABSENT"); 2367 2368 if ((l.getElement(2) & 0x1) == 0) { 2369 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_REPORT_WITH_BXP88", 2370 locoAddr, transpActivity, reporterSystemName, 2371 reporterUserName, section, zone, bxp88Number, bxp88Zone, bxpa1Number); 2372 } else { 2373 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_REPORT_NOT_BDL16X", 2374 locoAddr, transpActivity, reporterSystemName, 2375 reporterUserName, bxp88Number, bxp88Zone, bxpa1Number); 2376 } 2377 } 2378 2379 private static String convertRailComAD(int indexValue, int dynamicValue) { 2380 /* 2381 *************************************************** 2382 * RailCom App DYN (ID 7) message 2383 * indexValue = 6 bit value value per standard 2384 * dynamicValue = 8 bit value per standard 2385 **/ 2386 2387 String indexString; 2388 switch (indexValue) { 2389 case 0: //Speed 2390 indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_0"); 2391 break; 2392 case 7: //QoS 2393 indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_7"); 2394 break; 2395 default: 2396 indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_UNKNOWN"); 2397 break; 2398 } 2399 2400 return Bundle.getMessage("LN_MSG_RAILCOM_REPORT", 2401 indexValue, indexString, dynamicValue); 2402 } 2403 2404 private static String interpretOpcMultiSenseRailcomAD(LocoNetMessage l, String reporterPrefix) { 2405 /* 2406 *************************************************** 2407 * Multi Sense Standard RailCom App DYN message (Loconet OpCode 0xD0) 2408 * The message bytes are assigned as follows: 2409 * 2410 * <0xD0> <RC_I> <RCDV_L> <AD_H> <AD_L> <CHK> 2411 * 2412 * <RC_I> is encoded as shown below 2413 * bit 7 always 0 2414 * bits 6-5 always 10 (0x40) 2415 * bits 4-1 RailCom App:Dyn Index Value (4 bit value, but expected 6 bits per standard) 2416 * bit 0 RailCom Dynamic Value high bit 2417 * 2418 * <RCDV_L> RCDV_L{6:0} represent the upper 7 bits * of the 8 bit RailCom Dynamic Value. The 8th bit is bit 0 of <RC_I> 2419 * 2420 * <AD_H> is encoded as shown below: * When 2421 * <AD_H> = 0x7D, * Address is a 7 bit value defined solely by 2422 * <AD_L>. * When <AD_H> is not 0x7D, * Address is a 14 bit 2423 * value; AD_H{6:0} represent the upper 7 bits * of the 14 bit 2424 * address. 2425 * 2426 * Information reverse-engineered by Michael Richardson 2427 **/ 2428 2429 String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); 2430 int indexValue = (l.getElement(1) & 0x1E)/2; //bits 4-1 2431 int dynamicValue = l.getElement(2) + (l.getElement(1) & 0x01) * 128; 2432 2433 String railcomAdString = convertRailComAD(indexValue, dynamicValue); 2434 2435 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_RAILCOM_REPORT", 2436 locoAddr, railcomAdString); 2437 } 2438 2439 private static String interpretOpcMultiSenseLong(LocoNetMessage l, String reporterPrefix) { 2440 /* 2441 ************************************************** 2442 * Multi Sense Long RailCom App DYN message (Loconet OpCode 0xE0) 2443 * The message bytes as assigned as follows: 2444 * 2445 * <0xE0> <0x09> <MSL_I> <BLK_L> <AD_H> <AD_L> <RCDV_H> <RCDV_L> <CHK> 2446 * 2447 * <0xEO> OpCode 2448 * <Ox09> Message Length 2449 * <MSL_I> is encoded as shown below 2450 * bit 7 always 0 2451 * bits 6-5 (00 = Absent, 01 = Present, 10 = Present with AppDyn Message, 11 = unknown) 2452 * bit 4 ? - unverified - currently part of detection block number logic following Multi Sense Standard 2453 * bits 0-3 block high 2454 * 2455 * <BLK_L> 11 bit number representing the detection block. Lower 7 bits plus 4 high bits from <MSL_I> {3:0} 2456 * 2457 * <AD_H> is encoded as shown below: 2458 * When <AD_H> = 0x7D, Address is a 7 bit value defined solely by <AD_L>. 2459 * When <AD_H> is not 0x7D, Address is a 14 bit value; 2460 * AD_H{6:0} represent the upper 7 bits * of the 14 bit address. 2461 * 2462 * <RCDV_H> is encoded as shown below: 2463 * bit 7 always 0 2464 * bit 6 - Loco direction: 0 = East, 1 = West 2465 * bits 5-1 RailCom App:Dyn Index Value (5 bit value, but expected 6 bits per standard) 2466 * bit 0 RailCom Dynamic Value high bit 2467 * 2468 * <RCDV_L> {6:0} represent the lower 7 bits of the 8 bit RailCom Dynamic Value. The 8th bit is bit 0 of <RCDV_H> 2469 * 2470 * <CHK> 2471 * 2472 * Information reverse-engineered by Michael Ricahrdson 2473 */ 2474 2475 if (l.getElement(1) == 0x09){ // Only process 0xE0 0x09 messages 2476 // Transponding Event 2477 // get system and user names 2478 2479 int type = l.getElement(2) & LnConstants.OPC_MULTI_SENSE_MSG; //bits 5-4 2480 //0x00 = absent = 00 2481 //0x20 = present = 10 2482 //0x40 = present with App Dyn = 01 2483 //0x60 = unknown = 11 2484 2485 if (type == 0x60) { //unknown at this point 2486 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2487 } 2488 2489 // Now Process 0x00, 0x20, and 0x40 2490 String reporterSystemName = reporterPrefix + ((l.getElement(2) & 0x1F) * 128 + l.getElement(3) + 1); 2491 2492 Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName); 2493 String reporterUserName = ""; 2494 if (reporter != null) { 2495 String uname = reporter.getUserName(); 2496 if ((uname != null) && (!uname.isEmpty())) { 2497 reporterUserName = uname; 2498 } 2499 } 2500 2501 String locoAddr = convertToMixed(l.getElement(5), l.getElement(4)); 2502 2503 String transpActivity; 2504 2505 String direction = ((l.getElement(6) & 0x40) == 0) 2506 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_LOCO_DIRECTION_HELPER_EAST") 2507 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_LOCO_DIRECTION_HELPER_WEST"); 2508 2509 switch (type) { 2510 case LnConstants.OPC_MULTI_SENSE_RAILCOM_AD: 2511 int indexValue = (l.getElement(6) & 0x3E)/2; //bits 5-1 2512 int dynamicValue = l.getElement(7) + (l.getElement(6) & 0x01) * 128; 2513 transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT"); 2514 2515 String railcomAdString = convertRailComAD(indexValue, dynamicValue); 2516 String multiSenseLongString = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT", 2517 locoAddr, direction, transpActivity, reporterSystemName, reporterUserName); 2518 2519 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_RAILCOM_REPORT", 2520 multiSenseLongString, railcomAdString); 2521 2522 case LnConstants.OPC_MULTI_SENSE_PRESENT: 2523 if ((l.getElement(6) & 0x3F) != 0 || l.getElement(7) != 0 ) { 2524 // within current understanding values here are not expected 2525 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2526 } 2527 2528 transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT"); 2529 2530 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT", 2531 locoAddr, direction, transpActivity, reporterSystemName, reporterUserName); 2532 2533 case LnConstants.OPC_MULTI_SENSE_ABSENT: 2534 if ((l.getElement(6) & 0x3F) != 0 || l.getElement(7) != 0 ) { 2535 // within current understanding values here are not expected 2536 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2537 } 2538 2539 transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_ABSENT"); 2540 2541 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT", 2542 locoAddr, direction, transpActivity, reporterSystemName, reporterUserName); 2543 2544 default: 2545 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2546 } 2547 2548 } else { 2549 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2550 } 2551 } 2552 2553 private static String interpretOpcWrSlDataOpcSlRdData(LocoNetMessage l) { 2554 int slot = l.getElement(2); // slot number for this request 2555 String mode; 2556 int command = l.getOpCode(); 2557 int id1 = l.getElement(11); // ls 7 bits of ID code 2558 int id2 = l.getElement(12); // ms 7 bits of ID code 2559 /* 2560 * These messages share a common data format with the only difference being 2561 * whether we are reading or writing the slot data. 2562 */ 2563 if (command == LnConstants.OPC_WR_SL_DATA) { 2564 mode = Bundle.getMessage("LN_MSG_SLOT_HELPER_ACCESS_TYPE_REQUEST"); 2565 } else { 2566 mode = Bundle.getMessage("LN_MSG_SLOT_HELPER_ACCESS_TYPE_RESPONSE"); 2567 } 2568 2569 String result; 2570 switch (slot) { 2571 case LnConstants.FC_SLOT: 2572 result = interpretFastClockSlot(l, mode, id1, id2); 2573 if (!result.isEmpty()) { 2574 return result; 2575 } 2576 break; 2577 case LnConstants.PRG_SLOT: 2578 result = interpretProgSlot(l, mode, id1, id2, command); 2579 if (!result.isEmpty()) { 2580 return result; 2581 } 2582 break; 2583 2584 case 0x79: 2585 case 0x7a: 2586 case 0x7D: 2587 return ""; 2588 case LnConstants.CFG_EXT_SLOT: 2589 result = interpretCmdStnExtCfgSlotRdWr(l, command); 2590 if (!result.isEmpty()) { 2591 return result; 2592 } 2593 break; 2594 2595 // end programming track block 2596 case LnConstants.CFG_SLOT: 2597 result = interpretCmdStnCfgSlotRdWr(l, command); 2598 if (!result.isEmpty()) { 2599 return result; 2600 } 2601 break; 2602 2603 default: 2604 result = interpretStandardSlotRdWr(l, id1, id2, command, slot); 2605 if (!result.isEmpty()) { 2606 return result; 2607 } 2608 break; 2609 } 2610 2611 return ""; 2612 } 2613 2614 private static String interpretOpcInputRep(LocoNetMessage l, String sensorPrefix) { 2615 int in1 = l.getElement(1); 2616 int in2 = l.getElement(2); 2617 int contactNum = ((SENSOR_ADR(in1, in2) - 1) * 2 + ((in2 & LnConstants.OPC_INPUT_REP_SW) != 0 ? 2 : 1)); 2618 // get system and user names 2619 String sensorSystemName = sensorPrefix + contactNum; 2620 String sensorUserName; 2621 Sensor sensor = InstanceManager.getDefault(SensorManager.class).getSensor(sensorSystemName); 2622 sensorUserName = ""; 2623 if (sensor != null) { 2624 String uname = sensor.getUserName(); 2625 if ((uname != null) && (!uname.isEmpty())) { 2626 sensorUserName = " ("+uname+")"; 2627 } 2628 } 2629 2630 int sensorid = (SENSOR_ADR(in1, in2) - 1) * 2 2631 + ((in2 & LnConstants.OPC_INPUT_REP_SW) != 0 ? 2 : 1); 2632 2633 int bdlid = ((sensorid - 1) / 16) + 1; 2634 int bdlin = ((sensorid - 1) % 16) + 1; 2635 String bdl = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_BDL_INFO", 2636 bdlid, bdlin); 2637 2638 int boardid = ((sensorid - 1) / 8) + 1; 2639 int boardindex = ((sensorid - 1) % 8); 2640 String otherBoardsNames; 2641 String otherBoardsInputs; 2642 if (sensorid < 289) { 2643 otherBoardsNames = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_ALL_EQUIV_BOARDS", boardid); 2644 otherBoardsInputs = Bundle.getMessage("LN_MSG_OPC_INPUT_REPORT_INPUT_NAMES_ALL_EQUIV_BOARDS", 2645 ds54sensors[boardindex], ds64sensors[boardindex], 2646 se8csensors[boardindex]); 2647 } else { 2648 otherBoardsNames = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_NO_SE8C", boardid); 2649 otherBoardsInputs = Bundle.getMessage("LN_MSG_OPC_INPUT_REPORT_INPUT_NAMES_NO_SE8C", 2650 ds54sensors[boardindex], ds64sensors[boardindex]); 2651 } 2652 2653 // There is no way to tell what kind of a board sent the message. 2654 // To be user-friendly, we just print all the known combos. 2655 return Bundle.getMessage("LN_MSG_OPC_INPUT_REP", 2656 sensorSystemName, sensorUserName, 2657 Bundle.getMessage((in2 & LnConstants.OPC_INPUT_REP_HI) != 0 2658 ? "LN_MSG_SENSOR_STATE_HIGH" : "LN_MSG_SENSOR_STATE_LOW"), 2659 bdl, 2660 otherBoardsNames, otherBoardsInputs); 2661 } 2662 2663 private static String interpretOpcSwRep(LocoNetMessage l, String turnoutPrefix) { 2664 int sn1 = l.getElement(1); 2665 int sn2 = l.getElement(2); 2666 // get system and user names 2667 String turnoutUserName = ""; 2668 2669 String turnoutSystemName = turnoutPrefix 2670 + SENSOR_ADR(sn1, sn2); 2671 Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); 2672 2673 String uname; 2674 if (turnout != null) { 2675 uname = turnout.getUserName(); 2676 if ((uname != null) && (!uname.isEmpty())) { 2677 turnoutUserName = uname; 2678 } else { 2679 turnoutUserName = ""; 2680 } 2681 } 2682 2683 if ((sn2 & LnConstants.OPC_SW_REP_INPUTS) != 0) { 2684 return Bundle.getMessage("LN_MSG_OPC_SW_REP_INPUTS_STATE", 2685 turnoutSystemName, turnoutUserName, 2686 Bundle.getMessage(((sn2 & LnConstants.OPC_SW_REP_SW) != 0 2687 ? "LN_MSG_SENSOR_SW_INPUT_TYPE_HI" 2688 : "LN_MSG_SENSOR_SW_INPUT_TYPE_LO")), 2689 Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_HI) != 0) 2690 ? "LN_MSG_SENSOR_SW_INPUT_STATE_HI" 2691 : "LN_MSG_SENSOR_SW_INPUT_STATE_LO"))); 2692 } 2693 return Bundle.getMessage("LN_MSG_OPC_SW_REP_OUTPUT_STATE", 2694 turnoutSystemName, turnoutUserName, 2695 Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_CLOSED) != 0) 2696 ? "LN_MSG_SENSOR_SW_OUTPUT_STATE_ON" 2697 : "LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF")), 2698 Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_THROWN) != 0) 2699 ? "LN_MSG_SENSOR_SW_OUTPUT_STATE_ON" 2700 : "LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF"))); 2701 } 2702 2703 private static String interpretOpcSwAck(LocoNetMessage l, String turnoutPrefix) { 2704 int sw2 = l.getElement(2); 2705 if ((sw2 & 0x40) == 0x40) { 2706 return ""; 2707 } 2708 // get system and user names 2709 String turnoutUserName = ""; 2710 2711 String turnoutSystemName = turnoutPrefix 2712 + SENSOR_ADR(l.getElement(1), l.getElement(2)); 2713 Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); 2714 2715 String uname; 2716 if (turnout != null) { 2717 uname = turnout.getUserName(); 2718 if ((uname != null) && (!uname.isEmpty())) { 2719 turnoutUserName = uname; 2720 } else { 2721 turnoutUserName = ""; 2722 } 2723 } 2724 2725 String pointsDirection = ((sw2 & LnConstants.OPC_SW_ACK_CLOSED) != 0 2726 ? Bundle.getMessage("LN_MSG_SW_POS_CLOSED") 2727 : Bundle.getMessage("LN_MSG_SW_POS_THROWN")); 2728 String outputState = (((sw2 & LnConstants.OPC_SW_ACK_OUTPUT) != 0) 2729 ? Bundle.getMessage("LN_MSG_SENSOR_SW_OUTPUT_STATE_ON") 2730 : Bundle.getMessage("LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF")); 2731 return Bundle.getMessage("LN_MSG_REQ_SWITCH", turnoutSystemName, 2732 turnoutUserName, pointsDirection, outputState); 2733 } 2734 2735 private static String interpretOpcSwState(LocoNetMessage l, String turnoutPrefix) { 2736 // get system and user names 2737 if ((l.getElement(2) & 0x40) != 0x00) { 2738 return ""; 2739 } 2740 String turnoutUserName = ""; 2741 String turnoutSystemName = turnoutPrefix 2742 + SENSOR_ADR(l.getElement(1), l.getElement(2)); 2743 Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); 2744 2745 String uname; 2746 if (turnout != null) { 2747 uname = turnout.getUserName(); 2748 if ((uname != null) && (!uname.isEmpty())) { 2749 turnoutUserName = uname; 2750 } else { 2751 turnoutUserName = ""; 2752 } 2753 } 2754 2755 2756 return Bundle.getMessage("LN_MSG_SW_STATE", turnoutSystemName, 2757 turnoutUserName); 2758 } 2759 2760 private static String interpretOpcRqSlData(LocoNetMessage l) { 2761 int slot = l.getElement(1) + 128 * (l.getElement(2) & 0x07); 2762 boolean expSlotRequ = (l.getElement(2) & 0x40) == 0X40; 2763 switch (slot) { 2764 // Slots > 120 & < 128 are all special, but these are the only ones we know to decode. 2765 // Extended System Slots 248 thru 251 dealt with separately, not here 2766 case LnConstants.FC_SLOT: 2767 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_FC_SLOT"); 2768 case LnConstants.CFG_SLOT: 2769 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_CFG_SLOT"); 2770 case LnConstants.CFG_EXT_SLOT: 2771 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_EXT_CFG_SLOT"); 2772 case LnConstants.PRG_SLOT: 2773 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_PRG_SLOT"); 2774 case 0x79: 2775 case 0x7a: 2776 case 0x7d: 2777 break; 2778 default: 2779 if (expSlotRequ) { 2780 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_LOCO_EXP_SLOT", slot); 2781 } else { 2782 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_LOCO_SLOT", slot); 2783 } 2784 } 2785 return ""; 2786 } 2787 2788 private static String interpretOpcMoveSlots(LocoNetMessage l) { 2789 int src = l.getElement(1); 2790 int dest = l.getElement(2); 2791 if ((src >= 0x79) && (src <= 0x7f)) { 2792 return ""; 2793 } 2794 if ((dest >= 0x79) && (dest <= 0x7f)) { 2795 return ""; 2796 } 2797 2798 /* check special cases */ 2799 if (src == 0) { 2800 /* DISPATCH GET */ 2801 2802 return Bundle.getMessage("LN_MSG_MOVE_SL_GET_DISP"); 2803 } else if (src == dest) { 2804 /* IN USE */ 2805 2806 return Bundle.getMessage("LN_MSG_MOVE_SL_NULL_MOVE", src); 2807 } else if (dest == 0) { 2808 /* DISPATCH PUT */ 2809 2810 return Bundle.getMessage("LN_MSG_MOVE_SL_DISPATCH_PUT", src); 2811 } else { 2812 /* general move */ 2813 2814 return Bundle.getMessage("LN_MSG_MOVE_SL_MOVE", src, dest); 2815 } 2816 } 2817 2818 private static String interpretOpcConsistFunc(LocoNetMessage l) { 2819 int slot = l.getElement(1); 2820 int dirf = l.getElement(2); 2821 if ((dirf & 0x40) == 0x40) { 2822 return ""; 2823 } 2824 return Bundle.getMessage("LN_MSG_CONSIST_FUNC", 2825 slot, 2826 interpretDIRF(dirf)); 2827 } 2828 2829 private static String interpretOpcLocoSnd(LocoNetMessage l) { 2830 int slot = l.getElement(1); 2831 int snd = l.getElement(2); 2832 return Bundle.getMessage("LN_MSG_OPC_LOCO_SND", 2833 slot, 2834 Bundle.getMessage((snd & LnConstants.SND_F5) != 0 2835 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), 2836 Bundle.getMessage((snd & LnConstants.SND_F6) != 0 2837 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), 2838 Bundle.getMessage((snd & LnConstants.SND_F7) != 0 2839 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), 2840 Bundle.getMessage((snd & LnConstants.SND_F8) != 0 2841 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")); 2842 2843 } 2844 2845 protected static String interpretDIRF(int dirf) { 2846 if ((dirf & 0x40) == 0x40) { 2847 return ""; 2848 } 2849 String[] dirf0_4 = interpretF0_F4toStrings(dirf); 2850 return Bundle.getMessage("LN_MSG_HELPER_DIRF", 2851 Bundle.getMessage((dirf & LnConstants.DIRF_DIR) != 0 2852 ? "LN_MSG_DIRECTION_REV" : "LN_MSG_DIRECTION_FWD"), 2853 dirf0_4[0], dirf0_4[1], dirf0_4[2], dirf0_4[3], dirf0_4[4]); 2854 2855 } 2856 2857 private static String interpretOpcLocoDirf(LocoNetMessage l) { 2858 int slot = l.getElement(1); 2859 int dirf = l.getElement(2); 2860 2861 String dirFinfo = interpretDIRF(dirf); 2862 if (dirFinfo.isEmpty()) { 2863 return ""; 2864 } 2865 2866 return Bundle.getMessage("LN_MSG_OPC_LOCO_DIRF", 2867 slot, dirFinfo); 2868 } 2869 2870 private static String interpretOpcLocoSpd(LocoNetMessage l) { 2871 int slot = l.getElement(1); 2872 int spd = l.getElement(2); 2873 2874 if (spd == LnConstants.OPC_LOCO_SPD_ESTOP) { 2875 return Bundle.getMessage("LN_MSG_OPC_LOCO_SPD_ESTOP", slot); 2876 } else { 2877 return Bundle.getMessage("LN_MSG_OPC_LOCO_SPD_NORMAL", slot, spd); 2878 } 2879 2880 } 2881 2882 private static String interpretOpcPanelQuery(LocoNetMessage l) { 2883 switch (l.getElement(1)) { 2884 case 0x00: { 2885 return Bundle.getMessage("LN_MSG_OPC_DF_TETHERLESS_QUERY"); 2886 } 2887 case 0x40: { 2888 if (l.getElement(2) == 0x1F) { 2889 // Some UR devices treat this operation as a set plus query, others 2890 // treat this only as a set. 2891 return Bundle.getMessage("LN_MSG_OPC_DF_SET_LOCONETID", l.getElement(3)); 2892 } 2893 break; 2894 } 2895 default: { 2896 break; 2897 } 2898 } 2899 return ""; 2900 } 2901 2902 private static String interpretOpcSwReq(LocoNetMessage l, String turnoutPrefix) { 2903 int sw1 = l.getElement(1); 2904 int sw2 = l.getElement(2); 2905 if ((sw2 & 0x40) == 0x40) { 2906 return ""; 2907 } 2908 2909 if ((!(((sw2 & 0xCF) == 0x0F) && ((sw1 & 0xFC) == 0x78))) 2910 && (!(((sw2 & 0xCF) == 0x07) && ((sw1 & 0xFC) == 0x78)))) { 2911 // ordinary form, LPU V1.0 page 9 2912 // handle cases which are not "stationary decoder interrogate" messages 2913 // get system and user names 2914 String turnoutUserName = ""; 2915 2916 String turnoutSystemName = turnoutPrefix 2917 + SENSOR_ADR(l.getElement(1), l.getElement(2)); 2918 Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); 2919 2920 String uname; 2921 if (turnout != null) { 2922 uname = turnout.getUserName(); 2923 if ((uname != null) && (!uname.isEmpty())) { 2924 turnoutUserName = uname; 2925 } else { 2926 turnoutUserName = ""; 2927 } 2928 } 2929 2930 String pointsDirection = ((sw2 & LnConstants.OPC_SW_ACK_CLOSED) != 0 2931 ? Bundle.getMessage("LN_MSG_SW_POS_CLOSED") 2932 : Bundle.getMessage("LN_MSG_SW_POS_THROWN")); 2933 String outputState = ((sw2 & LnConstants.OPC_SW_ACK_OUTPUT) != 0 2934 ? Bundle.getMessage("LN_MSG_SW_OUTPUT_STATE_ON") 2935 : Bundle.getMessage("LN_MSG_SW_OUTPUT_STATE_OFF")); 2936 if (turnoutUserName.isEmpty()) { 2937 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_NORMAL_WITHOUT_USERNAME", 2938 turnoutSystemName, 2939 pointsDirection, outputState); 2940 } else { 2941 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_NORMAL_WITH_USERNAME", 2942 turnoutSystemName, turnoutUserName, 2943 pointsDirection, outputState); 2944 } 2945 } 2946 2947 /* 2948 Handle cases which are "stationary decoder interrogate" messages. 2949 */ 2950 2951 /* 2952 * Decodes a/c/b bits to allow proper creation of a list of addresses 2953 * which ought to reply to the "stationary decoder interrogate" message. 2954 */ 2955 int a = (sw2 & 0x20) >> 5; 2956 int c = (sw1 & 0x02) >> 1; 2957 int b = (sw1 & 0x01); 2958 2959 /* 2960 * All this blob does is loop through the ranges indicated by the 2961 * a/c/b bits, they are mask bits in the midde of the range. The 2962 * idea is to get 8 sensors at a time, since that is generally what 2963 * units have, and to query units 1, 9, 17... then 2, 10, 18... and 2964 * so on such that if they are all in a row they don't get hit at 2965 * the same time. 2966 */ 2967 int topbits; 2968 int midbits = (a << 2) + (c << 1) + b; 2969 int count = 0; 2970 StringBuilder addrListB = new StringBuilder(); 2971 for (topbits = 0; topbits < 32; topbits++) { 2972 // The extra "+1" adjusts for the fact that we show 1-2048, 2973 // rather than 0-2047 on the wire. 2974 int lval = (topbits << 6) + (midbits << 3) + 1; 2975 int hval = lval + 7; 2976 2977 if ((count % 8) != 0) { 2978 addrListB.append(", "); // NOI18N 2979 } else { 2980 if (count == 0) { 2981 addrListB.append("\t"); // NOI18N 2982 } else { 2983 addrListB.append(",\n\t"); // NOI18N 2984 } 2985 } 2986 addrListB.append(lval); // NOI18N 2987 addrListB.append("-").append(hval); // NOI18N 2988 count++; 2989 } 2990 2991 String addrList = addrListB.toString(); 2992 2993 if (((sw2 & 0xCF) == 0x0F) && ((sw1 & 0xFC) == 0x78)) { 2994 // broadcast address LPU V1.0 page 12 2995 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_INTERROGATE_TURNOUTS", 2996 a, c, b, addrList); 2997 } else { 2998 // broadcast address LPU V1.0 page 13 2999 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_INTERROGATE_SENSORS_TURNOUTS", 3000 a, c, b, addrList); 3001 } 3002 } 3003 3004 private static String interpretFastClockSlot(LocoNetMessage l, String mode, int id1, int id2) { 3005 /* 3006 * FAST Clock: The system FAST clock and parameters are implemented in 3007 * Slot#123 <7B>. Use <EF> to write new clock information, Slot read of 3008 * 0x7B,<BB><7B>.., will return current System clock information, and 3009 * other throttles will update to this SYNC. Note that all attached 3010 * display devices keep a current clock calculation based on this SYNC 3011 * read value, i.e. devices MUST not continuously poll the clock SLOT to 3012 * generate time, but use this merely to restore SYNC and follow current 3013 * RATE etc. This clock slot is typically "pinged" * or read SYNC'd 3014 * every 70 to 100 seconds, by a single user, so all attached devices 3015 * can synchronise any phase drifts. Upon seeing a SYNC read, all 3016 * devices should reset their local sub-minute phase counter and 3017 * invalidate the SYNC update ping generator. 3018 * <p> 3019 * Clock Slot Format: 3020 * <p> 3021 * <0xEF>,<0E>,<7B>,<CLK_RATE>,<FRAC_MINSL>,<FRAC_MINSH>,<256-MINS_60>, 3022 * <TRK><256-HRS_24>,<DAYS>,<CLK_CNTRL>,<ID1>,<1D2>,<CHK> 3023 * <p> 3024 * where: 3025 * <p> 3026 * <CLK_RATE> 0=Freeze clock, * 1=normal 1:1 rate, 10=10:1 etc., max 3027 * VALUE is 7F/128 to 1 3028 * <p> 3029 * <FRAC_MINSL> FRAC mins hi/lo are a sub-minute counter, depending on 3030 * the CLOCK generator 3031 * <p> 3032 * <FRAC_MINSH> Not for ext. usage. This counter is reset when valid 3033 * <E6><7B> 3034 * SYNC message is seen 3035 * <p> 3036 * <256-MINS_60> This is FAST clock MINUTES subtracted from 256. Modulo 3037 * 0-59 3038 * <p> 3039 * <256-HRS_24> This is FAST clock HOURS subtracted from 256. Modulo 3040 * 0-23 3041 * <p> 3042 * <DAYS> number of 24 Hr clock rolls, positive count 3043 * <p> 3044 * <CLK_CNTRL> Clock Control Byte D6- 1=This is valid Clock information, 3045 * 0=ignore this <E6><7B>, SYNC reply 3046 * <p> 3047 * <ID1>,<1D2> This is device ID last setting the clock. 3048 * <p> 3049 * <00><00> shows no set has happened 3050 * <p> 3051 * <7F><7x> are reserved for PC access * 3052 */ 3053 3054 int minutes; // temporary time values 3055 int hours; 3056 int clk_rate = l.getElement(3); // 0 = Freeze clock, 1 = normal, 3057 // 10 = 10:1 etc. Max is 0x7f 3058 int mins_60 = l.getElement(6); // 256 - minutes 3059 int track_stat = l.getElement(7); // track status 3060 int hours_24 = l.getElement(8); // 256 - hours 3061 int days = l.getElement(9); // clock rollovers 3062 int clk_cntrl = l.getElement(10); // bit 6 = 1; data is valid 3063 // clock info 3064 // " " 0; ignore this reply 3065 // id1/id2 is device id of last device to set the clock 3066 // " " = zero shows not set has happened 3067 3068 /* recover hours and minutes values */ 3069 minutes = ((255 - mins_60) & 0x7f) % 60; 3070 hours = ((256 - hours_24) & 0x7f) % 24; 3071 hours = (24 - hours) % 24; 3072 minutes = (60 - minutes) % 60; 3073 3074 return Bundle.getMessage("LN_MSG_SLOT_ACCESS_FAST_CLOCK", 3075 mode, 3076 ((clk_cntrl & 0x20) != 0 ? "" : Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_SYNC")), 3077 (clk_rate != 0 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_RUNNING") 3078 : Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_FROZEN")), 3079 clk_rate, 3080 days, 3081 fcTimeToString(hours, minutes), 3082 idString(id1, id2), 3083 trackStatusByteToString(track_stat)); 3084 } 3085 3086 private static String interpretProgSlot(LocoNetMessage l, String mode, int id1, int id2, int command) { 3087 /* 3088 * ******************************************************************************************** 3089 * Programmer track: 3090 * ================= 3091 * The programmer track is 3092 * accessed as Special slot #124 ( $7C, 0x7C). It is a full 3093 * asynchronous shared system resource. 3094 * 3095 * To start Programmer task, 3096 * write to slot 124. There will be an immediate LACK acknowledge 3097 * that indicates what programming will be allowed. If a valid programming 3098 * task is started, 3099 * then at the final (asynchronous) programming 3100 * completion, a Slot read <E7> from slot 124 will be sent. This is 3101 * the final task status reply. 3102 * 3103 * Programmer Task Start: 3104 * 3105 * ---------------------- 3106 * <p> 3107 * <0xEF>,<0E>,<7C>,<PCMD>,<0>,<HOPSA>,<LOPSA>,<TRK>;<CVH>,<CVL>, 3108 * <p> 3109 * <DATA7>,<0>,<0>,<CHK> * * This OPC leads to immediate LACK codes: 3110 * <p> 3111 * <B4>,<7F>,<7F>,<chk> Function NOT implemented, no reply. 3112 * <p> 3113 * <B4>,<7F>,<0>,<chk> Programmer BUSY , task aborted, no reply. 3114 * <p> 3115 * <B4>,<7F>,<1>,<chk> Task accepted , <E7> reply at completion. 3116 * <p> 3117 * <B4>,<7F>,<0x40>,<chk> Task accepted blind NO <E7> 3118 * reply at completion. * * Note that the <7F> code will occur in 3119 * Operations Mode Read requests if the System is not * configured for 3120 * and has no Advanced Acknowledgement detection installed.. Operations 3121 * Mode * requests can be made and executed whilst a current Service 3122 * Mode programming task is keeping * the Programming track BUSY. If a 3123 * Programming request is rejected, delay and resend the * complete 3124 * request later. Some readback operations can keep the Programming 3125 * track busy for up * to a minute. Multiple devices, throttles/PC's 3126 * etc, can share and sequentially use the * Programming track as long 3127 * as they correctly interpret the response messages. Any Slot RD * from 3128 * the master will also contain the Programmer Busy status in bit 3 of 3129 * the <TRK> byte. * * A <PCMD> value of 3130 * <00> will abort current SERVICE mode programming task and will echo 3131 * with an <E6> RD the command string that was aborted. 3132 * 3133 * <PCMD> 3134 * Programmer Command: 3135 * -------------------------- 3136 * Defined as 3137 * D7 -0 3138 * D6 -Write/Read 1= Write, 0=Read 3139 * D5 -Byte Mode 1= Byte operation, 0=Bit operation (if possible) 3140 * D4 -TY1 Programming Type select bit 3141 * D3 -TY0 Prog type select bit 3142 * D2 -Ops Mode 1=Ops Mode on Mainlines, 0=Service Mode on Programming Track 3143 * D1 -0 reserved 3144 * D0 -0-reserved 3145 * 3146 * Type codes: 3147 * ----------- * Byte Mode Ops Mode 3148 * TY1 TY0 Meaning * 1 0 0 0 Paged mode byte Read/Write on Service Track 3149 * * 1 0 0 0 Paged mode byte Read/Write on Service Track * 1 0 0 1 3150 * Direct mode byteRead/Write on Service Track * 0 0 0 1 Direct mode bit 3151 * Read/Write on Service Track * x 0 1 0 Physical Register byte 3152 * Read/Write on Service Track * x 0 1 1 Service Track- reserved 3153 * function * 1 1 0 0 Ops mode Byte program, no feedback * 1 1 0 1 Ops 3154 * mode Byte program, feedback * 0 1 0 0 Ops mode Bit program, no 3155 * feedback * 0 1 0 1 Ops mode Bit program, feedback * * 3156 * <HOPSA>Operations Mode Programming * 7 High address bits of Loco to 3157 * program, 0 if Service Mode 3158 * <p> 3159 * <LOPSA>Operations Mode Programming * 7 Low address bits of Loco to 3160 * program, 0 if Service Mode 3161 * <p> 3162 * <TRK> Normal Global Track status for this Master, * Bit 3 also is 1 3163 * WHEN Service Mode track is BUSY 3164 * <p> 3165 * <CVH> High 3 BITS of CV#, and ms bit of DATA.7 3166 * <p> 3167 * <0,0,CV9,CV8 - 0,0, D7,CV7> 3168 * <p> 3169 * <CVL> Low 7 bits of 10 bit CV address. 3170 * <p> 3171 * <0,CV6,CV5,CV4-CV3,CV2,CV1,CV0> 3172 * <p> 3173 * <DATA7>Low 7 BITS OF data to WR or RD COMPARE 3174 * <p> 3175 * <0,D6,D5,D4 - D3,D2,D1,D0> * ms bit is at CVH bit 1 position. * * 3176 * Programmer Task Final Reply: * ---------------------------- * (if saw 3177 * LACK 3178 * <B4>,<7F>,<1>,<chk> code reply at task start) 3179 * <p> 3180 * <0xE7>,<0E>,<7C>,<PCMD>,<PSTAT>,<HOPSA>,<LOPSA>,<TRK>;<CVH>,<CVL>, 3181 * <p> 3182 * <DATA7>,<0>,<0>,<CHK> * * <PSTAT> Programmer Status error flags. 3183 * Reply codes resulting from * completed task in PCMD * D7-D4 -reserved 3184 * * D3 -1= User Aborted this command * D2 -1= Failed to detect READ 3185 * Compare acknowledge response * from decoder * D1 -1= No Write 3186 * acknowledge response from decoder * D0 -1= Service Mode programming 3187 * track empty- No decoder detected * * This <E7> response is issued 3188 * whenever a Programming task is completed. It echos most of the * 3189 * request information and returns the PSTAT status code to indicate how 3190 * the task completed. * If a READ was requested <DATA7> and <CVH> 3191 * contain the returned data, if the PSTAT indicates * a successful 3192 * readback (typically =0). Note that if a Paged Read fails to detect a 3193 * * successful Page write acknowledge when first setting the Page 3194 * register, the read will be * aborted, showing no Write acknowledge 3195 * flag D1=1. * 3196 * ******************************************************************************************** 3197 */ 3198 int cvData; 3199 int cvNumber; 3200 3201 // progTask = (progTaskMsg *) msgBuf; 3202 // slot - slot number for this request - slot 124 is programmer 3203 int pcmd = l.getElement(3); // programmer command 3204 int pstat = l.getElement(4); // programmer status error flags in 3205 // reply message 3206 int hopsa = l.getElement(5); // Ops mode - 7 high address bits 3207 // of loco to program 3208 int lopsa = l.getElement(6); // Ops mode - 7 low address bits of 3209 // loco to program 3210 /* trk - track status. Note: bit 3 shows if prog track is busy */ 3211 int cvh = l.getElement(8); // hi 3 bits of CV# and msb of data7 3212 int cvl = l.getElement(9); // lo 7 bits of CV# 3213 int data7 = l.getElement(10); // 7 bits of data to program, msb 3214 // is in cvh above 3215 3216 cvData = (((cvh & LnConstants.CVH_D7) << 6) | (data7 & 0x7f)); // was 3217 // PROG_DATA 3218 cvNumber = (((((cvh & LnConstants.CVH_CV8_CV9) >> 3) | (cvh & LnConstants.CVH_CV7)) * 128) + (cvl & 0x7f)) + 1; // was 3219 // PROG_CV_NUM(progTask) 3220 3221 if (command == LnConstants.OPC_WR_SL_DATA) { 3222 /* interpret the programming mode request (to programmer) */ 3223 switch ((pcmd & (LnConstants.PCMD_MODE_MASK | LnConstants.PCMD_RW))) { 3224 case LnConstants.PAGED_ON_SRVC_TRK: 3225 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_PAGED_RD", 3226 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3227 cvNumber)); 3228 case LnConstants.PAGED_ON_SRVC_TRK | LnConstants.PCMD_RW: 3229 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_PAGED_WR", 3230 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3231 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3232 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3233 case LnConstants.DIR_BYTE_ON_SRVC_TRK: 3234 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BYTE_RD", 3235 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3236 cvNumber)); 3237 case LnConstants.DIR_BYTE_ON_SRVC_TRK | LnConstants.PCMD_RW: 3238 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BYTE_WR", 3239 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3240 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3241 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3242 case LnConstants.DIR_BIT_ON_SRVC_TRK: 3243 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BIT_RD", 3244 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3245 cvNumber), cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3246 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true)); 3247 case LnConstants.DIR_BIT_ON_SRVC_TRK | LnConstants.PCMD_RW: 3248 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BIT_WR", 3249 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3250 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3251 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3252 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK: 3253 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE: 3254 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_REG_BYTE_RD", 3255 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3256 cvNumber)); 3257 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_RW: 3258 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: 3259 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_REG_BYTE_WR", 3260 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3261 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3262 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3263 case LnConstants.SRVC_TRK_RESERVED: 3264 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE: 3265 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_RD_RESERVED", 3266 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3267 cvNumber)); 3268 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_RW: 3269 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: 3270 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_WR_RESERVED", 3271 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3272 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3273 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3274 case LnConstants.OPS_BYTE_NO_FEEDBACK: 3275 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_RD_NO_FEEDBACK", 3276 convertToMixed(lopsa, hopsa), 3277 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3278 cvNumber)); 3279 case LnConstants.OPS_BYTE_NO_FEEDBACK | LnConstants.PCMD_RW: 3280 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_WR_NO_FEEDBACK", 3281 convertToMixed(lopsa, hopsa), 3282 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3283 cvNumber, cvData, 3284 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3285 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3286 case LnConstants.OPS_BYTE_FEEDBACK: 3287 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_RD_FEEDBACK", 3288 convertToMixed(lopsa, hopsa), 3289 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3290 cvNumber)); 3291 case LnConstants.OPS_BYTE_FEEDBACK | LnConstants.PCMD_RW: 3292 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_WR_FEEDBACK", 3293 convertToMixed(lopsa, hopsa), 3294 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3295 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3296 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3297 case LnConstants.OPS_BIT_NO_FEEDBACK: 3298 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_RD_NO_FEEDBACK", 3299 convertToMixed(lopsa, hopsa), 3300 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3301 cvNumber)); 3302 case LnConstants.OPS_BIT_NO_FEEDBACK | LnConstants.PCMD_RW: 3303 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_WR_NO_FEEDBACK", 3304 convertToMixed(lopsa, hopsa), 3305 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3306 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3307 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3308 case LnConstants.OPS_BIT_FEEDBACK: 3309 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_RD_FEEDBACK", 3310 convertToMixed(lopsa, hopsa), 3311 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3312 cvNumber)); 3313 case LnConstants.OPS_BIT_FEEDBACK | LnConstants.PCMD_RW: 3314 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_WR_FEEDBACK", 3315 convertToMixed(lopsa, hopsa), 3316 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3317 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3318 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3319 case 0: 3320 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UHLENBROCK_RD", 3321 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3322 cvNumber)); 3323 case LnConstants.PCMD_RW: 3324 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UHLENBROCK_WR", 3325 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3326 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3327 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3328 default: 3329 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UNKNOWN", 3330 pcmd, 3331 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3332 StringUtil.twoHexFromInt(pcmd)), 3333 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3334 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3335 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3336 } 3337 } else { 3338 /* interpret the programming mode response (from programmer) */ 3339 /* if we're reading the slot back, check the status 3340 * this is supposed to be the Programming task final reply 3341 * and will have the resulting status byte. 3342 */ 3343 String responseMessage = "(ODD BEHAVIOR - Default value not overwritten - report to developers!"; // NOI18N 3344 String hexMessage = ""; 3345 if (pstat != 0) { 3346 if ((pstat & LnConstants.PSTAT_USER_ABORTED) != 0) { 3347 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_USER_ABORT"); 3348 } else if ((pstat & LnConstants.PSTAT_READ_FAIL) != 0) { 3349 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_READ_COMPARE_ACK_DETECT"); 3350 } else if ((pstat & LnConstants.PSTAT_WRITE_FAIL) != 0) { 3351 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_WRITE_ACK_DETECT"); 3352 } else if ((pstat & LnConstants.PSTAT_NO_DECODER) != 0) { 3353 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_LOCO_ON_PROGRAMMING_TRACK"); 3354 } else if ((pstat & 0xF0) != 0) { 3355 if ((pstat & 0xF0) == 0x10) { 3356 // response from transponding decoder 3357 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_SUCCESS_VIA_RX4_BDL16X"); 3358 3359 } else { 3360 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_UNDECODED", 3361 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3362 StringUtil.twoHexFromInt(pstat))); 3363 hexMessage = Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 3364 } 3365 } 3366 } else { 3367 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_SUCCEEDED"); 3368 } 3369 3370 switch ((pcmd & (LnConstants.PCMD_MODE_MASK | LnConstants.PCMD_RW))) { 3371 case LnConstants.PAGED_ON_SRVC_TRK: 3372 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_PAGED_RD", 3373 responseMessage, 3374 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3375 cvNumber, cvData, 3376 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3377 StringUtil.twoHexFromInt(cvData)), 3378 StringUtil.to8Bits(cvData, true)))+hexMessage; 3379 case LnConstants.PAGED_ON_SRVC_TRK | LnConstants.PCMD_RW: 3380 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_PAGED_WR", 3381 responseMessage, 3382 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3383 cvNumber, cvData, 3384 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3385 StringUtil.twoHexFromInt(cvData)), 3386 StringUtil.to8Bits(cvData, true)))+hexMessage; 3387 case LnConstants.DIR_BYTE_ON_SRVC_TRK: 3388 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BYTE_RD", 3389 responseMessage, 3390 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3391 cvNumber, cvData, 3392 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3393 StringUtil.twoHexFromInt(cvData)), 3394 StringUtil.to8Bits(cvData, true)))+hexMessage; 3395 case LnConstants.DIR_BYTE_ON_SRVC_TRK | LnConstants.PCMD_RW: 3396 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BYTE_WR", 3397 responseMessage, 3398 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3399 cvNumber, 3400 cvData, 3401 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3402 StringUtil.twoHexFromInt(cvData)), 3403 StringUtil.to8Bits(cvData, true)))+hexMessage; 3404 case LnConstants.DIR_BIT_ON_SRVC_TRK: 3405 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BIT_RD", 3406 responseMessage, 3407 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3408 cvNumber, cvData, 3409 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3410 StringUtil.twoHexFromInt(cvData)), 3411 StringUtil.to8Bits(cvData, true)))+hexMessage; 3412 case LnConstants.DIR_BIT_ON_SRVC_TRK | LnConstants.PCMD_RW: 3413 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BIT_WR", 3414 responseMessage, 3415 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3416 cvNumber, cvData, 3417 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3418 StringUtil.twoHexFromInt(cvData)), 3419 StringUtil.to8Bits(cvData, true)))+hexMessage; 3420 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK: 3421 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE: 3422 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_REG_BYTE_RD", 3423 responseMessage, 3424 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3425 cvNumber, cvData, 3426 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3427 StringUtil.twoHexFromInt(cvData)), 3428 StringUtil.to8Bits(cvData, true)))+hexMessage; 3429 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_RW: 3430 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: 3431 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_REG_BYTE_WR", 3432 responseMessage, 3433 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3434 cvNumber, cvData, 3435 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3436 StringUtil.twoHexFromInt(cvData)), 3437 StringUtil.to8Bits(cvData, true)))+hexMessage; 3438 case LnConstants.SRVC_TRK_RESERVED: 3439 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE: 3440 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_RD_RESERVED", 3441 responseMessage, 3442 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3443 cvNumber, cvData, 3444 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3445 StringUtil.twoHexFromInt(cvData)), 3446 StringUtil.to8Bits(cvData, true)))+hexMessage; 3447 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_RW: 3448 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: 3449 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_WR_RESERVED", 3450 responseMessage, 3451 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3452 cvNumber, cvData, 3453 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3454 StringUtil.twoHexFromInt(cvData)), 3455 StringUtil.to8Bits(cvData, true)))+hexMessage; 3456 case LnConstants.OPS_BYTE_NO_FEEDBACK: 3457 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_RD_NO_FEEDBACK", 3458 responseMessage, 3459 convertToMixed(lopsa, hopsa), 3460 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3461 cvNumber, cvData, 3462 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3463 StringUtil.twoHexFromInt(cvData)), 3464 StringUtil.to8Bits(cvData, true)))+hexMessage; 3465 case LnConstants.OPS_BYTE_NO_FEEDBACK | LnConstants.PCMD_RW: 3466 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_WR_NO_FEEDBACK", 3467 responseMessage, 3468 convertToMixed(lopsa, hopsa), 3469 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3470 cvNumber, cvData, 3471 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3472 StringUtil.twoHexFromInt(cvData)), 3473 StringUtil.to8Bits(cvData, true)))+hexMessage; 3474 case LnConstants.OPS_BYTE_FEEDBACK: 3475 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_RD_FEEDBACK", 3476 responseMessage, 3477 convertToMixed(lopsa, hopsa), 3478 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3479 cvNumber, cvData, 3480 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3481 StringUtil.twoHexFromInt(cvData)), 3482 StringUtil.to8Bits(cvData, true)))+hexMessage; 3483 case LnConstants.OPS_BYTE_FEEDBACK | LnConstants.PCMD_RW: 3484 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_WR_FEEDBACK", 3485 responseMessage, 3486 convertToMixed(lopsa, hopsa), 3487 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3488 cvNumber, cvData, 3489 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3490 StringUtil.twoHexFromInt(cvData)), 3491 StringUtil.to8Bits(cvData, true)))+hexMessage; 3492 case LnConstants.OPS_BIT_NO_FEEDBACK: 3493 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_RD_NO_FEEDBACK", 3494 responseMessage, 3495 convertToMixed(lopsa, hopsa), 3496 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3497 cvNumber, cvData, 3498 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3499 StringUtil.twoHexFromInt(cvData)), 3500 StringUtil.to8Bits(cvData, true)))+hexMessage; 3501 case LnConstants.OPS_BIT_NO_FEEDBACK | LnConstants.PCMD_RW: 3502 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_WR_NO_FEEDBACK", 3503 responseMessage, 3504 convertToMixed(lopsa, hopsa), 3505 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3506 cvNumber, cvData, 3507 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3508 StringUtil.twoHexFromInt(cvData)), 3509 StringUtil.to8Bits(cvData, true)))+hexMessage; 3510 case LnConstants.OPS_BIT_FEEDBACK: 3511 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_RD_FEEDBACK", 3512 responseMessage, 3513 convertToMixed(lopsa, hopsa), 3514 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3515 cvNumber, cvData, 3516 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3517 StringUtil.twoHexFromInt(cvData)), 3518 StringUtil.to8Bits(cvData, true)))+hexMessage; 3519 case LnConstants.OPS_BIT_FEEDBACK | LnConstants.PCMD_RW: 3520 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_WR_FEEDBACK", 3521 responseMessage, 3522 convertToMixed(lopsa, hopsa), 3523 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3524 cvNumber, cvData, 3525 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3526 StringUtil.twoHexFromInt(cvData)), 3527 StringUtil.to8Bits(cvData, true)))+hexMessage; 3528 case 0: 3529 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UHLENBROCK_RD", 3530 responseMessage, 3531 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3532 cvNumber, cvData, 3533 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3534 StringUtil.twoHexFromInt(cvData)), 3535 StringUtil.to8Bits(cvData, true)))+hexMessage; 3536 case LnConstants.PCMD_RW: 3537 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UHLENBROCK_WR", 3538 responseMessage, 3539 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3540 cvNumber, cvData, 3541 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3542 StringUtil.twoHexFromInt(cvData)), 3543 StringUtil.to8Bits(cvData, true)))+hexMessage; 3544 default: 3545 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UNKNOWN", 3546 pcmd, 3547 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3548 StringUtil.twoHexFromInt(pcmd)), 3549 responseMessage, 3550 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3551 cvNumber, cvData, 3552 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3553 StringUtil.twoHexFromInt(cvData)), 3554 StringUtil.to8Bits(cvData, true)))+hexMessage; 3555 } 3556 } 3557 } 3558 3559 private static String interpretCmdStnCfgSlotRdWr(LocoNetMessage l, int command) { 3560 /* 3561 * ************************************************ 3562 * Configuration slot, holding op switches 3563 * ************************************************ 3564 * <p> 3565 * NOTE: previously, this message provided specific text about the 3566 * meaning of each OpSw when it was closed. With the advent of newer 3567 * Digitrax command stations, the specific information was no longer 3568 * completely accurate. As such, this information now only shows bits as 3569 * "closed" or "thrown". 3570 */ 3571 String thrown = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); 3572 String closed = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED"); 3573 3574 String opswGroup1, opswGroup2, opswGroup3, opswGroup4, 3575 opswGroup5, opswGroup6, opswGroup7, opswGroup8; 3576 opswGroup1 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3577 1, ((l.getElement(3) & 0x01) != 0 ? closed : thrown), 3578 2, ((l.getElement(3) & 0x02) != 0 ? closed : thrown), 3579 3, ((l.getElement(3) & 0x04) != 0 ? closed : thrown), 3580 4, ((l.getElement(3) & 0x08) != 0 ? closed : thrown), 3581 5, ((l.getElement(3) & 0x10) != 0 ? closed : thrown), 3582 6, ((l.getElement(3) & 0x20) != 0 ? closed : thrown), 3583 7, ((l.getElement(3) & 0x40) != 0 ? closed : thrown), 3584 8, thrown); 3585 opswGroup2 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3586 9, ((l.getElement(4) & 0x01) != 0 ? closed : thrown), 3587 10, ((l.getElement(4) & 0x02) != 0 ? closed : thrown), 3588 11, ((l.getElement(4) & 0x04) != 0 ? closed : thrown), 3589 12, ((l.getElement(4) & 0x08) != 0 ? closed : thrown), 3590 13, ((l.getElement(4) & 0x10) != 0 ? closed : thrown), 3591 14, ((l.getElement(4) & 0x20) != 0 ? closed : thrown), 3592 15, ((l.getElement(4) & 0x40) != 0 ? closed : thrown), 3593 16, thrown); 3594 opswGroup3 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3595 17, ((l.getElement(5) & 0x01) != 0 ? closed : thrown), 3596 18, ((l.getElement(5) & 0x02) != 0 ? closed : thrown), 3597 19, ((l.getElement(5) & 0x04) != 0 ? closed : thrown), 3598 20, ((l.getElement(5) & 0x08) != 0 ? closed : thrown), 3599 21, ((l.getElement(5) & 0x10) != 0 ? closed : thrown), 3600 22, ((l.getElement(5) & 0x20) != 0 ? closed : thrown), 3601 23, ((l.getElement(5) & 0x40) != 0 ? closed : thrown), 3602 24, thrown); 3603 opswGroup4 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3604 25, ((l.getElement(6) & 0x01) != 0 ? closed : thrown), 3605 26, ((l.getElement(6) & 0x02) != 0 ? closed : thrown), 3606 27, ((l.getElement(6) & 0x04) != 0 ? closed : thrown), 3607 28, ((l.getElement(6) & 0x08) != 0 ? closed : thrown), 3608 29, ((l.getElement(6) & 0x10) != 0 ? closed : thrown), 3609 30, ((l.getElement(6) & 0x20) != 0 ? closed : thrown), 3610 31, ((l.getElement(6) & 0x40) != 0 ? closed : thrown), 3611 32, thrown); 3612 opswGroup5 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3613 33, ((l.getElement(8) & 0x01) != 0 ? closed : thrown), 3614 34, ((l.getElement(8) & 0x02) != 0 ? closed : thrown), 3615 35, ((l.getElement(8) & 0x04) != 0 ? closed : thrown), 3616 36, ((l.getElement(8) & 0x08) != 0 ? closed : thrown), 3617 37, ((l.getElement(8) & 0x10) != 0 ? closed : thrown), 3618 38, ((l.getElement(8) & 0x20) != 0 ? closed : thrown), 3619 39, ((l.getElement(8) & 0x40) != 0 ? closed : thrown), 3620 40, thrown); 3621 opswGroup6 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3622 41, ((l.getElement(9) & 0x01) != 0 ? closed : thrown), 3623 42, ((l.getElement(9) & 0x02) != 0 ? closed : thrown), 3624 43, ((l.getElement(9) & 0x04) != 0 ? closed : thrown), 3625 44, ((l.getElement(9) & 0x08) != 0 ? closed : thrown), 3626 45, ((l.getElement(9) & 0x10) != 0 ? closed : thrown), 3627 46, ((l.getElement(9) & 0x20) != 0 ? closed : thrown), 3628 47, ((l.getElement(9) & 0x40) != 0 ? closed : thrown), 3629 48, thrown); 3630 opswGroup7 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3631 49, ((l.getElement(10) & 0x01) != 0 ? closed : thrown), 3632 50, ((l.getElement(10) & 0x02) != 0 ? closed : thrown), 3633 51, ((l.getElement(10) & 0x04) != 0 ? closed : thrown), 3634 52, ((l.getElement(10) & 0x08) != 0 ? closed : thrown), 3635 53, ((l.getElement(10) & 0x10) != 0 ? closed : thrown), 3636 54, ((l.getElement(10) & 0x20) != 0 ? closed : thrown), 3637 55, ((l.getElement(10) & 0x40) != 0 ? closed : thrown), 3638 56, thrown); 3639 opswGroup8 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3640 57, ((l.getElement(11) & 0x01) != 0 ? closed : thrown), 3641 58, ((l.getElement(11) & 0x02) != 0 ? closed : thrown), 3642 59, ((l.getElement(11) & 0x04) != 0 ? closed : thrown), 3643 60, ((l.getElement(11) & 0x08) != 0 ? closed : thrown), 3644 61, ((l.getElement(11) & 0x10) != 0 ? closed : thrown), 3645 62, ((l.getElement(11) & 0x20) != 0 ? closed : thrown), 3646 63, ((l.getElement(11) & 0x40) != 0 ? closed : thrown), 3647 64, thrown); 3648 return Bundle.getMessage(((command == LnConstants.OPC_WR_SL_DATA) 3649 ? "LN_MSG_SLOT_CMD_STN_CFG_WRITE_REQ" 3650 : "LN_MSG_SLOT_CMD_STN_CFG_READ_REPORT"), 3651 opswGroup1, opswGroup2, opswGroup3, opswGroup4, 3652 opswGroup5, opswGroup6, opswGroup7, opswGroup8); 3653 3654 } 3655 3656 private static String interpretCmdStnExtCfgSlotRdWr(LocoNetMessage l, int command) { 3657 /* 3658 * ************************************************ 3659 * Extended Configuration slot, holding op switches 3660 * ************************************************ 3661 */ 3662 String thrown = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); 3663 String closed = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED"); 3664 3665 String opswGroup1, opswGroup2, opswGroup3, opswGroup4, 3666 opswGroup5, opswGroup6, opswGroup7, opswGroup8; 3667 opswGroup1 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3668 65, ((l.getElement(3) & 0x01) != 0 ? closed : thrown), 3669 66, ((l.getElement(3) & 0x02) != 0 ? closed : thrown), 3670 67, ((l.getElement(3) & 0x04) != 0 ? closed : thrown), 3671 68, ((l.getElement(3) & 0x08) != 0 ? closed : thrown), 3672 69, ((l.getElement(3) & 0x10) != 0 ? closed : thrown), 3673 70, ((l.getElement(3) & 0x20) != 0 ? closed : thrown), 3674 71, ((l.getElement(3) & 0x40) != 0 ? closed : thrown), 3675 72, thrown); 3676 opswGroup2 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3677 73, ((l.getElement(4) & 0x01) != 0 ? closed : thrown), 3678 74, ((l.getElement(4) & 0x02) != 0 ? closed : thrown), 3679 75, ((l.getElement(4) & 0x04) != 0 ? closed : thrown), 3680 76, ((l.getElement(4) & 0x08) != 0 ? closed : thrown), 3681 77, ((l.getElement(4) & 0x10) != 0 ? closed : thrown), 3682 78, ((l.getElement(4) & 0x20) != 0 ? closed : thrown), 3683 79, ((l.getElement(4) & 0x40) != 0 ? closed : thrown), 3684 80, thrown); 3685 opswGroup3 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3686 81, ((l.getElement(5) & 0x01) != 0 ? closed : thrown), 3687 82, ((l.getElement(5) & 0x02) != 0 ? closed : thrown), 3688 83, ((l.getElement(5) & 0x04) != 0 ? closed : thrown), 3689 84, ((l.getElement(5) & 0x08) != 0 ? closed : thrown), 3690 85, ((l.getElement(5) & 0x10) != 0 ? closed : thrown), 3691 86, ((l.getElement(5) & 0x20) != 0 ? closed : thrown), 3692 87, ((l.getElement(5) & 0x40) != 0 ? closed : thrown), 3693 88, thrown); 3694 opswGroup4 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3695 89, ((l.getElement(6) & 0x01) != 0 ? closed : thrown), 3696 90, ((l.getElement(6) & 0x02) != 0 ? closed : thrown), 3697 91, ((l.getElement(6) & 0x04) != 0 ? closed : thrown), 3698 92, ((l.getElement(6) & 0x08) != 0 ? closed : thrown), 3699 93, ((l.getElement(6) & 0x10) != 0 ? closed : thrown), 3700 94, ((l.getElement(6) & 0x20) != 0 ? closed : thrown), 3701 95, ((l.getElement(6) & 0x40) != 0 ? closed : thrown), 3702 96, thrown); 3703 opswGroup5 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3704 97, ((l.getElement(8) & 0x01) != 0 ? closed : thrown), 3705 98, ((l.getElement(8) & 0x02) != 0 ? closed : thrown), 3706 99, ((l.getElement(8) & 0x04) != 0 ? closed : thrown), 3707 100, ((l.getElement(8) & 0x08) != 0 ? closed : thrown), 3708 101, ((l.getElement(8) & 0x10) != 0 ? closed : thrown), 3709 102, ((l.getElement(8) & 0x20) != 0 ? closed : thrown), 3710 103, ((l.getElement(8) & 0x40) != 0 ? closed : thrown), 3711 104, thrown); 3712 opswGroup6 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3713 105, ((l.getElement(9) & 0x01) != 0 ? closed : thrown), 3714 106, ((l.getElement(9) & 0x02) != 0 ? closed : thrown), 3715 107, ((l.getElement(9) & 0x04) != 0 ? closed : thrown), 3716 108, ((l.getElement(9) & 0x08) != 0 ? closed : thrown), 3717 109, ((l.getElement(9) & 0x10) != 0 ? closed : thrown), 3718 110, ((l.getElement(9) & 0x20) != 0 ? closed : thrown), 3719 111, ((l.getElement(9) & 0x40) != 0 ? closed : thrown), 3720 112, thrown); 3721 opswGroup7 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3722 113, ((l.getElement(10) & 0x01) != 0 ? closed : thrown), 3723 114, ((l.getElement(10) & 0x02) != 0 ? closed : thrown), 3724 115, ((l.getElement(10) & 0x04) != 0 ? closed : thrown), 3725 116, ((l.getElement(10) & 0x08) != 0 ? closed : thrown), 3726 117, ((l.getElement(10) & 0x10) != 0 ? closed : thrown), 3727 118, ((l.getElement(10) & 0x20) != 0 ? closed : thrown), 3728 119, ((l.getElement(10) & 0x40) != 0 ? closed : thrown), 3729 120, thrown); 3730 opswGroup8 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3731 121, ((l.getElement(11) & 0x01) != 0 ? closed : thrown), 3732 122, ((l.getElement(11) & 0x02) != 0 ? closed : thrown), 3733 123, ((l.getElement(11) & 0x04) != 0 ? closed : thrown), 3734 124, ((l.getElement(11) & 0x08) != 0 ? closed : thrown), 3735 125, ((l.getElement(11) & 0x10) != 0 ? closed : thrown), 3736 126, ((l.getElement(11) & 0x20) != 0 ? closed : thrown), 3737 127, ((l.getElement(11) & 0x40) != 0 ? closed : thrown), 3738 128, thrown); 3739 return Bundle.getMessage(((command == LnConstants.OPC_WR_SL_DATA) 3740 ? "LN_MSG_SLOT_CMD_STN_EXT_CFG_WRITE_REQ" 3741 : "LN_MSG_SLOT_CMD_STN_EXT_CFG_READ_REPORT"), 3742 opswGroup1, opswGroup2, opswGroup3, opswGroup4, 3743 opswGroup5, opswGroup6, opswGroup7, opswGroup8); 3744 } 3745 3746 private static String interpretStandardSlotRdWr(LocoNetMessage l, int id1, int id2, int command, int slot) { 3747 /* 3748 * ************************************************ 3749 * normal slot read/write message - see info above * 3750 * ************************************************ 3751 */ 3752 int trackStatus = l.getElement(7); // track status 3753 int stat = l.getElement(3); // slot status 3754 int adr = l.getElement(4); // loco address 3755 int spd = l.getElement(5); // command speed 3756 int dirf = l.getElement(6); // direction and F0-F4 bits 3757 String[] dirf0_4 = interpretF0_F4toStrings(dirf); 3758 int ss2 = l.getElement(8); // slot status 2 (tells how to use 3759 // ID1/ID2 & ADV Consist) 3760 int adr2 = l.getElement(9); // loco address high 3761 int snd = l.getElement(10); // Sound 1-4 / F5-F8 3762 String[] sndf5_8 = interpretF5_F8toStrings(snd); 3763 3764 String locoAdrStr = figureAddressIncludingAliasing(adr, adr2, ss2, id1, id2); 3765 return Bundle.getMessage(((command == LnConstants.OPC_WR_SL_DATA) 3766 ? "LN_MSG_SLOT_LOCO_INFO_WRITE" 3767 : "LN_MSG_SLOT_LOCO_INFO_READ"), 3768 slot, 3769 locoAdrStr, 3770 LnConstants.CONSIST_STAT(stat), 3771 LnConstants.LOCO_STAT(stat), 3772 LnConstants.DEC_MODE(stat), 3773 directionOfTravelString((dirf & LnConstants.DIRF_DIR) == 0), 3774 spd, // needs re-interpretation for some cases of slot consisting state 3775 dirf0_4[0], 3776 dirf0_4[1], 3777 dirf0_4[2], 3778 dirf0_4[3], 3779 dirf0_4[4], 3780 sndf5_8[0], 3781 sndf5_8[1], 3782 sndf5_8[2], 3783 sndf5_8[3], 3784 trackStatusByteToString(trackStatus), 3785 Bundle.getMessage("LN_MSG_SLOT_HELPER_SS2_SIMPLE", 3786 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3787 StringUtil.twoHexFromInt(ss2))), 3788 Bundle.getMessage("LN_MSG_SLOT_HELPER_ID1_ID2_AS_THROTTLE_ID", 3789 idString(id1, id2))); 3790 } 3791 3792 private static String interpretOpcPanelResponse(LocoNetMessage l) { 3793 switch (l.getElement(1)) { 3794 case 0x12: { 3795 // Bit 3 (0x08 in hex) is set by every UR-92 we've ever captured. 3796 // The hypothesis is this indicates duplex enabled, but this has 3797 // not been confirmed with Digitrax. 3798 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR92", 3799 l.getElement(3) & 0x07, 3800 ((l.getElement(3) & 0x08) == 0x08 3801 ? Bundle.getMessage("LN_MSG_HELPER_D7_UR92_DUPLEX") 3802 : "")); 3803 } 3804 case 0x17: { 3805 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR90", 3806 l.getElement(3) & 0x07); 3807 } 3808 case 0x1F: { 3809 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR91", 3810 l.getElement(3) & 0x07); 3811 } 3812 case 0x22: { 3813 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR93", 3814 l.getElement(3) & 0x07); 3815 } 3816 default: { 3817 return ""; 3818 } 3819 } 3820 } 3821 3822 private static String interpretOpcLissyUpdate(LocoNetMessage l) { 3823 /* 3824 * OPC_LISSY_UPDATE 0xE4 3825 * 3826 * LISSY is an automatic train detection system made by Uhlenbrock. 3827 * All documentation appears to be in German. 3828 * Also used by LocoIO-based RFID readers eg. GCA51 3829 */ 3830 log.debug("Message from LISSY: {}", Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString())); 3831 switch (l.getElement(1)) { 3832 case 0x08: // Format LISSY message 3833 int unit = (l.getElement(4) & 0x7F); 3834 if ((l.getElement(3) & 0x40) != 0) { // Loco movement 3835 int category = l.getElement(2) + 1; 3836 int address = (l.getElement(6) & 0x7F) + 128 * (l.getElement(5) & 0x7F); 3837 return Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_LOCO_MOVEMENT", 3838 unit, 3839 Integer.toString(address), 3840 Integer.toString(category), 3841 ((l.getElement(3) & 0x20) == 0 3842 ? Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_DIRECTION_NORTH") 3843 : Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_DIRECTION_SOUTH"))); 3844 } else { // other messages 3845 switch (l.getElement(2)) { 3846 case 0x00: // Loco speed 3847 int speed = (l.getElement(6) & 0x7F) + 128 * (l.getElement(5) & 0x7F); 3848 return Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_LOCO_SPEED", 3849 unit, 3850 Integer.toString(speed)); 3851 3852 case 0x01: // Block status 3853 return Bundle.getMessage("LN_MSG_LISSY_BLOCK_REPORT", 3854 unit, 3855 ((l.getElement(6) & 0x01) == 0 3856 ? Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_BLOCK_FREE") 3857 : Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_BLOCK_OCCUPIED"))); 3858 default: 3859 break; 3860 } 3861 } 3862 break; 3863 3864 case 0x0A: // Format special message 3865 int element = l.getElement(2) * 128 + l.getElement(3); 3866 int stat1 = l.getElement(5); 3867 int stat2 = l.getElement(6); 3868 String status; 3869 switch (stat1 & 0x30) { 3870 case 0x30: 3871 status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_BOTH_RES"); 3872 break; 3873 case 0x10: 3874 status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_AX_RES"); 3875 break; 3876 case 0x20: 3877 status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_XA_RES"); 3878 break; 3879 default: 3880 status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_NO_RES"); 3881 break; 3882 } 3883 3884 return Bundle.getMessage("LN_MSG_SE_REPORT", 3885 (element + 1), element, 3886 l.getElement(7), l.getElement(8), 3887 status, 3888 Bundle.getMessage(((stat2 & 0x01) != 0) 3889 ? "LN_MSG_SWITCH_STATE_THROWN" 3890 : "LN_MSG_SWITCH_STATE_CLOSED"), 3891 Bundle.getMessage(((stat1 & 0x01) != 0) 3892 ? "LN_MSG_SE_REPORT_HELPER_OCCUPIED" 3893 : "LN_MSG_SE_REPORT_HELPER_UNOCCUPIED")); 3894 case 0x09: 3895 if (l.getElement(4) == 0x00) { 3896 return Bundle.getMessage("LN_MSG_UNRECOGNIZED_SIG_STATE_REPORT_MAY_BE_FROM_CML_HW")+ 3897 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 3898 } 3899 break; 3900 case 0x0C: 3901 case 0x0E: 3902 if (l.getElement(2) == 0x41 ) { 3903 // RFID-5 or 7 reader report 3904 // RFID-7 reader report [E4 0E 41 00 02 04 3A 4B 4A 60 60 01 50 38] 3905 // elem[3] = sensorAddr >> 7; addr.high 3906 // elem[4] = sensorAddr & 0x7F; addr.low 3907 StringBuilder tg = new StringBuilder(); 3908 int max = l.getElement(1) - 2; // GCA51 RFID-7 elem(1) = size = 0x0E; RFID-5 elem(1) = size = 0x0C 3909 int rfidHi = l.getElement(max); // MSbits are transmitted via element(max) 3910 for (int j = 5; j < max; j++) { 3911 int shift = j-5; 3912 int hi = 0x0; 3913 if(((rfidHi >> shift) & 0x1) == 1) hi = 0x80; 3914 tg.append(String.format("%1$02X", l.getElement(j) + hi)); 3915 } 3916 int portAddress = l.getElement(3) << 7 | l.getElement(4); 3917 int msgType = 7; 3918 if (max == 9) msgType = 5; 3919 return Bundle.getMessage("LN_MSG_LISSY_RFIDX_REPORT", msgType, portAddress, tg.toString()); 3920 } 3921 break; 3922 default: 3923 break; 3924 } 3925 return ""; 3926 } 3927 3928 private static String interpretOpcImmPacket(LocoNetMessage l) { 3929 String result; 3930 3931 /* 3932 * OPC_IMM_PACKET 0xED 3933 */ 3934 if (l.getElement(1) == 0x0F) { // length = 15 3935 // check for a specific type - Uhlenbrock LNSV Programming messages format 3936 result = interpretLncvMessage(l); 3937 if (!result.isEmpty()) { 3938 return result; 3939 } 3940 3941 return ""; 3942 } 3943 3944 /* Else: 3945 * OPC_IMM_PACKET 0xED ;SEND n-byte packet immediate LACK 3946 * ; Follow on message: LACK 3947 * ; <0xED>,<0B>,<7F>,<REPS>,<DHI>,<IM1>,<IM2>, 3948 * ; <IM3>,<IM4>,<IM5>,<CHK> 3949 * ; <DHI>=<0,0,1,IM5.7-IM4.7,IM3.7,IM2.7,IM1.7> 3950 * ; <REPS> D4,5,6=#IM bytes, 3951 * ; D3=0(reserved); 3952 * ; D2,1,0=repeat CNT 3953 * ; IF Not limited MASTER then 3954 * ; LACK=<B4>,<7D>,<7F>,<chk> if CMD ok 3955 * ; IF limited MASTER then Lim Masters respond 3956 * ; with <B4>,<7E>,<lim adr>,<chk> 3957 * ; IF internal buffer BUSY/full respond 3958 * ; with <B4>,<7D>,<0>,<chk> 3959 * ; (NOT IMPLEMENTED IN DT200) 3960 * 3961 * This sends a raw NMRA packet across the LocoNet. 3962 * 3963 * Page 11 of LocoNet Personal Edition v1.0. 3964 * 3965 * Decodes for the F9-F28 functions taken from the NMRA standards and 3966 * coded by Leo Bicknell. 3967 */ 3968 3969 // sendPkt = (sendPktMsg *) msgBuf; 3970 int val7f = l.getElement(2); 3971 /* fixed value of 0x7f */ 3972 3973 int reps = l.getElement(3); 3974 /* repeat count */ 3975 3976 int dhi = l.getElement(4); 3977 /* high bits of data bytes */ 3978 3979 int im1 = l.getElement(5); 3980 int im2 = l.getElement(6); 3981 int im3 = l.getElement(7); 3982 int im4 = l.getElement(8); 3983 int im5 = l.getElement(9); 3984 int mobileDecoderAddress = -999; 3985 int nmraInstructionType = -999; 3986 int nmraSubInstructionType = -999; 3987 int playableWhistleLevel = -999; 3988 3989 // see if it really is a 'Send Packet' as defined in LocoNet PE 3990 if ((val7f == 0x7f) && (l.getElement(1) == 0x0B)) { 3991 int len = ((reps & 0x70) >> 4); 3992 if (len < 2) { 3993 return ""; // no valid NMRA packets of less than 2 bytes. 3994 } 3995 // duplication of packet data as packetInt was deemed necessary 3996 // due to issues with msBit loss when converting from "byte" to 3997 // integral forms 3998 byte[] packet = new byte[len]; 3999 int[] packetInt = new int[len]; 4000 packet[0] = (byte) (im1 + ((dhi & 0x01) != 0 ? 0x80 : 0)); 4001 packetInt[0] = (im1 + ((dhi & 0x01) != 0 ? 0x80 : 0)); 4002 4003 // len >= 2 always true at this point 4004 packet[1] = (byte) (im2 + ((dhi & 0x02) != 0 ? 0x80 : 0)); 4005 packetInt[1] = (im2 + ((dhi & 0x02) != 0 ? 0x80 : 0)); 4006 4007 if (len >= 3) { 4008 packet[2] = (byte) (im3 + ((dhi & 0x04) != 0 ? 0x80 : 0)); 4009 packetInt[2] = (im3 + ((dhi & 0x04) != 0 ? 0x80 : 0)); 4010 } 4011 if (len >= 4) { 4012 packet[3] = (byte) (im4 + ((dhi & 0x08) != 0 ? 0x80 : 0)); 4013 packetInt[3] = (im4 + ((dhi & 0x08) != 0 ? 0x80 : 0)); 4014 } 4015 if (len >= 5) { 4016 packet[4] = (byte) (im5 + ((dhi & 0x10) != 0 ? 0x80 : 0)); 4017 packetInt[4] = (im5 + ((dhi & 0x10) != 0 ? 0x80 : 0)); 4018 } 4019 4020 int address; 4021 // compute some information which is useful for decoding 4022 // the "Playable" whistle message 4023 // Information reverse-engineered by B. Milhaupt and used with permission 4024 if ((packetInt[0] & 0x80) == 0x0) { 4025 // immediate packet addresses a 7-bit multi-function (mobile) decoder 4026 mobileDecoderAddress = packetInt[0]; 4027 nmraInstructionType = (packetInt[1] & 0xE) >> 5; 4028 nmraSubInstructionType = (packetInt[1] & 0x1f); 4029 if ((nmraSubInstructionType == 0x1d) && (packetInt[2] == 0x7f)) { 4030 playableWhistleLevel = packetInt[3]; 4031 } 4032 } else if ((packetInt[0] & 0xC0) == 0xC0) { 4033 // immediate packet addresses a 14-bit multi-function (mobile) decoder 4034 mobileDecoderAddress = ((packetInt[0] & 0x3F) << 8) + packetInt[1]; 4035 nmraInstructionType = (packetInt[2] & 0xE0) >> 5; 4036 nmraSubInstructionType = (packetInt[2] & 0x1f); 4037 if ((nmraSubInstructionType == 0x1d) && (packetInt[3] == 0x7f)) { 4038 playableWhistleLevel = packetInt[4]; 4039 } 4040 } 4041 // immediate packet not addressed to a multi-function (mobile) decoder 4042 if ((mobileDecoderAddress >= 0) 4043 && (nmraInstructionType == 1) 4044 && (nmraSubInstructionType == 0x1D)) { 4045 // the "Playable" whistle message 4046 // Information reverse-engineered by B. Milhaupt and used with permission 4047 return Bundle.getMessage("LN_MSG_PLAYABLE_WHISTLE_CONTROL", 4048 Integer.toString(mobileDecoderAddress), 4049 playableWhistleLevel, 4050 (reps & 0x7)); 4051 } 4052 4053 // F9-F28 w/a long address. 4054 if ((packetInt[0] & 0xC0) == 0xC0) { 4055 address = ((packetInt[0] & 0x3F) << 8) + packetInt[1]; 4056 4057 if ((packetInt[2] & 0xFF) == 0xDF) { 4058 // Functions 21-28 4059 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F21_TO_F28", 4060 Integer.toString(address), 4061 Bundle.getMessage(((packetInt[3] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4062 Bundle.getMessage(((packetInt[3] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4063 Bundle.getMessage(((packetInt[3] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4064 Bundle.getMessage(((packetInt[3] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4065 Bundle.getMessage(((packetInt[3] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4066 Bundle.getMessage(((packetInt[3] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4067 Bundle.getMessage(((packetInt[3] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4068 Bundle.getMessage(((packetInt[3] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4069 } else if ((packetInt[2] & 0xFF) == 0xDE) { 4070 // Functions 13-20 4071 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F13_TO_F20", 4072 Integer.toString(address), 4073 Bundle.getMessage((((packetInt[3] & 0x01) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4074 Bundle.getMessage((((packetInt[3] & 0x02) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4075 Bundle.getMessage((((packetInt[3] & 0x04) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4076 Bundle.getMessage((((packetInt[3] & 0x08) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4077 Bundle.getMessage((((packetInt[3] & 0x10) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4078 Bundle.getMessage((((packetInt[3] & 0x20) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4079 Bundle.getMessage((((packetInt[3] & 0x40) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4080 Bundle.getMessage((((packetInt[3] & 0x80) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4081 } else if ((packetInt[2] & 0xF0) == 0xA0) { 4082 // Functions 9-12 4083 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F9_TO_F12", 4084 Integer.toString(address), 4085 Bundle.getMessage((((packetInt[2] & 0x01) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4086 Bundle.getMessage((((packetInt[2] & 0x02) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4087 Bundle.getMessage((((packetInt[2] & 0x04) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4088 Bundle.getMessage((((packetInt[2] & 0x08) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4089 } else { 4090 return Bundle.getMessage("LN_MSG_OPC_IMM_PKT_GENERIC", 4091 ((reps & 0x70) >> 4), 4092 (reps & 0x07), 4093 reps, 4094 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4095 StringUtil.twoHexFromInt(dhi)), 4096 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4097 StringUtil.twoHexFromInt(im1)), 4098 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4099 StringUtil.twoHexFromInt(im2)), 4100 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4101 StringUtil.twoHexFromInt(im3)), 4102 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4103 StringUtil.twoHexFromInt(im4)), 4104 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4105 StringUtil.twoHexFromInt(im5)), 4106 NmraPacket.format(packet)); 4107 } 4108 } else { // F9-F28 w/a short address. 4109 address = packetInt[0]; 4110 if ((packetInt[1] & 0xFF) == 0xDF) { 4111 // Functions 21-28 4112 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F21_TO_F28", 4113 address, 4114 Bundle.getMessage(((packetInt[2] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4115 Bundle.getMessage(((packetInt[2] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4116 Bundle.getMessage(((packetInt[2] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4117 Bundle.getMessage(((packetInt[2] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4118 Bundle.getMessage(((packetInt[2] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4119 Bundle.getMessage(((packetInt[2] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4120 Bundle.getMessage(((packetInt[2] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4121 Bundle.getMessage(((packetInt[2] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4122 4123 } else if ((packetInt[1] & 0xFF) == 0xDE) { 4124 // Functions 13-20 4125 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F13_TO_F20", 4126 address, 4127 Bundle.getMessage(((packetInt[2] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4128 Bundle.getMessage(((packetInt[2] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4129 Bundle.getMessage(((packetInt[2] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4130 Bundle.getMessage(((packetInt[2] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4131 Bundle.getMessage(((packetInt[2] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4132 Bundle.getMessage(((packetInt[2] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4133 Bundle.getMessage(((packetInt[2] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4134 Bundle.getMessage(((packetInt[2] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4135 } else if ((packetInt[1] & 0xF0) == 0xA0) { 4136 // Functions 9-12 4137 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F9_TO_F12", 4138 address, 4139 Bundle.getMessage(((packetInt[1] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4140 Bundle.getMessage(((packetInt[1] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4141 Bundle.getMessage(((packetInt[1] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4142 Bundle.getMessage(((packetInt[1] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4143 } else { 4144 // Unknown 4145 if ((packetInt[0] & 0xC0) == 0x80 ) { 4146 /* 4147 * 2.4.7 Extended Decoder Control Packet address for 4148 * operations mode programming and Aspect Setting. 4149 * 10AAAAAA 0 0AAA0AA1 4150 * Packets 3 bytes in length are Accessory Aspect packets (2.4.3) 4151 * {preamble} 10AAAAAA 0 0AAA0AA1 0 XXXXXXXX 0 EEEEEEEE 1 4152 * Please note that the use of 0 in bit 3 of byte 2 is to 4153 * ensure that this packet cannot be confused with the 4154 * legacy accessory-programming packets. The resulting packet 4155 * would be: 4156 * {preamble} 10AAAAAA 0 0AAA0AA1 0 (1110CCVV 0 VVVVVVVV 0 DDDDDDDD) 0 EEEEEEEE 1 4157 * A5 43 00 4158 * 10100101 0010011 00000000 4159 * Signal Decoder Address (Configuration Variable Access Instruction) Error Byte 4160 */ 4161 log.debug("Is an Extended Accessory Ops-mode CV access or Set Signal Aspect"); 4162 log.debug(" LocoNet message: {} {} {} {} {} {} {} {} {} {} {}", 4163 StringUtil.twoHexFromInt(l.getElement(0)), 4164 StringUtil.twoHexFromInt(l.getElement(1)), 4165 StringUtil.twoHexFromInt(l.getElement(2)), 4166 StringUtil.twoHexFromInt(l.getElement(3)), 4167 StringUtil.twoHexFromInt(l.getElement(4)), 4168 StringUtil.twoHexFromInt(l.getElement(5)), 4169 StringUtil.twoHexFromInt(l.getElement(6)), 4170 StringUtil.twoHexFromInt(l.getElement(7)), 4171 StringUtil.twoHexFromInt(l.getElement(8)), 4172 StringUtil.twoHexFromInt(l.getElement(9)), 4173 StringUtil.twoHexFromInt(l.getElement(10)) 4174 ); 4175 if (packetInt.length == 5) { 4176 log.debug(" NMRA packet: {} {} {} {} {}", 4177 StringUtil.twoHexFromInt(packetInt[0]), 4178 StringUtil.twoHexFromInt(packetInt[1]), 4179 StringUtil.twoHexFromInt(packetInt[2]), 4180 StringUtil.twoHexFromInt(packetInt[3]), 4181 StringUtil.twoHexFromInt(packetInt[4]) 4182 ); 4183 } else if (packetInt.length == 3) { 4184 log.debug(" NMRA packet: {} {} {}", 4185 StringUtil.twoHexFromInt(packetInt[0]), 4186 StringUtil.twoHexFromInt(packetInt[1]), 4187 StringUtil.twoHexFromInt(packetInt[2]) 4188 ); 4189 } else { 4190 log.warn(" Unknown Extended Accessory Packet length [{}])",packetInt.length); 4191 return ""; 4192 } 4193 if ((packetInt[2] & 0xF0) == 0xF0) { 4194 /* 4195 * 2.3.7.2 Configuration Variable Access Instruction - Short Form 4196 * This instruction has the format of: 4197 * {instruction bytes} = 1111GGGG 0 DDDDDDDD 0 DDDDDDDD 4198 * The 8-bit data DDDDDDDD is placed in the configuration 4199 * variable identified by GGGG according 4200 */ 4201 log.debug("Is an Short-form Extended Accessory Ops-mode CV access"); 4202 } 4203 if ((packetInt[2] & 0xf0) == 0xe0) { 4204 /* 4205 * 2.3.7.3 Configuration Variable Access Instruction - Long Form 4206 * The long form allows the direct manipulation of all CVs8. This 4207 * instruction is valid both when the Digital Decoder has its 4208 * long address active and short address active. Digital Decoders 4209 * shall not act on this instruction if sent to its consist 4210 * address. 4211 * 4212 * The format of the instructions using Direct CV 4213 * addressing is: 4214 * {instruction bytes}= 1110GGVV 0 VVVVVVVV 0 DDDDDDDD 4215 * 4216 * The actual Configuration Variable desired is selected 4217 * via the 10-bit address with the 2-bit address (VV) in 4218 * the first data byte being the most significant bits of 4219 * the address. The Configuration variable being addressed 4220 * is the provided 10-bit address plus 1. For example, to 4221 * address CV1 the 10 bit address is "00 00000000". 4222 * 4223 * The defined values for Instruction type (CC) are: 4224 * GG=00 Reserved for future use 4225 * GG=01 Verify byte 505 4226 * GG=11 Write byte 4227 * GG=10 Bit manipulation 4228 * */ 4229 int addr = getExtendedAccessoryAddressFromDCCPacket(packetInt); 4230 log.debug("Long-format Extended Accessory Ops-mode CV access: Extended Acceccory Address {}", addr); 4231 int cvnum = 1 + ((packetInt[2] & 0x03) << 8) + (packetInt[3] & 0xff); 4232 switch (packetInt[2] & 0x0C) { 4233 case 0x04: 4234 // GG=01 Verify byte 4235 /* 4236 * Type = "01" VERIFY BYTE 4237 * 4238 * The contents of the Configuration Variable as indicated 4239 * by the 10-bit address are compared with the data byte 4240 * (DDDDDDDD). If the decoder successfully receives this 4241 * packet and the values are identical, the Digital 4242 * Decoder shall respond with the contents of the CV as 4243 * the Decoder Response Transmission, if enabled. 4244 */ 4245 log.debug("CV # {}, Verify Byte: {}", cvnum, packetInt[4]); 4246 return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_VERIFY", 4247 addr, cvnum, packetInt[4] ); 4248 case 0x08: 4249 // GG=10 Bit manipulation 4250 /* 4251 * Type = "10" BIT MANIPULATION. 4252 * 4253 * The bit manipulation instructions use a special 4254 * format for the data byte (DDDDDDDD): 111FDBBB, where 4255 * BBB represents the bit position within the CV, 4256 * D contains the value of the bit to be verified 4257 * or written, and F describes whether the 4258 * operation is a verify bit or a write bit 4259 * operation. 4260 * 4261 * F = "1" : WRITE BIT 4262 * F = "0" : VERIFY BIT 4263 * The VERIFY BIT and WRITE BIT instructions operate 4264 * in a manner similar to the VERIFY BYTE and WRITE 4265 * BYTE instructions (but operates on a single bit). 4266 * Using the same criteria as the VERIFY BYTE 4267 * instruction, an operations mode acknowledgment 4268 * will be generated in response to a VERIFY BIT 4269 * instruction if appropriate. Using the same 4270 * criteria as the WRITE BYTE instruction, a 4271 * configuration variable access acknowledgment 4272 * will be generated in response to the second 4273 * identical WRITE BIT instruction if appropriate. 4274 */ 4275 if ((packetInt[4]& 0xE0) != 0xE0) { 4276 break; 4277 } 4278 log.debug("CV # {}, Bit Manipulation: {} {} (of bits 0-7) with {}", 4279 cvnum, (packetInt[4] & 0x10) == 0x10 ? "Write" : "Verify", 4280 (packetInt[4] & 0x7), 4281 (packetInt[4] >> 3) & 0x1); 4282 4283 // "Extended Accessory Decoder CV Bit {} bit, 4284 // Address {}, CV {}, bit # {} (of bits 0-7) 4285 // with value {}.\n" 4286 return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_BIT_ACCESS", 4287 ((packetInt[4] & 0x10) == 0x10 ? "Write" : "Verify"), 4288 addr, cvnum, (packetInt[4] & 0x7), 4289 ((packetInt[4] >>3) & 0x1) ); 4290 case 0x0c: 4291 // GG=11 Write byte 4292 /* 4293 * Type = "11" WRITE BYTE 4294 * 4295 * The contents of the Configuration Variable as indicated by the 10-bit 4296 * address are replaced by the data byte (DDDDDDDD). Two identical 4297 * packets are needed before the decoder shall modify a 4298 * configuration variable. These two packets need not be back 4299 * to back on the track. However any other packet to the same 4300 * decoder will invalidate the write operation. (This includes 4301 * broadcast packets.) If the decoder successfully receives 4302 * this second identical packet, it shall respond with a 4303 * configuration variable access acknowledgment. 4304 */ 4305 log.debug("CV # {}, Write Byte: {}", cvnum, packetInt[4]); 4306 return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_WRITE", 4307 addr, cvnum, packetInt[4] ); 4308 case 0x0: 4309 default: 4310 // GG=00 Reserved for future use 4311 log.debug("CV # {}, Reserved (GG=0); {}", cvnum, packetInt[4]); 4312 } 4313 } else if (packetInt.length == 3) { 4314 int addr = getExtendedAccessoryAddressFromDCCPacket(packetInt); 4315 return Bundle.getMessage("LN_MSG_EXTEND_ACCY_SET_ASPECT", 4316 addr, addr - 4, packetInt[2] ); 4317 } 4318 } 4319 return Bundle.getMessage("LN_MSG_OPC_IMM_PKT_GENERIC", 4320 ((reps & 0x70) >> 4), 4321 (reps & 0x07), 4322 reps, 4323 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4324 StringUtil.twoHexFromInt(dhi)), 4325 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4326 StringUtil.twoHexFromInt(im1)), 4327 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4328 StringUtil.twoHexFromInt(im2)), 4329 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4330 StringUtil.twoHexFromInt(im3)), 4331 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4332 StringUtil.twoHexFromInt(im4)), 4333 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4334 StringUtil.twoHexFromInt(im5)), 4335 NmraPacket.format(packet)); 4336 } 4337 } // else { // F9-F28 w/a short address. 4338 } else if (l.getElement(1) == 0x1F) { 4339 if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42 4340 && l.getElement(6) != 0x5E && l.getElement(10) == 0x70 && l.getElement(11) == 0x00 && l.getElement(15) == 0x10) { 4341 // Uhlenbrock IB-COM / Intellibox I and II read or write CV value on programming track 4342 String cv = Integer.toString(l.getElement(8) * 256 + ((l.getElement(5) & 0x02) * 64) + l.getElement(7)); 4343 int val = l.getElement(9) + 16 * (l.getElement(5) & 0x08); 4344 switch (l.getElement(6)) { 4345 case 0x6C: 4346 return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_REG_MODE_FROM_PT", cv); 4347 case 0x6D: 4348 return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_REG_MODE_FROM_PT", cv); 4349 case 0x6E: 4350 return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_PAGED_MODE_FROM_PT", cv); 4351 case 0x6F: 4352 return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_PAGED_MODE_FROM_PT", cv); 4353 case 0x71: 4354 return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_DIRECT_BYTE_MODE_FROM_PT", 4355 cv, val); 4356 case 0x70: // observed on Intellibox II, even though it does not work on IB-COM 4357 case 0x72: 4358 return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_DIRECT_BYTE_MODE_FROM_PT", cv); 4359 default: 4360 break; 4361 } 4362 return ""; 4363 } else if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42 4364 && l.getElement(6) == 0x5E) { 4365 // Uhlenbrock IB-COM / Intellibox I and II write CV value on main track 4366 int addr = l.getElement(8) * 256 + ((l.getElement(5) & 0x02) * 64) + l.getElement(7); 4367 String cv = Integer.toString(l.getElement(11) * 256 + ((l.getElement(5) & 0x08) << 4) + l.getElement(9)); 4368 int val = ((l.getElement(10) & 0x02) << 6) + l.getElement(12); 4369 return Bundle.getMessage("LN_MSG_UHLEN_CV_OPS_MODE_WRITE", 4370 addr, cv, val); 4371 } 4372 } 4373 return ""; // not an understood message. 4374 } 4375 4376 /* 4377 * Returns the Digitrax Extended Accessory Packet Address 4378 */ 4379 private static int getExtendedAccessoryAddressFromDCCPacket(int[] packetInt) { 4380 return ( 1 + ((packetInt[0] & 0x3F) << 2) + 4381 ((( ~ packetInt[1]) & 0x70) << 4) 4382 + ((packetInt[1] & 0x06) >> 1)); 4383 } 4384 4385 private static String interpretOpcPr3Mode(LocoNetMessage l) { 4386 /* 4387 * Sets the operating mode of the PR3 device, if present. 4388 * 4389 * Information reverse-engineered by B. Milhaupt and used with permission 4390 */ 4391 4392 if ((l.getElement(1) == 0x10) && ((l.getElement(2) & 0x7c) == 0) 4393 && (l.getElement(3) == 0) && (l.getElement(4) == 0)) { 4394 // set PR3 mode of operation, where LS 2 bits of byte 2 are encoded as: 4395 // 0x00 Set the PR3 mode to MS100 interface mode with PR3 LocoNet termination disabled 4396 // 0x01 Set the PR3 to decoder programming track mode 4397 // 0x03 Set the PR3 to MS100 interface mode with PR3 LocoNet termination enabled 4398 4399 switch (l.getElement(2) & 0x3) { 4400 case 0x00: { 4401 return Bundle.getMessage("LN_MSG_SET_PR3_MODE_LOCONET_IF_WITHOUT_TERM"); 4402 } 4403 case 0x02: { 4404 return Bundle.getMessage("LN_MSG_SET_PR3_MODE_PR3_PROGRAMMING_TRACK_ONLY"); 4405 } 4406 case 0x03: { 4407 return Bundle.getMessage("LN_MSG_SET_PR3_MODE_LN_MSG_SET_PR3_MODE_LOCONET_IF_WITH_TERM"); 4408 } 4409 default: { 4410 break; 4411 } 4412 } 4413 } 4414 return ""; 4415 } 4416 4417 private static String interpretIb2Special(LocoNetMessage l) { 4418 // Intellibox function control message for mobile decoder F0-F28 (IB-I) and F13-F28 (IB-II) 4419 if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) 4420 && ((l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN) 4421 || (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F13_F19_TOKEN) 4422 || (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F21_F27_TOKEN))) { 4423 // Intellibox-I function control message for mobile decoder F5 thru F27 except F12 and F20 4424 // Intellibox-II function control message for mobile decoder F13 thru F27 except F20 4425 // Note: Intellibox-II documentation implies capability to control 4426 // MANY more functions. This capability may be extended by 4427 // additional tokens in element 3, including the special-case encoding 4428 // for the "eighth bit" as handled in the following case, below, 4429 // for F12, F20 & F28 4430 int funcOffset = 5 + 8 * (l.getElement(3) - LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN); 4431 String encodingType; 4432 if (l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN) { 4433 encodingType = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_IB1"); 4434 } else { 4435 encodingType = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_IB2"); 4436 } 4437 String[] funcInfo = new String[7]; 4438 int mask = 1; 4439 for (int i = 0; i < 7; i++) { 4440 // handle 7 bits of data 4441 funcInfo[i] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", 4442 funcOffset + i, 4443 Bundle.getMessage(((l.getElement(4) & mask) != 0) 4444 ? "LN_MSG_FUNC_ON" 4445 : "LN_MSG_FUNC_OFF")); 4446 mask *= 2; 4447 } 4448 return Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL", 4449 encodingType, l.getElement(2), funcInfo[0], 4450 funcInfo[1], funcInfo[2], funcInfo[3], 4451 funcInfo[4], funcInfo[5], funcInfo[6]); 4452 } else if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) 4453 && (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F20_F28_TOKEN)) { 4454 // Special-case for F12, F20 and F28, since the tokens from the previous case 4455 // can only encode 7 bits of data in element(4). 4456 return Bundle.getMessage("LN_MSG_INTELLIBOX_SPECIAL_FUNC_CTL", 4457 l.getElement(2), 4458 Bundle.getMessage(((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F12_MASK) != 0) 4459 ? "LN_MSG_FUNC_ON" 4460 : "LN_MSG_FUNC_OFF"), 4461 Bundle.getMessage(((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F20_MASK) != 0) 4462 ? "LN_MSG_FUNC_ON" 4463 : "LN_MSG_FUNC_OFF"), 4464 Bundle.getMessage(((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F28_MASK) != 0) 4465 ? "LN_MSG_FUNC_ON" 4466 : "LN_MSG_FUNC_OFF")); 4467 } else if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) 4468 && (l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F0_F4_TOKEN)) { 4469 // For Intellibox-I "one" with SW version 2.x - Special-case for F0 to F4 4470 String[] funcInfo = new String[7]; 4471 funcInfo[0] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", 4472 0, 4473 (l.getElement(4) & LnConstants.RE_IB1_F0_MASK) == 0 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4474 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4475 int mask = 1; 4476 for (int i = 0; i < 4; i++) { 4477 // handle 7 bits of data 4478 funcInfo[i + 1] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", 4479 i + 1, 4480 Bundle.getMessage(((l.getElement(4) & mask) != 0) 4481 ? "LN_MSG_FUNC_ON" 4482 : "LN_MSG_FUNC_OFF")); 4483 mask *= 2; 4484 } 4485 return Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_F0_TO_F4", 4486 l.getElement(2), 4487 funcInfo[0], funcInfo[1], funcInfo[2], funcInfo[3], 4488 funcInfo[4]); 4489 } 4490 // Because the usage of other tokens in message element(3) are not yet 4491 // understood, let execution fall thru to the "default" case 4492 return ""; 4493 } 4494 4495 private static String interpretIb2F9_to_F12(LocoNetMessage l) { 4496 // Intellibox-II function control message for mobile decoder F9 thru F12. 4497 int slot = l.getElement(1); 4498 int funcs = l.getElement(2); 4499 return Bundle.getMessage("LN_MSG_INTELLIBOX_SLOT_SET_F9_TO_F12", 4500 slot, 4501 Bundle.getMessage(((funcs & LnConstants.RE_IB2_F9_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4502 Bundle.getMessage(((funcs & LnConstants.RE_IB2_F10_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4503 Bundle.getMessage(((funcs & LnConstants.RE_IB2_F11_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4504 Bundle.getMessage(((funcs & LnConstants.RE_IB2_F12_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4505 } 4506 4507 /** 4508 * Convert bytes from LocoNet packet into a locomotive address. 4509 * 4510 * @param a1 Byte containing the upper bits. 4511 * @param a2 Byte containing the lower bits. 4512 * @return a locomotive address in the range of 0-16383 4513 */ 4514 static private int LOCO_ADR(int a1, int a2) { 4515 return (((a1 & 0x7f) * 128) + (a2 & 0x7f)); 4516 } 4517 4518 /** 4519 * Convert bytes from LocoNet packet into a 1-based address for a sensor or 4520 * turnout. 4521 * 4522 * @param a1 Byte containing the upper bits 4523 * @param a2 Byte containing the lower bits 4524 * @return 1-4096 address 4525 */ 4526 static private int SENSOR_ADR(int a1, int a2) { 4527 return (((a2 & 0x0f) * 128) + (a1 & 0x7f)) + 1; 4528 } 4529 4530 /* 4531 * Take an int and convert it to a dotted version number 4532 * as used by the LocoIO protocol. 4533 * Example: 123 => 1.2.3 4534 */ 4535 /** 4536 * Take the LocoIO version number and convert to human friendly format, like 4537 * "1.4.8" or "9.1". 4538 * 4539 * @param val The LocoIO version. 4540 * @return String with human readable format 4541 */ 4542 public static String dotme(int val) { 4543 if ((val >= 0) && (val < 10)) { 4544 return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_ONE_DIGIT", val); 4545 } else if ((val >= 10) && (val < 100)) { 4546 return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_TWO_DIGITS", val / 10, val % 10); 4547 } else if ((val >= 100) && (val < 1000)) { 4548 int hundreds = val / 100; 4549 int tens = (val - (hundreds * 100)) / 10; 4550 int ones = val % 10; 4551 return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_THREE_DIGITS", hundreds, tens, ones); 4552 } 4553 return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_OUT_OF_RANGE", val); 4554 } 4555 4556 /** 4557 * Convert throttle ID to a human friendly format. 4558 * 4559 * @param id1 Byte #1 of the ID 4560 * @param id2 Byte #2 of the ID 4561 * @return String with human friendly format, without the influence of 4562 * Locale 4563 */ 4564 private static String idString(int id1, int id2) { 4565 /* the decimalIdValueWithoutLocale_SpecificFormatting variable 4566 is used to generate a string representation of the ID value 4567 without any local-specific formatting. In other words, in a 4568 us_EN locale, we want "14385", not "14,385". 4569 */ 4570 String decimalIdValueWithoutLocale_SpecificFormatting 4571 = Integer.toString(((id2 & 0x7F) * 128 + (id1 & 0x7F))); 4572 4573 return Bundle.getMessage("LN_MSG_THROTTLE_ID", 4574 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4575 StringUtil.twoHexFromInt(id2 & 0x7F)), 4576 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4577 StringUtil.twoHexFromInt(id1 & 0x7F)), 4578 decimalIdValueWithoutLocale_SpecificFormatting); 4579 } 4580 4581 /** 4582 * Create a string representation of the loco address in 4583 * addressLow and addressHigh in a form appropriate for the type of address (2 4584 * or 4 digit) using the Digitrax 'mixed mode' if necessary. 4585 * <p> 4586 * "Mixed mode" is used by DT100 and DT200 throttles to display loco 4587 * addresses between 100 and 127 as a two-digit displayable value, where the 4588 * left digit is either 'a', 'b', or 'c', (for addresses in the 10x, 11x, 4589 * and 12x ranges, respectively), and the right digit is the "x" from the 4590 * ranges above. 4591 * 4592 * @param addressLow the least-significant 7 bits of the loco address 4593 * @param addressHigh the most-significant 7 bits of the loco address 4594 * @return a String containing the address, using Digitrax 'mixed mode' 4595 * representation of the loco address, if appropriate 4596 */ 4597 public static String convertToMixed(int addressLow, int addressHigh) { 4598 // if we have a 2 digit decoder address, proceed accordingly 4599 switch (addressHigh) { 4600 case 0x7d: 4601 log.debug("addressLow / 10 = {}", addressLow / 10); 4602 switch (addressLow) { 4603 case 100: case 101: case 102: case 103: case 104: case 105: 4604 case 106: case 107: case 108: case 109: 4605 // N (short, alternately 'An') (or long address NN) 4606 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Ax", 4607 addressLow, 4608 addressLow-100, 4609 String.valueOf(LOCO_ADR(addressHigh, addressLow))); 4610 // Note: .toString intentionally used here to remove the "internationalized" 4611 // presentation of integers, which, in US English, adds a "," between 4612 // the thousands digit and the hundreds digit. This comma is undesired 4613 // in this application. 4614 case 110: case 111: case 112: case 113: case 114: case 115: 4615 case 116: case 117: case 118: case 119: 4616 // N (short, alternately 'Bn') (or long address NN) 4617 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Bx", 4618 addressLow, 4619 addressLow-110, 4620 String.valueOf(LOCO_ADR(addressHigh, addressLow))); 4621 // Note: .toString intentionally used here to remove the "internationalized" 4622 // presentation of integers, which, in US English, adds a "," between 4623 // the thousands digit and the hundreds digit. This comma is undesired 4624 // in this application. 4625 case 120: case 121: case 122: case 123: case 124: case 125: 4626 case 126: case 127: 4627 // N (short, alternately 'Cn') (or long address NN) 4628 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Cx", 4629 addressLow, 4630 addressLow-120, 4631 String.valueOf(LOCO_ADR(addressHigh, addressLow))); 4632 // Note: .toString intentionally used here to remove the "internationalized" 4633 // presentation of integers, which, in US English, adds a "," between 4634 // the thousands digit and the hundreds digit. This comma is undesired 4635 // in this application. 4636 default: 4637 // N (short) (or long address NN) 4638 return Bundle.getMessage("LN_MSG_HELPER_IS_SHORT_AND_LONG_ADDRESS", 4639 addressLow, 4640 String.valueOf(LOCO_ADR(addressHigh, addressLow))); 4641 // Note: .toString intentionally used here to remove the "internationalized" 4642 // presentation of integers, which, in US English, adds a "," between 4643 // the thousands digit and the hundreds digit. This comma is undesired 4644 // in this application. 4645 } 4646 4647 case 0x00: 4648 case 0x7f: 4649 switch (addressLow) { 4650 case 100: case 101: case 102: case 103: case 104: case 105: 4651 case 106: case 107: case 108: case 109: 4652 // N (short, alternately 'An') 4653 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Ax", 4654 addressLow, 4655 addressLow-100); 4656 case 110: case 111: case 112: case 113: case 114: case 115: 4657 case 116: case 117: case 118: case 119: 4658 // N (short, alternately 'Bn') 4659 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Bx", 4660 addressLow, 4661 addressLow-110); 4662 case 120: case 121: case 122: case 123: case 124: case 125: 4663 case 126: case 127: 4664 // N (short, alternately 'Cn') 4665 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Cx", 4666 addressLow, 4667 addressLow-120); 4668 default: 4669 // N (short) 4670 return Bundle.getMessage("LN_MSG_HELPER_IS_SHORT_ADDRESS", 4671 addressLow); 4672 } 4673 default: 4674 // return the full 4 digit address 4675 return String.valueOf(LOCO_ADR(addressHigh, addressLow)); 4676 // Note: .toString intentionally used here to remove the "internationalized" 4677 // presentation of integers, which, in US English, adds a "," between 4678 // the thousands digit and the hundreds digit. This comma is undesired 4679 // in this application. 4680 } 4681 } 4682 4683 private static String trackStatusByteToString(int trackStatusByte) { 4684 return Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STAT", 4685 (((trackStatusByte & LnConstants.GTRK_MLOK1) != 0) 4686 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_LN1_1") 4687 : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_DT200")), 4688 (((trackStatusByte & LnConstants.GTRK_POWER) != 0) 4689 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_ON") 4690 : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_OFF")), 4691 (((trackStatusByte & LnConstants.GTRK_IDLE) != 0) 4692 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_RUNNING") 4693 : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_PAUSED")), 4694 (((trackStatusByte & LnConstants.GTRK_PROG_BUSY) != 0) 4695 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_PRG_BUSY") 4696 : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_PRG_AVAILABLE")) 4697 ); 4698 } 4699 4700 /** 4701 * Return a string which is formatted by a bundle Resource Name. 4702 * 4703 * @param hour fast-clock hour 4704 * @param minute fast-clock minute 4705 * @return a formatted string containing the time 4706 */ 4707 private static String fcTimeToString(int hour, int minute) { 4708 return Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_TIME", 4709 LocalTime.of(hour, minute).toString()); 4710 } 4711 4712 protected static String[] interpretF0_F4toStrings(int dirf) { 4713 String[] s = new String[5]; 4714 4715 s[0] = (((dirf & LnConstants.DIRF_F0) == LnConstants.DIRF_F0) 4716 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4717 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4718 s[1] = (((dirf & LnConstants.DIRF_F1) == LnConstants.DIRF_F1) 4719 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4720 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4721 s[2] = (((dirf & LnConstants.DIRF_F2) == LnConstants.DIRF_F2) 4722 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4723 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4724 s[3] = (((dirf & LnConstants.DIRF_F3) == LnConstants.DIRF_F3) 4725 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4726 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4727 s[4] = (((dirf & LnConstants.DIRF_F4) == LnConstants.DIRF_F4) 4728 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4729 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4730 return s; 4731 } 4732 4733 protected static String directionOfTravelString(boolean isForward) { 4734 return Bundle.getMessage(isForward ? "LN_MSG_DIRECTION_FWD" 4735 : "LN_MSG_DIRECTION_REV"); 4736 } 4737 4738 protected static String[] interpretF5_F8toStrings(int snd) { 4739 String[] s = new String[4]; 4740 4741 s[0] = (((snd & LnConstants.SND_F5) == LnConstants.SND_F5) 4742 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4743 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4744 4745 s[1] = (((snd & LnConstants.SND_F6) == LnConstants.SND_F6) 4746 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4747 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4748 4749 s[2] = (((snd & LnConstants.SND_F7) == LnConstants.SND_F7) 4750 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4751 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4752 4753 s[3] = (((snd & LnConstants.SND_F8) == LnConstants.SND_F8) 4754 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4755 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4756 4757 return s; 4758 } 4759 4760 private static String figureAddressIncludingAliasing(int adr, int adr2, int ss2, int id1, int id2) { 4761 4762 /* 4763 * Build loco address string. String will be a simple 4764 * number, unless the address is between 100 and 127 4765 * (inclusive), where a Digitrax "mixed mode" version 4766 * of the address will be appended. 4767 */ 4768 String mixedAdrStr = convertToMixed(adr, adr2); 4769 4770 /* 4771 * If the address is a command station "alias" condition, 4772 * then note it in the string. 4773 */ 4774 if (adr2 == 0x7f) { 4775 if ((ss2 & LnConstants.STAT2_ALIAS_MASK) == LnConstants.STAT2_ID_IS_ALIAS) { 4776 /* this is an aliased address and we have the alias */ 4777 return Bundle.getMessage("LN_MSG_LOCO_ADDR_HELPER_ALIAS_2_DIGIT_WITH_KNOWN_4_DIGIT", 4778 Integer.toString(LOCO_ADR(id2, id1)), mixedAdrStr); 4779 } else { 4780 /* this is an aliased address and we don't have the alias */ 4781 return Bundle.getMessage("LN_MSG_LOCO_ADDR_HELPER_ALIAS_2_DIGIT_WITH_UNKNOWN_4_DIGIT", 4782 mixedAdrStr); 4783 } 4784 } else { 4785 /* a regular address which is not an alias */ 4786 return mixedAdrStr; 4787 } 4788 } 4789 4790 public static String getDeviceNameFromIPLInfo(int manuf, int type) { 4791 if (manuf != LnConstants.RE_IPL_MFR_DIGITRAX) { 4792 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_UNDEFINED_MFG_PROD", 4793 manuf, type); 4794 } 4795 switch (type) { 4796 case LnConstants.RE_IPL_DIGITRAX_HOST_ALL: 4797 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_ALLDEVICES"); 4798 case LnConstants.RE_IPL_DIGITRAX_HOST_LNRP: 4799 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_LNRP"); 4800 case LnConstants.RE_IPL_DIGITRAX_HOST_UT4: 4801 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UT4"); 4802 case LnConstants.RE_IPL_DIGITRAX_HOST_UT6: 4803 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UT6"); 4804 case LnConstants.RE_IPL_DIGITRAX_HOST_WTL12: 4805 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_WTL12"); 4806 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS210: 4807 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS210"); 4808 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS210PLUS: 4809 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS210PLUS"); 4810 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240: 4811 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS240"); 4812 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240PLUS: 4813 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS240PLUS"); 4814 case LnConstants.RE_IPL_DIGITRAX_HOST_PR3: 4815 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PR3"); 4816 case LnConstants.RE_IPL_DIGITRAX_HOST_DT402: 4817 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT402"); 4818 case LnConstants.RE_IPL_DIGITRAX_HOST_DT500: 4819 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT500"); 4820 case LnConstants.RE_IPL_DIGITRAX_HOST_DT602: 4821 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT602"); 4822 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS51: 4823 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS51"); 4824 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS52: 4825 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS52"); 4826 case LnConstants.RE_IPL_DIGITRAX_HOST_UR92: 4827 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UR92"); 4828 case LnConstants.RE_IPL_DIGITRAX_HOST_UR93: 4829 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UR93"); 4830 case LnConstants.RE_IPL_DIGITRAX_HOST_PR4: 4831 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PR4"); 4832 case LnConstants.RE_IPL_DIGITRAX_HOST_LNWI: 4833 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_LNWI"); 4834 case LnConstants.RE_IPL_DIGITRAX_HOST_BXP88: 4835 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_BXP88"); 4836 case LnConstants.RE_IPL_DIGITRAX_HOST_BXPA1: 4837 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_BXPA1"); 4838 case LnConstants.RE_IPL_DIGITRAX_HOST_DS74: 4839 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DS74"); 4840 case LnConstants.RE_IPL_DIGITRAX_HOST_DS78V: 4841 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DS78V"); 4842 case LnConstants.RE_IPL_DIGITRAX_HOST_DB210: 4843 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB210"); 4844 case LnConstants.RE_IPL_DIGITRAX_HOST_DB210OPTO: 4845 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB210OPTO"); 4846 case LnConstants.RE_IPL_DIGITRAX_HOST_DB220: 4847 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB220"); 4848 case LnConstants.RE_IPL_DIGITRAX_HOST_PM74: 4849 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PM74"); 4850 case LnConstants.RE_IPL_DIGITRAX_HOST_SE74: 4851 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_SE74"); 4852 case LnConstants.RE_IPL_DIGITRAX_HOST_BDL716: 4853 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_BDL716"); 4854 4855 default: 4856 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UNKNOWN", type); 4857 } 4858 } 4859 4860 public static String getSlaveNameFromIPLInfo(int manuf, int slaveNum) { 4861 if (manuf != LnConstants.RE_IPL_MFR_DIGITRAX) { 4862 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_UNDEFINED_MFG_PROD", 4863 manuf, slaveNum); 4864 } 4865 switch (slaveNum) { 4866 case LnConstants.RE_IPL_DIGITRAX_SLAVE_ALL: 4867 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_ALLDEVICES"); 4868 case LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24: 4869 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_RF24"); 4870 default: 4871 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_UNKNOWN", slaveNum); 4872 } 4873 } 4874 4875 /** 4876 * Interpret messages with Opcode of OPC_ALM_READ, OPC_ALM_WRITE. 4877 * 4878 * @param l LocoNet Message to interpret 4879 * @return String containing interpreted message or empty string if 4880 * message is not interpretable. 4881 */ 4882 public static String interpretAlm(LocoNetMessage l) { 4883 if (l.getElement(1) == 0x10) { 4884 String ret; 4885 ret = jmri.jmrix.loconet.alm.almi.Almi.interpretAlm(l); 4886 if (ret.length() > 1) { 4887 return ret; 4888 } 4889 } 4890 4891 if (l.getElement(1) == 0x15) { 4892 int slot = ( (l.getElement(2) & 0x07 ) *128) + l.getElement(3); // slot number for this request 4893 4894 String result = interpretExtendedSlotRdWr(l, slot) ; 4895 if (!result.isEmpty()) { 4896 return result; 4897 } 4898 } 4899 return ""; 4900 4901 } 4902 private static String interpretOpcExpMoveSlots(LocoNetMessage l) { 4903 int src = ((l.getElement(1) & 0x03) * 128) + (l.getElement(2) & 0x7f); 4904 int dest = ((l.getElement(3) & 0x03) * 128) + (l.getElement(4) & 0x7f); 4905 4906 if ((src >= 0x79) && (src <= 0x7f)) { 4907 return ""; 4908 } 4909 if ((dest >= 0x79) && (dest <= 0x7f)) { 4910 return ""; 4911 } 4912 4913 if ((l.getElement(1) & 0x78) != 0x38) { 4914 // Ignore if message is not one from a DCS240 (or newer) command 4915 // station's "Expanded" slot messaging. The message is probably 4916 // from an Intellibox or other non-Digitrax command station. 4917 return ""; 4918 } 4919 4920 boolean isSettingStatus = ((l.getElement(3) & 0b01110000) == 0b01100000); 4921 if (isSettingStatus) { 4922 int stat = l.getElement(4); 4923 return Bundle.getMessage("LN_MSG_OPC_EXP_SET_STATUS", 4924 src, 4925 LnConstants.CONSIST_STAT(stat), 4926 LnConstants.LOCO_STAT(stat), 4927 LnConstants.DEC_MODE(stat)); 4928 } 4929 boolean isUnconsisting = ((l.getElement(3) & 0b01110000) == 0b01010000); 4930 if (isUnconsisting) { 4931 // source and dest same, returns slot contents 4932 return Bundle.getMessage("LN_MSG_OPC_EXP_UNCONSISTING", 4933 src); 4934 } 4935 boolean isConsisting = ((l.getElement(3) & 0b01110000) == 0b01000000); 4936 if (isConsisting) { 4937 //add dest to src, returns dest slot contents 4938 return Bundle.getMessage("LN_MSG_OPC_EXP_CONSISTING", 4939 src,dest); 4940 } 4941 /* check special cases */ 4942 if (src == 0) { 4943 /* DISPATCH GET */ 4944 return Bundle.getMessage("LN_MSG_MOVE_SL_GET_DISP"); 4945 } else if (src == dest) { 4946 /* IN USE */ 4947 return Bundle.getMessage("LN_MSG_MOVE_SL_NULL_MOVE", src); 4948 } else if (dest == 0) { 4949 /* DISPATCH PUT */ 4950 return Bundle.getMessage("LN_MSG_MOVE_SL_DISPATCH_PUT", src); 4951 } else { 4952 /* general move */ 4953 return Bundle.getMessage("LN_MSG_MOVE_SL_MOVE", src, dest); 4954 } 4955 } 4956 4957 private static String interpretPocExpLocoSpdDirFunction(LocoNetMessage l) { 4958 int slot = ((l.getElement(1) & 0x03) * 128) + (l.getElement(2) & 0x7f); 4959 if ((l.getElement(1) & LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_SPEED) == LnConstants.OPC_EXP_SEND_SPEED_AND_DIR_FWD) { 4960 // speed and direction 4961 int spd = l.getElement(4); 4962 String direction = Bundle.getMessage((l.getElement(1) & LnConstants.OPC_EXP_SEND_SPEED_AND_DIR_REV) != 0 4963 ? "LN_MSG_DIRECTION_REV" : "LN_MSG_DIRECTION_FWD"); 4964 String throttleID = Integer.toHexString(l.getElement(3)); 4965 return Bundle.getMessage("LN_MSG_OPC_EXP_SPEED_DIRECTION", slot, spd, direction, throttleID); 4966 } 4967 // Build a string for the functions on off 4968 String[] fn = new String[8]; 4969 for (int bitIndex = 0; bitIndex < 8; bitIndex++) { 4970 fn[bitIndex] = (l.getElement(4) >> (7 - bitIndex) & 1) == 1 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4971 : Bundle.getMessage("LN_MSG_FUNC_OFF"); 4972 } 4973 if ((l.getElement(1) & 4974 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F0F6) { 4975 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F0_F6", slot, fn[3], fn[7], fn[6], fn[5], fn[4], fn[2], 4976 fn[1]); 4977 } else if ((l.getElement(1) & 4978 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F7F13) { 4979 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F7_F13", slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], 4980 fn[1]); 4981 } else if ((l.getElement(1) & 4982 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F14F20) { 4983 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F14_F20",slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], 4984 fn[1]); 4985 } else if ((l.getElement(1) & 4986 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F21F28_F28OFF) { 4987 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F21_F28",slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], 4988 fn[1], Bundle.getMessage("LN_MSG_FUNC_OFF")); 4989 } else if ((l.getElement(1) & 4990 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F21F28_F28ON) { 4991 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F21_F28", slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], 4992 fn[1], Bundle.getMessage("LN_MSG_FUNC_ON")); 4993 } 4994 return ""; 4995 } 4996 4997 private static String interpretExtendedSlotRdWr(LocoNetMessage l, int slot) { 4998 /* 4999 * ************************************************ 5000 * extended slot read/write message * 5001 * ************************************************ 5002 */ 5003 /* 5004 * If it's a "Special" slot (Stats etc) use a different routine 5005 */ 5006 if (slot > 247 && slot < 253) { 5007 return interpretExtendedSlot_StatusData(l,slot); 5008 } 5009 int trackStatus = l.getElement(7); // track status 5010 int id1 = l.getElement(19); 5011 int id2 = l.getElement(18); 5012 int command = l.getOpCode(); 5013 int stat = l.getElement(4); // slot status 5014 //int adr = l.getElement(5) + 128 * l.getElement(6); // loco address 5015 int adr = l.getElement(5); 5016 int spd = l.getElement(8); // command speed 5017 int dirf = l.getElement(10) & 0b00111111; // direction and F0-F4 bits 5018 String[] dirf0_4 = interpretF0_F4toStrings(dirf); 5019 int ss2 = l.getElement(18); // slot status 2 (tells how to use 5020 // ID1/ID2 & ADV Consist) 5021 int adr2 = l.getElement(6); // loco address high 5022 int snd = l.getElement(10); // Sound 1-4 / F5-F8 5023 String[] sndf5_8 = interpretF5_F8toStrings(snd); 5024 5025 String locoAdrStr = figureAddressIncludingAliasing(adr, adr2, ss2, id1, id2); 5026 return Bundle.getMessage(((command == 0xEE) 5027 ? "LN_MSG_SLOT_LOCO_INFO_WRITE" 5028 : "LN_MSG_SLOT_LOCO_INFO_READ"), 5029 slot, 5030 locoAdrStr, 5031 LnConstants.CONSIST_STAT(stat), 5032 LnConstants.LOCO_STAT(stat), 5033 LnConstants.DEC_MODE(stat), 5034 directionOfTravelString((dirf & LnConstants.DIRF_DIR) == 0), 5035 spd, // needs re-interpretation for some cases of slot consisting state 5036 dirf0_4[0], 5037 dirf0_4[1], 5038 dirf0_4[2], 5039 dirf0_4[3], 5040 dirf0_4[4], 5041 sndf5_8[0], 5042 sndf5_8[1], 5043 sndf5_8[2], 5044 sndf5_8[3], 5045 trackStatusByteToString(trackStatus), 5046 Bundle.getMessage("LN_MSG_SLOT_HELPER_SS2_SIMPLE", 5047 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 5048 StringUtil.twoHexFromInt(ss2))), 5049 Bundle.getMessage("LN_MSG_SLOT_HELPER_ID1_ID2_AS_THROTTLE_ID", 5050 idString(id1, id2))); 5051 } 5052 5053 /** 5054 * Interprets an Enhanced Slot Report message in the "Query Mode" range of 5055 * slot numbers. 5056 * <p> 5057 * Only the primary slot numbers are interpreted, not any "aliases". 5058 * 5059 * @param l Enhanced Slot report LocoNetMessage to be interpreted 5060 * @param slot Slot number 5061 * @return String showing interpretation. 5062 */ 5063 private static String interpretExtendedSlot_StatusData(LocoNetMessage l, int slot) { 5064 String baseInfo; 5065 String detailInfo = ""; 5066 switch (slot) { 5067 case 248: 5068 5069 baseInfo = interpretExtendedSlot_StatusData_Base_Detail(l, slot); // Basic Identifying information 5070 detailInfo = interpretExtendedSlot_Query_Mode_248(l); // Flags 5071 5072 break; 5073 case 249: 5074 baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information 5075 detailInfo = interpretExtendedSlot_Query_Mode_249(l); // Electrical properties 5076 5077 break; 5078 case 250: 5079 baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information 5080 detailInfo = interpretExtendedSlot_Query_Mode_250(l); // Slots info 5081 break; 5082 case 251: 5083 baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information 5084 detailInfo = interpretExtendedSlot_Query_Mode_251(l); // LocoNet events and messages and stats 5085 break; 5086 case 252: 5087 baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information 5088 detailInfo = interpretExtendedSlot_Query_Mode_252(l); // DCC track status info 5089 break; 5090 default: 5091 baseInfo = "Wrong Slot # ("+ slot +")"; 5092 } 5093 return Bundle.getMessage("LN_MSG_OPC_EXP_QUERY_MODE_OVERALL", 5094 slot, baseInfo, detailInfo); 5095 } 5096 5097 /** 5098 * Interpret the base information in bytes 16,18,19 5099 * for slots 249,250,251, but not 248. 5100 * 5101 * @param l LocoNetMessage to be interpreted 5102 * @param slot slot number 5103 * @return formatted message 5104 */ 5105 private static String interpretExtendedSlot_StatusData_Base(LocoNetMessage l, int slot) { 5106 String hwType = LnConstants.IPL_NAME(l.getElement(16)); 5107 int hwSerial = ((l.getElement(19) & 0x3f) * 128 ) + l.getElement(18); 5108 String serNumHex = "0000"+Integer.toHexString(hwSerial).toUpperCase(); 5109 serNumHex = serNumHex.substring(serNumHex.length()-4); 5110 5111 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_BASE", 5112 hwType, 5113 hwSerial + "(0x" + serNumHex + ")"); 5114 } 5115 5116 /** 5117 * Interpret slot 248 base details. 5118 * 5119 * @param l LocoNetMessage to be interpreted 5120 * @param slot slot number 5121 * @return formatted message 5122 */ 5123 private static String interpretExtendedSlot_StatusData_Base_Detail(LocoNetMessage l, int slot) { 5124 String hwType = LnConstants.IPL_NAME(l.getElement(14)); 5125 if ((l.getElement(19) & 0x40) == 0x40) { 5126 hwType = hwType + Bundle.getMessage("LN_MSG_COMMAND_STATION"); 5127 } 5128 int hwSerial = ((l.getElement(19) & 0x3f) * 128 ) + l.getElement(18); 5129 String serNumHex = "0000"+Integer.toHexString(hwSerial).toUpperCase(); 5130 serNumHex = serNumHex.substring(serNumHex.length()-4); 5131 5132 float hwVersion = ((float)(l.getElement(17) & 0x78) / 8 ) + ((float)(l.getElement(17) & 0x07) / 10 ) ; 5133 float swVersion = ((float)(l.getElement(16) & 0x78) / 8 ) + ((float)(l.getElement(16) & 0x07) / 10 ) ; 5134 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_BASEDETAIL", 5135 hwType, 5136 hwSerial + "(0x" + serNumHex + ")", 5137 hwVersion, swVersion); 5138 } 5139 5140 private static String queryOnOff(int val, int bit) { 5141 return (((val & 1 << bit) == 1 << bit)?Ln_On:Ln_Off); 5142 } 5143 5144 /** 5145 * Interprets _some_ of the data in Query Mode report of slot 248 (and aliases!) 5146 * - "Flags" info. 5147 * 5148 * @param l LocoNetMessage to be interpreted 5149 * @return formatted message 5150 */ 5151 private static String interpretExtendedSlot_Query_Mode_248(LocoNetMessage l) { 5152 5153 int b = l.getElement(4); 5154 String lnetVmin = Bundle.getMessage("LNET_QUERY_LNETVMIN", queryOnOff(b, 6)); 5155 String overTemp = Bundle.getMessage("LNET_QUERY_OVERTEMP", queryOnOff(b, 5)); 5156 String fuseBad = Bundle.getMessage("LNET_QUERY_FUSEBAD", queryOnOff(b, 4)); 5157 String rsynMax = Bundle.getMessage("LNET_QUERY_RSYNMAX", queryOnOff(b, 3)); 5158 String vinHi = Bundle.getMessage("LNET_QUERY_VINHI", queryOnOff(b, 2)); 5159 String vinLo = Bundle.getMessage("LNET_QUERY_VINLO", queryOnOff(b, 1)); 5160 String iTrk = Bundle.getMessage("LNET_QUERY_ITRK", queryOnOff(b, 0)); 5161 5162 b = l.getElement(5); 5163 String usbLink = Bundle.getMessage("LNET_QUERY_ULINK", queryOnOff(b, 5)); 5164 String iLim = Bundle.getMessage("LNET_QUERY_ILIM", queryOnOff(b, 3)); 5165 String PTrkMaxI = Bundle.getMessage("LNET_QUERY_PTRKMAXI", queryOnOff(b, 2)); 5166 String PtrkIsol = Bundle.getMessage("LNET_QUERY_PTRKISOL", queryOnOff(b, 1)); 5167 5168 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_FLAGS", 5169 rsynMax, usbLink, iTrk, vinLo, vinHi, fuseBad, 5170 overTemp, lnetVmin, PtrkIsol, PTrkMaxI, iLim); 5171 } 5172 5173 /** 5174 * Interprets _some_ of the data in Query Mode report of slot 249 (and aliases!) 5175 * - "Electrical" info. 5176 * 5177 * @param l LocoNetMessage to be interpreted 5178 * @return formatted message 5179 */ 5180 private static String interpretExtendedSlot_Query_Mode_249(LocoNetMessage l) { 5181 float voltsTrack = ((float)l.getElement(4)) * 2 / 10 ; 5182 float voltsIn = ((float)l.getElement(5)) * 2 / 10; 5183 float ampsIn = ((float)l.getElement(6)) / 10; 5184 float ampsLimit = ((float)l.getElement(7)) / 10; 5185 float voltsRsLoaded = ((float)l.getElement(12)) * 2 / 10; 5186 float voltsRsUnLoaded = ((float)l.getElement(10)) * 2 / 10; 5187 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_ELECTRIC", 5188 voltsTrack, 5189 voltsIn, 5190 ampsIn, 5191 ampsLimit, 5192 voltsRsLoaded, 5193 voltsRsUnLoaded); 5194 } 5195 5196 /** 5197 * Interprets _some_ of the data in Query Mode report of slot 250 (and aliases!) 5198 * - "Slots" info. 5199 * 5200 * @param l LocoNetMessage to be interpreted 5201 * @return formatted message 5202 */ 5203 private static String interpretExtendedSlot_Query_Mode_250(LocoNetMessage l) { 5204 int msgInUse = (l.getElement(4) + ( l.getElement(5) * 128)) ; 5205 int msgIdle = (l.getElement(6) + ( l.getElement(7) * 128)) ; 5206 int msgFree = (l.getElement(8) + ( l.getElement(9) * 128)) ; 5207 int ctop = (l.getElement(10) + ( l.getElement(11) * 128)) ; 5208 int cup = (l.getElement(12) + ( l.getElement(13) * 128)) ; 5209 5210 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_SLOTS", 5211 msgInUse, msgIdle, msgFree, ctop, cup); 5212 } 5213 5214 /** 5215 * Interprets _some_ of the data in Query Mode report of slot 251 (and aliases!) 5216 * - "LocoNet message" info. 5217 * 5218 * @param l LocoNetMessage to be interpreted 5219 * @return formatted message 5220 */ 5221 private static String interpretExtendedSlot_Query_Mode_251(LocoNetMessage l) { 5222 int msgTotal = (l.getElement(4) + ( l.getElement(5) * 128)) ; 5223 int msgErrors = (l.getElement(6) + ( l.getElement(7) * 128)) ; 5224 int sleeps = (l.getElement(10) + ( l.getElement(11) * 128)); 5225 5226 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_LOCONET", 5227 msgTotal, msgErrors, sleeps); 5228 } 5229 5230 /** 5231 * Interprets _some_ of the data in Query Mode report of slot 252 (and aliases!) 5232 * - "DCC status" info. 5233 * 5234 * @param l LocoNetMessage to be interpreted 5235 * @return formatted message 5236 */ 5237 private static String interpretExtendedSlot_Query_Mode_252(LocoNetMessage l) { 5238 int flt = (l.getElement(4) & 0x7f) + ((l.getElement(5) & 0x7f) << 7); 5239 int arv = (l.getElement(6) & 0x7f) + ((l.getElement(7) & 0x7f) << 7); 5240 int dst = (l.getElement(8) & 0x7f) + ((l.getElement(9) & 0x7f) << 7); 5241 return Bundle.getMessage("LN_MSG_OPC_EXP_QUERY_LOCONET_STAT2_LOCONET", 5242 flt, arv, dst); 5243 } 5244 5245 private static final String[] ds54sensors = {"AuxA", "SwiA", "AuxB", "SwiB", "AuxC", "SwiC", "AuxD", "SwiD"}; // NOI18N 5246 private static final String[] ds64sensors = {"A1", "S1", "A2", "S2", "A3", "S3", "A4", "S4"}; // NOI18N 5247 private static final String[] se8csensors = {"DS01", "DS02", "DS03", "DS04", "DS05", "DS06", "DS07", "DS08"}; // NOI18N 5248 5249 private final static Logger log = LoggerFactory.getLogger(LocoNetMessageInterpret.class); 5250}