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) & 1) << 7) + l.getElement(5)); 1137 String slave = getSlaveNameFromIPLInfo( l.getElement(6) + ((l.getElement(4) & 2) << 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) & 1) << 7) + 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(6) + ((l.getElement(4) & 2) << 6)); 1341 String slaveInfo; 1342 1343 if ((l.getElement(6) != 0) && (l.getElement(5) != l.getElement(6))) { 1344 String slaveVer = (((l.getElement(10) & 0x78) >> 3) + ((l.getElement(9) & 1) << 4)) + "." + ((l.getElement(10) & 0x7)); 1345 int slaveSnInt 1346 = ((l.getElement(15) + (((l.getElement(14) & 0x1) == 1) ? 128 : 0))) 1347 + ((l.getElement(16) + (((l.getElement(14) & 0x2) == 2) ? 128 : 0)) * 256) 1348 + ((l.getElement(17) + (((l.getElement(14) & 0x4) == 4) ? 128 : 0)) * 256 * 256) 1349 + ((l.getElement(18) + (((l.getElement(14) & 0x8) == 8) ? 128 : 0)) * 256 * 256 * 256); 1350 slaveInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_SLAVE_DETAILS", slaveType, 1351 Integer.toHexString(slaveSnInt).toUpperCase(), 1352 slaveVer); 1353 } else if ((l.getElement(6) != 0) && (l.getElement(5) == l.getElement(6))) { 1354 int iplVer =l.getElement(10) + ((l.getElement(9) & 1)<<7); 1355 slaveInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_IPL", 1356 Integer.toString(iplVer>>3) + "." + Integer.toString(iplVer & 7)); 1357 } else { 1358 slaveInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_SLAVE_NO_SLAVE"); 1359 } 1360 return Bundle.getMessage("LN_MSG_IPL_DEVICE_IDENTITY_REPORT", 1361 hostInfo, 1362 slaveInfo); 1363 } 1364 1365 private static String interpretOpcPeerXfer16(LocoNetMessage l) { 1366 /* 1367 * SRC=7F is THROTTLE msg xfer 1368 * ; <DSTL><DSTH> encode ID#, 1369 * ; <0><0> is THROT B'CAST 1370 * ; <PXCT1>=<0,XC2,XC1,XC0 - D4.7,D3.7,D2.7,D1.7> 1371 * ; XC0-XC2=ADR type CODE-0=7 bit Peer 1372 * TO Peer adrs * 1373 * ; 1=<D1>is SRC HI,<D2>is DST HI 1374 * ; <PXCT2>=<0,XC5,XC4,XC3 - D8.7,D7.7,D6.7,D5.7> 1375 * ; XC3-XC5=data type CODE- 0=ANSI TEXT string, 1376 * ; balance RESERVED * 1377 * **************************************************** 1378 * SV programming format 1 1379 * 1380 * This is the message format as implemented by the certain 1381 * existing devices. New designs should not use this format. The 1382 * message bytes are assigned as follows: 1383 * ; <0xE5> <0x10> <SRC> <DST> <0x01> <PXCT1> 1384 * ; <D1> <D2> <D3> <D4> <PXCT2> 1385 * ; <D5> <D6> <D7> <D8> <CHK> 1386 * 1387 * The upper nibble of PXCT1 must be 0, 1388 * and the upper nibble of PXCT2 must be 1. The meanings of the 1389 * remaining bytes are as defined in the LocoNet Personal 1390 * Edition specification. 1391 * ********************************************* 1392 * SV programming format 2 1393 * 1394 * This is the recommended format for new designs. 1395 * The message bytes as assigned as follows: * 1396 * ; <0xE5> <0x10> <SRC> <SV_CMD> <SV_TYPE> <SVX1> 1397 * ; <DST_L> <DST_H> <SV_ADRL> <SV_ADRH> <SVX2> 1398 * ; <D1> <D2> <D3> <D4> <CHK> 1399 * 1400 * The upper nibble of both SVX1 (PXCT1) and SVX2 (PXCT2) must be 1. 1401 */ 1402 1403 int src = l.getElement(2); // source of transfer 1404 int dst_l = l.getElement(3); // ls 7 bits of destination 1405 int dst_h = l.getElement(4); // ms 7 bits of destination 1406 int pxct1 = l.getElement(5); 1407 int pxct2 = l.getElement(10); 1408 1409 int[] d = l.getPeerXfrData(); 1410 1411 if ((src == 0x7F) && (dst_l == 0x7F) && (dst_h == 0x7F) 1412 && ((pxct1 & 0x70) == 0x40)) { 1413 // Download (firmware?) messages. 1414 int sub = pxct2 & 0x70; 1415 switch (sub) { 1416 case 0x00: // setup 1417 return Bundle.getMessage("LN_MSG_IPL_SETUP", 1418 l.getElement(6), 1419 l.getElement(8), 1420 l.getElement(9), 1421 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1422 StringUtil.twoHexFromInt(l.getElement(7))), 1423 l.getElement(11)); 1424 case 0x10: // set address 1425 return Bundle.getMessage("LN_MSG_IPL_SET_ADDRESS", 1426 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1427 StringUtil.twoHexFromInt(d[0]) 1428 + StringUtil.twoHexFromInt(d[1]) 1429 + StringUtil.twoHexFromInt(d[2]))); 1430 case 0x20: // send data 1431 case 0x30: // verify 1432 return Bundle.getMessage((sub == 0x20) ? "LN_MSG_IPL_SEND_DATA" : "LN_MSG_IPL_VERIFY_REQUEST", 1433 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[0])), 1434 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[1])), 1435 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[2])), 1436 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[3])), 1437 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[4])), 1438 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[5])), 1439 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[6])), 1440 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[7]))); 1441 case 0x40: // end op 1442 return Bundle.getMessage("LN_MSG_IPL_END"); 1443 default: // everything else isn't understood, go to default 1444 break; 1445 } 1446 } 1447 1448 if ((src == 0x7F) && (dst_l == 0x0) && (dst_h == 0x0) 1449 && ((pxct1 & 0x3) == 0x00) && ((pxct2 & 0x70) == 0x70)) { 1450 // throttle semaphore symbol message 1451 return Bundle.getMessage("LN_MSG_THROTTLE_SEMAPHORE", 1452 ((d[0] * 128) + d[1]), 1453 Bundle.getMessage(((d[2] & 0x10) == 0x10) 1454 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" 1455 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), 1456 Bundle.getMessage(((d[2] & 0x08) == 0x08) 1457 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" 1458 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), 1459 Bundle.getMessage(((d[2] & 0x04) == 0x04) 1460 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" 1461 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), 1462 Bundle.getMessage(((d[2] & 0x02) == 0x02) 1463 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" 1464 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), 1465 Bundle.getMessage(((d[2] & 0x01) == 0x01) 1466 ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_BLINKING" 1467 : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNBLINKING") 1468 ); 1469 } 1470 1471 if ((src == 0x7F) && ((pxct1 & 0x70) == 0x00)) { 1472 1473 if ((dst_l == 0x00) && (dst_h == 0x00)) { 1474 char[] c = new char[]{0, 0, 0, 0, 0, 0, 0, 0}; 1475 c[0] = (char) d[0]; 1476 c[1] = (char) d[1]; 1477 c[2] = (char) d[2]; 1478 c[3] = (char) d[3]; 1479 c[4] = (char) d[4]; 1480 c[5] = (char) d[5]; 1481 c[6] = (char) d[6]; 1482 c[7] = (char) d[7]; 1483 return Bundle.getMessage("LN_MSG_THROTTLE_TEXT_MESSAGE_ALL_THROTTLES", 1484 c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); 1485 } else { 1486 return Bundle.getMessage("LN_MSG_THROTTLE_TEXT_MESSAGE_SPECIFIC_THROTTLE", 1487 (char) d[0], (char) d[1], 1488 (char) d[2], (char) d[3], 1489 (char) d[4], (char) d[5], 1490 (char) d[6], (char) d[7], 1491 convertToMixed(dst_l, dst_h)); 1492 } 1493 } 1494 1495 String result = interpretSV1Message(l); 1496 if (!result.isEmpty()) { 1497 return result; 1498 } 1499 1500 result = interpretSV0Message(l); 1501 if (!result.isEmpty()) { 1502 return result; 1503 } 1504 1505 // check for a specific type - SV Programming messages format 2 1506 result = interpretSV2Message(l); 1507 if (!result.isEmpty()) { 1508 return result; 1509 } 1510 1511 return ""; 1512 } 1513 1514 private static String interpretOpcPeerXfer15(LocoNetMessage l) { 1515 /* 1516 * see interpretOpcImm15 1517 * and jmri.jmrix.loconet.uhlenbrock.LncvMessageContents.java 1518 */ 1519 1520 // check for a specific type - Uhlenbrock LNCV Programming messages format 1521 String result = interpretLncvMessage(l); 1522 if (!result.isEmpty()) { 1523 return result; 1524 } 1525 1526 return ""; 1527 } 1528 1529 /** Try to decode an LNSV type 1 message as sent in replies from LocoIO boards 1530 * in response to a read or write request from LocoBuffer/PC. 1531 * 1532 * @param l The LocoNet message to decode 1533 * @return a localised string for display to the user in a monitor 1534 */ 1535 private static String interpretSV0Message(LocoNetMessage l) { 1536 String svReply = ""; 1537 Lnsv1MessageContents sv1mc = null; 1538 try { 1539 // assume the message is an SV1 message 1540 sv1mc = new Lnsv1MessageContents(l); 1541 } catch (IllegalArgumentException e) { 1542 // message is not an SV1 message. Ignore the exception. 1543 } 1544 if (sv1mc != null) { 1545 // the message was indeed an SV1 message 1546 try { 1547 // get string representation of the message from an 1548 // available translation which is best suited to 1549 // the currently-active "locale" 1550 svReply = sv1mc.toString(); 1551 } catch (IllegalArgumentException e) { 1552 // message is not a properly-formatted SV1 read reply message. Ignore the exception. 1553 } 1554 } 1555 return svReply; 1556 } 1557 1558 /** Try to decode an LNSV type 1 message as sent to LocoIO boards 1559 * containing a read or write request from LocoBuffer/PC. 1560 * 1561 * @param l The LocoNet message to decode 1562 * @return a localised string for display to the user in a monitor 1563 */ 1564 private static String interpretSV1Message(LocoNetMessage l) { 1565 // (New Designs) 1566 String svReply = ""; 1567 Lnsv1MessageContents sv1mc = null; 1568 try { 1569 // assume the message is an SV1 message 1570 sv1mc = new Lnsv1MessageContents(l); 1571 } catch (IllegalArgumentException e) { 1572 // message is not an SV1 message. Ignore the exception. 1573 } 1574 if (sv1mc != null) { 1575 // the message was indeed an SV1 message 1576 try { 1577 // get string representation of the message from an 1578 // available translation which is best suited to 1579 // the currently-active "locale" 1580 svReply = sv1mc.toString(); 1581 } catch (IllegalArgumentException e) { 1582 // message is not a properly-formatted SV1 message. Ignore the exception. 1583 } 1584 } 1585 return svReply; 1586 } 1587 1588 private static String interpretSV2Message(LocoNetMessage l) { 1589 // (New Designs) 1590 String svReply = ""; 1591 Lnsv2MessageContents sv2mc = null; 1592 try { 1593 // assume the message is an SV2 message 1594 sv2mc = new Lnsv2MessageContents(l); 1595 } catch (IllegalArgumentException e) { 1596 // message is not an SV2 message. Ignore the exception. 1597 } 1598 if (sv2mc != null) { 1599 // the message was indeed an SV2 message 1600 try { 1601 // get string representation of the message from an 1602 // available translation which is best suited to 1603 // the currently-active "locale" 1604 svReply = sv2mc.toString(); 1605 } catch (IllegalArgumentException e) { 1606 // message is not a properly-formatted SV2 message. Ignore the exception. 1607 } 1608 } 1609 return svReply; 1610 } 1611 1612 private static String interpretLncvMessage(LocoNetMessage l) { 1613 String lncvReply = ""; 1614 LncvMessageContents cvmc = null; 1615 try { 1616 // assume the message is an LNCV message 1617 cvmc = new LncvMessageContents(l); 1618 } catch (IllegalArgumentException e) { 1619 // message is not an LNCV message. Ignore the exception. 1620 } 1621 if (cvmc != null) { 1622 // the message was indeed an LNCV message 1623 try { 1624 // get string representation of the message from an 1625 // available translation which is best suited to 1626 // the currently-active "locale" 1627 lncvReply = cvmc.toString(); 1628 } catch (IllegalArgumentException e) { 1629 // message is not a properly-formatted LNCV message. Ignore the exception. 1630 } 1631 } 1632 return lncvReply; 1633 } 1634 1635 private static String interpretOpcPeerXfer10(LocoNetMessage l) { 1636 // throttle status 1637 int tcntrl = l.getElement(2); 1638 String stat; 1639 switch (tcntrl) { 1640 case 0x40: 1641 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_OK"); 1642 break; 1643 case 0x7F: 1644 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_NO_KEYPRESS"); 1645 break; 1646 case 0x43: 1647 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_PLUS_KEY"); 1648 break; 1649 case 0x42: 1650 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_MINUS_KEY"); 1651 break; 1652 case 0x41: 1653 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_RUNSTOP_KEY"); 1654 break; 1655 case 0x4e: 1656 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_RESP_SEM_DISP_CMD"); 1657 break; 1658 default: 1659 stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_UNKONWN"); 1660 break; 1661 } 1662 1663 return Bundle.getMessage("LN_MSG_THROTTLE_STATUS", 1664 StringUtil.twoHexFromInt(tcntrl), 1665 stat, 1666 idString(l.getElement(3), l.getElement(4)), 1667 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1668 StringUtil.twoHexFromInt(l.getElement(7))), 1669 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1670 StringUtil.twoHexFromInt(l.getElement(8)))); 1671 } 1672 1673 private static String interpretOpcPeerXfer9(LocoNetMessage l, String reporterPrefix) { 1674 /* 1675 * Transponding "find" query and report messages. 1676 * Information reverse-engineered by B. Milhaupt and used with permission */ 1677 switch (l.getElement(2)) { 1678 case 0x40: { 1679 /* 1680 * ********************************************************************************** 1681 * Transponding "find" query message * The message bytes are 1682 * assigned as follows: 1683 * <p> 1684 * <0xE5> <0x09> <0x40> <AD_H> <AD_L> <0x00> 1685 * <0x00> <0x00> <CHK> * where: 1686 * <p> 1687 * <AD_H> is encoded as shown below: * When 1688 * <AD_H> = 0x7D, * Address is a 7 bit value defined solely by 1689 * <AD_L>. * When <AD_H> is not 0x7D, * Address is a 14 bit 1690 * value; AD_H{6:0} represent the upper 7 bits * of the 14 bit 1691 * address. 1692 * <p> 1693 * <AD_L> contains the least significant 7 bits of the 14 or 7 1694 * bit address. * * Information reverse-engineered by B. 1695 * Milhaupt and used with permission * 1696 * ********************************************************************************** 1697 */ 1698 String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); 1699 return Bundle.getMessage("LN_MSG_TRANSP_FIND_QUERY", 1700 locoAddr); 1701 } 1702 case 0x00: { 1703 /* 1704 * ********************************************************************************** 1705 * Transponding "find" report message * The message bytes are 1706 * assigned as follows: 1707 * <p> 1708 * <0xE5> <0x09> <0x00> <AD_H> <AD_L> <TR_ST> 1709 * <TR_ZS> <0x00> <CHK> * where: 1710 * <p> 1711 * <AD_H> is encoded as shown below: * When 1712 * <AD_H> = 0x7D, * Address is a 7 bit value defined solely by 1713 * <AD_L>. * When <AD_H> is not 0x7D, * Address is a 14 bit 1714 * value; AD_H{6:0} represent the upper 7 bits * of the 14 bit 1715 * address. 1716 * <p> 1717 * <AD_L> contains the least significant 7 bits of the 14 or 7 1718 * bit address. 1719 * <p> 1720 * <TR_ST> contains the transponding status for the addressed 1721 * equipment, * encoded as: * bits 7-6 always 00b * bit 5 1722 * encodes transponding presence * 0 = Addressed equipment is 1723 * absent * 1 = Addressed equipment is present * bits 4-0 encode 1724 * bits 7-3 of the Detection Section 1725 * <p> 1726 * <TR_ZS> contains the zone number and detection section, 1727 * encoded as: * bit 7 always 0 * bits 6-4 encode bits 2-0 of 1728 * the Detection Section * bits 3-1 encode the Transponding Zone 1729 * as shown below * 000b Zone A * 001b Zone B * 010b Zone C * 1730 * 011b Zone D * 100b Zone E * 101b Zone F * 110b Zone G * 111b 1731 * Zone H * bit 0 always 0 * * Information reverse-engineered by 1732 * B. Milhaupt and used with permission * 1733 * ********************************************************************************** 1734 */ 1735 1736 int section = ((l.getElement(5) & 0x1F) << 3) + ((l.getElement(6) & 0x70) >> 4) + 1; 1737 String zone; 1738 String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); 1739 1740 switch (l.getElement(6) & 0x0F) { 1741 case 0x00: 1742 zone = "A"; 1743 break; 1744 case 0x02: 1745 zone = "B"; 1746 break; 1747 case 0x04: 1748 zone = "C"; 1749 break; 1750 case 0x06: 1751 zone = "D"; 1752 break; 1753 case 0x08: 1754 zone = "E"; 1755 break; 1756 case 0x0A: 1757 zone = "F"; 1758 break; 1759 case 0x0C: 1760 zone = "G"; 1761 break; 1762 case 0x0E: 1763 zone = "H"; 1764 break; 1765 default: 1766 zone = Bundle.getMessage("LN_MSG_TRANSP_HELPER_UNKNOWN_ZONE", 1767 l.getElement(6) & 0x0F); 1768 break; 1769 } 1770 1771 // get system and user names 1772 String reporterSystemName = reporterPrefix 1773 + ((l.getElement(5) & 0x1F) * 128 + l.getElement(6) + 1); 1774 1775 Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName); 1776 1777 String uname = ""; 1778 if (reporter != null) { 1779 uname = reporter.getUserName(); 1780 } 1781 1782 if ((uname != null) && (!uname.isEmpty())) { 1783 return Bundle.getMessage("LN_MSG_TRANSP_REPORT_KNOWN_REPORTER_USERNAME", 1784 locoAddr, 1785 reporterSystemName, 1786 uname, 1787 section, 1788 zone); 1789 } 1790 return Bundle.getMessage("LN_MSG_TRANSP_REPORT_KNOWN_REPORTER_UNKNOWN_USERNAME", 1791 locoAddr, 1792 reporterSystemName, 1793 section, 1794 zone); 1795 } 1796 default: { 1797 break; 1798 } 1799 } 1800 return ""; 1801 } 1802 1803 private static String interpretOpcPeerXfer7(LocoNetMessage l) { 1804 // This might be Uhlenbrock IB-COM start/stop programming track 1805 if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42) { 1806 switch (l.getElement(5)) { 1807 case 0x40: { 1808 return Bundle.getMessage("LN_MSG_UHLENBROCK_STOP_PROGRAMMING_TRACK"); 1809 } 1810 case 0x41: { 1811 return Bundle.getMessage("LN_MSG_UHLENBROCK_START_PROGRAMMING_TRACK"); 1812 } 1813 default: 1814 break; 1815 } 1816 } 1817 return ""; 1818 } 1819 1820 private static String interpretOpcPeerXfer(LocoNetMessage l, String reporterPrefix) { 1821 String result; 1822 // The first byte seems to determine the type of message. 1823 switch (l.getElement(1)) { 1824 case 0x10: { //l.getZElement(1) 1825 result = interpretOpcPeerXfer16(l); 1826 if (!result.isEmpty()) { 1827 return result; 1828 } 1829 break; 1830 } 1831 case 0x0F: { 1832 result = interpretOpcPeerXfer15(l); 1833 if (!result.isEmpty()) { 1834 return result; 1835 } 1836 break; 1837 } 1838 case 0x0A: { 1839 result = interpretOpcPeerXfer10(l); 1840 if (!result.isEmpty()) { 1841 return result; 1842 } 1843 break; 1844 1845 } 1846 case 0x14: { 1847 result = interpretOpcPeerXfer20(l); 1848 if (!result.isEmpty()) { 1849 return result; 1850 } 1851 break; 1852 } 1853 case 0x09: { // l.getZElement(1) 1854 result = interpretOpcPeerXfer9(l, reporterPrefix); 1855 if (!result.isEmpty()) { 1856 return result; 1857 } 1858 break; 1859 } 1860 case 0x07: { 1861 result = interpretOpcPeerXfer7(l); 1862 if (!result.isEmpty()) { 1863 return result; 1864 } 1865 break; 1866 } 1867 default: { 1868 break; 1869 } 1870 } 1871 return ""; 1872 1873 } 1874 1875 private static String interpretLongAck(LocoNetMessage l) { 1876 int opcode = l.getElement(1); 1877 int ack1 = l.getElement(2); 1878 1879 switch (opcode | 0x80) { 1880 case (LnConstants.OPC_LOCO_ADR): 1881 // response for OPC_LOCO_ADR 1882 return Bundle.getMessage("LN_MSG_LONG_ACK_LOCO_ADR"); 1883 1884 case (LnConstants.OPC_LINK_SLOTS): 1885 // response for OPC_LINK_SLOTS 1886 return Bundle.getMessage("LN_MSG_LONG_ACK_LINK_SLOTS"); 1887 1888 case (LnConstants.OPC_SW_ACK): 1889 // response for OPC_SW_ACK 1890 switch (ack1) { 1891 case 0: 1892 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_FULL"); 1893 case 0x7f: 1894 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_ACCEPT"); 1895 default: 1896 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_UNKNOWN", 1897 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1898 StringUtil.twoHexFromInt(ack1)))+ 1899 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 1900 } 1901 case (LnConstants.OPC_SW_REQ): 1902 // response for OPC_SW_REQ 1903 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_REQ_FAIL"); 1904 1905 case (LnConstants.OPC_WR_SL_DATA): 1906 // response for OPC_WR_SL_DATA 1907 switch (ack1) { 1908 case 0: 1909 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_FAIL"); 1910 case 0x01: 1911 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_OK"); 1912 case 0x23: 1913 case 0x2b: 1914 case 0x6B: 1915 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_PROG_DCS51_OK"); 1916 case 0x40: 1917 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_BLIND"); 1918 case 0x7f: 1919 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_NOT_IMPL"); 1920 default: 1921 return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_UNKNOWN", 1922 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1923 StringUtil.twoHexFromInt(ack1)))+ 1924 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 1925 1926 } 1927 1928 case (LnConstants.OPC_SW_STATE): 1929 // response for OPC_SW_STATE 1930 return Bundle.getMessage("LN_MSG_LONG_ACK_SW_STATE", 1931 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1932 StringUtil.twoHexFromInt(ack1)), 1933 Bundle.getMessage((((ack1 & 0x20) != 0) 1934 ? "LN_MSG_SWITCH_STATE_CLOSED" 1935 : "LN_MSG_SWITCH_STATE_THROWN"))); 1936 1937 case (LnConstants.OPC_MOVE_SLOTS): 1938 // response for OPC_MOVE_SLOTS 1939 switch (ack1) { 1940 case 0: 1941 return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_REJECT"); 1942 case 0x7f: 1943 return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_ACCEPT"); 1944 default: 1945 return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_UNKNOWN", 1946 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1947 StringUtil.twoHexFromInt(ack1)))+ 1948 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 1949 1950 } 1951 1952 case LnConstants.OPC_IMM_PACKET: 1953 // response for OPC_IMM_PACKET 1954 if (ack1 == 0) { 1955 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_REJECT"); 1956 } else if (ack1 == 0x7f) { 1957 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_ACCEPT"); 1958 } else if (ack1 == 0x01) { // (l.getElement(1) == 0x6D) is same as case, same code for LNCV "unsupported CV" 1959 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_UHL_PROG"); 1960 } else if (ack1 == 0x02) { // LNCV Uhlenbrock programming reply 1961 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LNCV_READONLY"); 1962 } else if (ack1 == 0x03) { // LNCV Uhlenbrock programming reply 1963 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LNCV_ILLEGALVAL"); 1964 } else { 1965 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_UNKNOWN", 1966 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1967 StringUtil.twoHexFromInt(ack1)), 1968 128+ack1, 1969 StringUtil.twoHexFromInt(128+ack1) 1970 )+ 1971 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 1972 } 1973 1974 case LnConstants.OPC_IMM_PACKET_2: 1975 // response for OPC_IMM_PACKET_2 1976 return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LIM_MASTER", 1977 ack1, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1978 StringUtil.twoHexFromInt(ack1))); 1979 1980 case (LnConstants.RE_LACK_SPEC_CASE1 | 0x80): // 0x50 plus opcode bit so can match the switch'd value: 1981 case (LnConstants.RE_LACK_SPEC_CASE2 | 0x80): //0x00 plus opcode bit so can match the switch'd value: 1982 // OpSwitch read response reverse-engineered by B. Milhaupt and 1983 // used with permission 1984 int responseValue = l.getElement(2); 1985 if (responseValue == 0x7f) { 1986 return Bundle.getMessage("LN_MSG_LONG_ACK_SPEC_CASE1_2_ACCEPTED"); 1987 } else { 1988 return Bundle.getMessage("LN_MSG_LONG_ACK_SPEC_CASE1_2_REPORT", 1989 (((responseValue & 0x20) == 0x20) ? 1 : 0), 1990 (((responseValue & 0x20) == 0x20) 1991 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED") 1992 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"))); 1993 } 1994 case LnConstants.OPC_EXP_SEND_FUNCTION_OR_SPEED_AND_DIR: 1995 return Bundle.getMessage("LN_MSG_LONG_ACK_WRONG_THROTTLE_ID", 1996 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 1997 StringUtil.twoHexFromInt(l.getElement(2)))); 1998 1999 case LnConstants.OPC_ALM_READ: 2000 if (l.getElement(2) == 0) { 2001 return Bundle.getMessage("LN_MSG_LONG_ACK_SLOT_NOT_SUPPORTED", 2002 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 2003 StringUtil.twoHexFromInt(opcode))); 2004 } 2005 //$FALL-THROUGH$ 2006 default: 2007 return Bundle.getMessage("LN_MSG_LONG_ACK_NOT_KNOWN", 2008 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 2009 StringUtil.twoHexFromInt(opcode | 0x80)), 2010 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 2011 StringUtil.twoHexFromInt(l.getElement(2)))); 2012 } 2013 } 2014 2015 private static String interpretPm4xPowerEvent(LocoNetMessage l) { 2016 int pCMD = (l.getElement(3) & 0xF0); 2017 2018 if ((pCMD == 0x30) || (pCMD == 0x10)) { 2019 // autoreverse 2020 int cm1 = l.getElement(3); 2021 int cm2 = l.getElement(4); 2022 String sect1Mode, sect1State; 2023 String sect2Mode, sect2State; 2024 String sect3Mode, sect3State; 2025 String sect4Mode, sect4State; 2026 2027 if ((cm1 & 1) != 0) { 2028 sect1Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); 2029 sect1State = ((cm2 & 1) != 0) 2030 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") 2031 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); 2032 } else { 2033 sect1Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); 2034 sect1State = ((cm2 & 1) != 0) 2035 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") 2036 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); 2037 } 2038 2039 if ((cm1 & 2) != 0) { 2040 sect2Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); 2041 sect2State = ((cm2 & 2) != 0) 2042 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") 2043 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); 2044 } else { 2045 sect2Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); 2046 sect2State = ((cm2 & 2) != 0) 2047 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") 2048 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); 2049 } 2050 2051 if ((cm1 & 4) != 0) { 2052 sect3Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); 2053 sect3State = ((cm2 & 4) != 0) 2054 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") 2055 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); 2056 } else { 2057 sect3Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); 2058 sect3State = ((cm2 & 4) != 0) 2059 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") 2060 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); 2061 } 2062 2063 if ((cm1 & 8) != 0) { 2064 sect4Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); 2065 sect4State = ((cm2 & 8) != 0) 2066 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") 2067 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); 2068 } else { 2069 sect4Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); 2070 sect4State = ((cm2 & 8) != 0) 2071 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") 2072 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); 2073 } 2074 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X", 2075 (l.getElement(2) + 1) + ((l.getElement(1) & 0x1) << 7), 2076 sect1Mode, sect1State, sect2Mode, sect2State, 2077 sect3Mode, sect3State, sect4Mode, sect4State); 2078 } 2079 if ((pCMD == 0x20) ) { //BXP88 2080 int cm1 = l.getElement(3); 2081 int cm2 = l.getElement(4); 2082 ArrayList<Integer> sectsShorted = new ArrayList<>(); 2083 ArrayList<Integer> sectsUnshorted = new ArrayList<>(); 2084 if ((cm2 & 0x01) != 0) { 2085 sectsShorted.add(1); 2086 } else { 2087 sectsUnshorted.add(1); 2088 } 2089 if ((cm2 & 0x02) != 0) { 2090 sectsShorted.add(2); 2091 } else { 2092 sectsUnshorted.add(2); 2093 } 2094 if ((cm2 & 0x04) != 0) { 2095 sectsShorted.add(3); 2096 } else { 2097 sectsUnshorted.add(3); 2098 } 2099 if ((cm2 & 0x08) != 0) { 2100 sectsShorted.add(4); 2101 } else { 2102 sectsUnshorted.add(4); 2103 } 2104 if ((cm1 & 0x01) != 0) { 2105 sectsShorted.add(5); 2106 } else { 2107 sectsUnshorted.add(5); 2108 } 2109 if ((cm1 & 0x02) != 0) { 2110 sectsShorted.add(6); 2111 } else { 2112 sectsUnshorted.add(6); 2113 } 2114 if ((cm1 & 0x04) != 0) { 2115 sectsShorted.add(7); 2116 } else { 2117 sectsUnshorted.add(7); 2118 } 2119 if ((cm1 & 0x08) != 0) { 2120 sectsShorted.add(8); 2121 } else { 2122 sectsUnshorted.add(8); 2123 } 2124 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXP88", 2125 (l.getElement(2) + 1) + ((l.getElement(1) & 0x1) << 7), 2126 StringUtils.join(sectsShorted, ','), StringUtils.join(sectsUnshorted, ',')); 2127 } 2128 if ( (pCMD == 0x50) || (pCMD == 0x40)) { //BXPA1 2129 int cm1 = l.getElement(3); 2130 String RevState = ""; 2131 String BreakState = ""; 2132 if ((cm1 & 0x10) != 0) { // reversing state 2133 if ((cm1 & 0x08) != 0) { 2134 RevState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_REV"); 2135 } else { 2136 RevState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_NORM"); 2137 } 2138 } else { 2139 // breaker state 2140 if ((cm1 & 0x08) != 0) { 2141 BreakState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_SHORT"); 2142 } else { 2143 BreakState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_NONSHORT"); 2144 } 2145 } 2146 int bxpa1_Id = ((l.getElement(2) << 3 ) + (l.getElement(3) & 0x07 ) + 1); 2147 // Due to a problem with the firmware messages from x and x+4 are identical 2148 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1", 2149 bxpa1_Id, bxpa1_Id +4, 2150 RevState, BreakState); 2151 } 2152 return ""; 2153 } 2154 2155 private static String interpretOpSws(LocoNetMessage l) { 2156 int pCMD = (l.getElement(3) & 0xF0); 2157 if (pCMD == 0x70) { 2158 // programming 2159 int deviceType = l.getElement(3) & 0x7; 2160 String device; 2161 switch (deviceType) { 2162 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_PM4X: 2163 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_PM4X"); 2164 break; 2165 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_BDL16X: 2166 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_BDL16X"); 2167 break; 2168 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_SE8: 2169 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_SE8C"); 2170 break; 2171 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_DS64: 2172 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_DS64"); 2173 break; 2174 default: 2175 return ""; 2176 } 2177 2178 int val = (l.getElement(4) & 0x01); 2179 int opsw = (l.getElement(4) & 0x7E) / 2 + 1; 2180 int bdaddr = l.getElement(2) + 1; 2181 if ((l.getElement(1) & 0x1) != 0) { 2182 bdaddr += 128; 2183 } 2184 2185 if ((deviceType == 0) && (bdaddr == 1) && (l.getElement(4) == 0)) { 2186 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_ACCESS_QUERY_ALL"); 2187 } 2188 2189 if ((l.getElement(1) & 0x10) != 0) { 2190 // write 2191 String valType = (val == 1) 2192 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED") 2193 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); 2194 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_WRITE_ACCESS", 2195 device, bdaddr, opsw, val, valType); 2196 } else { 2197 // query 2198 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_QUERY_ACCESS", 2199 device, bdaddr, opsw); 2200 } 2201 } 2202 return ""; 2203 } 2204 2205 private static String interpretDeviceType(LocoNetMessage l) { 2206 int pCMD = (l.getElement(3) & 0xF0); 2207 if (pCMD == 0x00) { 2208 /* 2209 * ************************************************** 2210 * Device type report * The message bytes as assigned as follows: 2211 * <p> 2212 * <0xD0> <DQT_REQ> <DQT_BRD> <DQT_B3> <DQT_B4> 2213 * <CHK> * * where: 2214 * <p> 2215 * <DQT_REQ> contains the device query request, * encoded as: * bits 2216 * 7-4 always 0110b * bits 3-1 always 001b * bit 0 (BoardID-1)<7> 2217 * <p> 2218 * <DQT_BRD> contains most the device board ID number, * encoded as: 2219 * * bit 7 always 0b * bits 6-0 (BoardID-1)<6:0> 2220 * <p> 2221 * <DQT_B3> contains the board type identification, * encoded as: * 2222 * bits 7-4 always 0000b * bits 3-0 contain the encoded device type, 2223 * * encoded as: * 0000b PM4x device * 0001b BDL16x device * 0010b 2224 * SE8C device * 0011b DS64 device * others Unknown device type 2225 * <p> 2226 * <DQT_B4> contains device version number: * bit 7 always 0b * bits 2227 * 6-0 VersionNumber(6:0) * * Information reverse-engineered by B. 2228 * Milhaupt and used with permission * 2229 * ************************************************** 2230 */ 2231 // This message is a report which is sent by a LocoNet device 2232 // in response to a query of attached devices 2233 // Note - this scheme is supported by only some Digitrax devices. 2234 // 2235 // A VersionNumber of 0 implies the hardware does not report 2236 // a valid version number. 2237 // 2238 // Device type report reverse-engineered by B. Milhaupt and 2239 // used with permission 2240 int deviceType = l.getElement(3) & 0x7; 2241 String device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_UNKNOWN"); 2242 switch (deviceType) { 2243 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_PM4X: 2244 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_PM4X"); 2245 break; 2246 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_BDL16X: 2247 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_BDL16X"); 2248 break; 2249 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_SE8: 2250 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_SE8C"); 2251 break; 2252 case LnConstants.RE_MULTI_SENSE_DEV_TYPE_DS64: 2253 device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_DS64"); 2254 break; 2255 default: 2256 log.warn("Unhandled device type: {}", deviceType); 2257 break; 2258 } 2259 2260 int bdaddr = l.getElement(2) + 1; 2261 if ((l.getElement(1) & 0x1) != 0) { 2262 bdaddr += 128; 2263 } 2264 String versionNumber = Integer.toString(l.getElement(4)); 2265 if (l.getElement(4) == 0) { 2266 versionNumber = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_VER_UNKNOWN"); 2267 } 2268 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_DEV_TYPE_RPT", 2269 device, bdaddr, versionNumber); 2270 } 2271 return ""; 2272 } 2273 2274 private static String interpretOpcMultiSense(LocoNetMessage l, String reporterPrefix) { 2275 int type = l.getElement(1) & LnConstants.OPC_MULTI_SENSE_MSG; 2276 String result; 2277 switch (type) { 2278 case LnConstants.OPC_MULTI_SENSE_POWER: 2279 // This is a PM42 power event. 2280 result = interpretPm4xPowerEvent(l); 2281 if (!result.isEmpty()) { 2282 return result; 2283 } 2284 result = interpretOpSws(l); 2285 if (!result.isEmpty()) { 2286 return result; 2287 } 2288 result = interpretDeviceType(l); 2289 if (!result.isEmpty()) { 2290 return result; 2291 } else { 2292 break; 2293 } 2294 2295 case LnConstants.OPC_MULTI_SENSE_PRESENT: 2296 case LnConstants.OPC_MULTI_SENSE_ABSENT: 2297 result = interpretOpcMultiSenseTranspPresence(l, reporterPrefix); 2298 if (!result.isEmpty()) { 2299 return result; 2300 } 2301 break; 2302 case LnConstants.OPC_MULTI_SENSE_RAILCOM_AD: 2303 result = interpretOpcMultiSenseRailcomAD(l, reporterPrefix); 2304 if (!result.isEmpty()) { 2305 return result; 2306 } 2307 break; 2308 default: 2309 break; 2310 } 2311 return ""; 2312 } 2313 2314 private static String interpretOpcMultiSenseTranspPresence(LocoNetMessage l, String reporterPrefix) { 2315 // Transponding Event 2316 // get system and user names 2317 String reporterSystemName; 2318 String reporterUserName; 2319 String zone; 2320 int bxp88Zone = 1 + (l.getElement(2) & 0x07); 2321 switch (l.getElement(2) & 0x0f) { // ignore bit 0 which seems to provide some unknown info from the BXP88 2322 case 0x00: 2323 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEA"); 2324 break; 2325 case 0x02: 2326 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEB"); 2327 break; 2328 case 0x04: 2329 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEC"); 2330 break; 2331 case 0x06: 2332 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONED"); 2333 break; 2334 case 0x08: 2335 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEE"); 2336 break; 2337 case 0x0A: 2338 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEF"); 2339 break; 2340 case 0x0C: 2341 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEG"); 2342 break; 2343 case 0x0E: 2344 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEH"); 2345 break; 2346 default: 2347 zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONE_UNKNOWN", 2348 (l.getElement(2) & 0x0F)); 2349 break; 2350 } 2351 int type = l.getElement(1) & LnConstants.OPC_MULTI_SENSE_MSG; 2352 2353 reporterSystemName = reporterPrefix 2354 + ((l.getElement(1) & 0x1F) * 128 + l.getElement(2) + 1); 2355 2356 Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName); 2357 reporterUserName = ""; 2358 if (reporter != null) { 2359 String uname = reporter.getUserName(); 2360 if ((uname != null) && (!uname.isEmpty())) { 2361 reporterUserName = uname; 2362 } 2363 } 2364 int bxpa1Number = 1 + l.getElement(2) + (l.getElement(1) & 0x1F) * 128; 2365 int bxp88Number = 1 + (l.getElement(2)/8) + (l.getElement(1) & 0x1F) * 16; 2366 int section = 1 + (l.getElement(2) / 16) + (l.getElement(1) & 0x1F) * 8; 2367 2368 String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); 2369 String transpActivity = (type == LnConstants.OPC_MULTI_SENSE_PRESENT) 2370 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT") 2371 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_ABSENT"); 2372 2373 if ((l.getElement(2) & 0x1) == 0) { 2374 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_REPORT_WITH_BXP88", 2375 locoAddr, transpActivity, reporterSystemName, 2376 reporterUserName, section, zone, bxp88Number, bxp88Zone, bxpa1Number); 2377 } else { 2378 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_REPORT_NOT_BDL16X", 2379 locoAddr, transpActivity, reporterSystemName, 2380 reporterUserName, bxp88Number, bxp88Zone, bxpa1Number); 2381 } 2382 } 2383 2384 private static String convertRailComAD(int indexValue, int dynamicValue) { 2385 /* 2386 *************************************************** 2387 * RailCom App DYN (ID 7) message 2388 * indexValue = 6 bit value value per standard 2389 * dynamicValue = 8 bit value per standard 2390 **/ 2391 2392 String indexString; 2393 switch (indexValue) { 2394 case 0: //Speed 2395 indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_0"); 2396 break; 2397 case 7: //QoS 2398 indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_7"); 2399 break; 2400 default: 2401 indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_UNKNOWN"); 2402 break; 2403 } 2404 2405 return Bundle.getMessage("LN_MSG_RAILCOM_REPORT", 2406 indexValue, indexString, dynamicValue); 2407 } 2408 2409 private static String interpretOpcMultiSenseRailcomAD(LocoNetMessage l, String reporterPrefix) { 2410 /* 2411 *************************************************** 2412 * Multi Sense Standard RailCom App DYN message (Loconet OpCode 0xD0) 2413 * The message bytes are assigned as follows: 2414 * 2415 * <0xD0> <RC_I> <RCDV_L> <AD_H> <AD_L> <CHK> 2416 * 2417 * <RC_I> is encoded as shown below 2418 * bit 7 always 0 2419 * bits 6-5 always 10 (0x40) 2420 * bits 4-1 RailCom App:Dyn Index Value (4 bit value, but expected 6 bits per standard) 2421 * bit 0 RailCom Dynamic Value high bit 2422 * 2423 * <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> 2424 * 2425 * <AD_H> is encoded as shown below: * When 2426 * <AD_H> = 0x7D, * Address is a 7 bit value defined solely by 2427 * <AD_L>. * When <AD_H> is not 0x7D, * Address is a 14 bit 2428 * value; AD_H{6:0} represent the upper 7 bits * of the 14 bit 2429 * address. 2430 * 2431 * Information reverse-engineered by Michael Richardson 2432 **/ 2433 2434 String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); 2435 int indexValue = (l.getElement(1) & 0x1E)/2; //bits 4-1 2436 int dynamicValue = l.getElement(2) + (l.getElement(1) & 0x01) * 128; 2437 2438 String railcomAdString = convertRailComAD(indexValue, dynamicValue); 2439 2440 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_RAILCOM_REPORT", 2441 locoAddr, railcomAdString); 2442 } 2443 2444 private static String interpretOpcMultiSenseLong(LocoNetMessage l, String reporterPrefix) { 2445 /* 2446 ************************************************** 2447 * Multi Sense Long RailCom App DYN message (Loconet OpCode 0xE0) 2448 * The message bytes as assigned as follows: 2449 * 2450 * <0xE0> <0x09> <MSL_I> <BLK_L> <AD_H> <AD_L> <RCDV_H> <RCDV_L> <CHK> 2451 * 2452 * <0xEO> OpCode 2453 * <Ox09> Message Length 2454 * <MSL_I> is encoded as shown below 2455 * bit 7 always 0 2456 * bits 6-5 (00 = Absent, 01 = Present, 10 = Present with AppDyn Message, 11 = unknown) 2457 * bit 4 ? - unverified - currently part of detection block number logic following Multi Sense Standard 2458 * bits 0-3 block high 2459 * 2460 * <BLK_L> 11 bit number representing the detection block. Lower 7 bits plus 4 high bits from <MSL_I> {3:0} 2461 * 2462 * <AD_H> is encoded as shown below: 2463 * When <AD_H> = 0x7D, Address is a 7 bit value defined solely by <AD_L>. 2464 * When <AD_H> is not 0x7D, Address is a 14 bit value; 2465 * AD_H{6:0} represent the upper 7 bits * of the 14 bit address. 2466 * 2467 * <RCDV_H> is encoded as shown below: 2468 * bit 7 always 0 2469 * bit 6 - Loco direction: 0 = East, 1 = West 2470 * bits 5-1 RailCom App:Dyn Index Value (5 bit value, but expected 6 bits per standard) 2471 * bit 0 RailCom Dynamic Value high bit 2472 * 2473 * <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> 2474 * 2475 * <CHK> 2476 * 2477 * Information reverse-engineered by Michael Ricahrdson 2478 */ 2479 2480 if (l.getElement(1) == 0x09){ // Only process 0xE0 0x09 messages 2481 // Transponding Event 2482 // get system and user names 2483 2484 int type = l.getElement(2) & LnConstants.OPC_MULTI_SENSE_MSG; //bits 5-4 2485 //0x00 = absent = 00 2486 //0x20 = present = 10 2487 //0x40 = present with App Dyn = 01 2488 //0x60 = unknown = 11 2489 2490 if (type == 0x60) { //unknown at this point 2491 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2492 } 2493 2494 // Now Process 0x00, 0x20, and 0x40 2495 String reporterSystemName = reporterPrefix + ((l.getElement(2) & 0x1F) * 128 + l.getElement(3) + 1); 2496 2497 Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName); 2498 String reporterUserName = ""; 2499 if (reporter != null) { 2500 String uname = reporter.getUserName(); 2501 if ((uname != null) && (!uname.isEmpty())) { 2502 reporterUserName = uname; 2503 } 2504 } 2505 2506 String locoAddr = convertToMixed(l.getElement(5), l.getElement(4)); 2507 2508 String transpActivity; 2509 2510 String direction = ((l.getElement(6) & 0x40) == 0) 2511 ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_LOCO_DIRECTION_HELPER_EAST") 2512 : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_LOCO_DIRECTION_HELPER_WEST"); 2513 2514 switch (type) { 2515 case LnConstants.OPC_MULTI_SENSE_RAILCOM_AD: 2516 int indexValue = (l.getElement(6) & 0x3E)/2; //bits 5-1 2517 int dynamicValue = l.getElement(7) + (l.getElement(6) & 0x01) * 128; 2518 transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT"); 2519 2520 String railcomAdString = convertRailComAD(indexValue, dynamicValue); 2521 String multiSenseLongString = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT", 2522 locoAddr, direction, transpActivity, reporterSystemName, reporterUserName); 2523 2524 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_RAILCOM_REPORT", 2525 multiSenseLongString, railcomAdString); 2526 2527 case LnConstants.OPC_MULTI_SENSE_PRESENT: 2528 if ((l.getElement(6) & 0x3F) != 0 || l.getElement(7) != 0 ) { 2529 // within current understanding values here are not expected 2530 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2531 } 2532 2533 transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT"); 2534 2535 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT", 2536 locoAddr, direction, transpActivity, reporterSystemName, reporterUserName); 2537 2538 case LnConstants.OPC_MULTI_SENSE_ABSENT: 2539 if ((l.getElement(6) & 0x3F) != 0 || l.getElement(7) != 0 ) { 2540 // within current understanding values here are not expected 2541 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2542 } 2543 2544 transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_ABSENT"); 2545 2546 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT", 2547 locoAddr, direction, transpActivity, reporterSystemName, reporterUserName); 2548 2549 default: 2550 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2551 } 2552 2553 } else { 2554 return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE"); 2555 } 2556 } 2557 2558 private static String interpretOpcWrSlDataOpcSlRdData(LocoNetMessage l) { 2559 int slot = l.getElement(2); // slot number for this request 2560 String mode; 2561 int command = l.getOpCode(); 2562 int id1 = l.getElement(11); // ls 7 bits of ID code 2563 int id2 = l.getElement(12); // ms 7 bits of ID code 2564 /* 2565 * These messages share a common data format with the only difference being 2566 * whether we are reading or writing the slot data. 2567 */ 2568 if (command == LnConstants.OPC_WR_SL_DATA) { 2569 mode = Bundle.getMessage("LN_MSG_SLOT_HELPER_ACCESS_TYPE_REQUEST"); 2570 } else { 2571 mode = Bundle.getMessage("LN_MSG_SLOT_HELPER_ACCESS_TYPE_RESPONSE"); 2572 } 2573 2574 String result; 2575 switch (slot) { 2576 case LnConstants.FC_SLOT: 2577 result = interpretFastClockSlot(l, mode, id1, id2); 2578 if (!result.isEmpty()) { 2579 return result; 2580 } 2581 break; 2582 case LnConstants.PRG_SLOT: 2583 result = interpretProgSlot(l, mode, id1, id2, command); 2584 if (!result.isEmpty()) { 2585 return result; 2586 } 2587 break; 2588 2589 case 0x79: 2590 case 0x7a: 2591 case 0x7D: 2592 return ""; 2593 case LnConstants.CFG_EXT_SLOT: 2594 result = interpretCmdStnExtCfgSlotRdWr(l, command); 2595 if (!result.isEmpty()) { 2596 return result; 2597 } 2598 break; 2599 2600 // end programming track block 2601 case LnConstants.CFG_SLOT: 2602 result = interpretCmdStnCfgSlotRdWr(l, command); 2603 if (!result.isEmpty()) { 2604 return result; 2605 } 2606 break; 2607 2608 default: 2609 result = interpretStandardSlotRdWr(l, id1, id2, command, slot); 2610 if (!result.isEmpty()) { 2611 return result; 2612 } 2613 break; 2614 } 2615 2616 return ""; 2617 } 2618 2619 private static String interpretOpcInputRep(LocoNetMessage l, String sensorPrefix) { 2620 int in1 = l.getElement(1); 2621 int in2 = l.getElement(2); 2622 int contactNum = ((SENSOR_ADR(in1, in2) - 1) * 2 + ((in2 & LnConstants.OPC_INPUT_REP_SW) != 0 ? 2 : 1)); 2623 // get system and user names 2624 String sensorSystemName = sensorPrefix + contactNum; 2625 String sensorUserName; 2626 Sensor sensor = InstanceManager.getDefault(SensorManager.class).getSensor(sensorSystemName); 2627 sensorUserName = ""; 2628 if (sensor != null) { 2629 String uname = sensor.getUserName(); 2630 if ((uname != null) && (!uname.isEmpty())) { 2631 sensorUserName = " ("+uname+")"; 2632 } 2633 } 2634 2635 int sensorid = (SENSOR_ADR(in1, in2) - 1) * 2 2636 + ((in2 & LnConstants.OPC_INPUT_REP_SW) != 0 ? 2 : 1); 2637 2638 int bdlid = ((sensorid - 1) / 16) + 1; 2639 int bdlin = ((sensorid - 1) % 16) + 1; 2640 String bdl = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_BDL_INFO", 2641 bdlid, bdlin); 2642 2643 int boardid = ((sensorid - 1) / 8) + 1; 2644 int boardindex = ((sensorid - 1) % 8); 2645 String otherBoardsNames; 2646 String otherBoardsInputs; 2647 if (sensorid < 289) { 2648 otherBoardsNames = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_ALL_EQUIV_BOARDS", boardid); 2649 otherBoardsInputs = Bundle.getMessage("LN_MSG_OPC_INPUT_REPORT_INPUT_NAMES_ALL_EQUIV_BOARDS", 2650 ds54sensors[boardindex], ds64sensors[boardindex], 2651 se8csensors[boardindex]); 2652 } else { 2653 otherBoardsNames = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_NO_SE8C", boardid); 2654 otherBoardsInputs = Bundle.getMessage("LN_MSG_OPC_INPUT_REPORT_INPUT_NAMES_NO_SE8C", 2655 ds54sensors[boardindex], ds64sensors[boardindex]); 2656 } 2657 2658 // There is no way to tell what kind of a board sent the message. 2659 // To be user-friendly, we just print all the known combos. 2660 return Bundle.getMessage("LN_MSG_OPC_INPUT_REP", 2661 sensorSystemName, sensorUserName, 2662 Bundle.getMessage((in2 & LnConstants.OPC_INPUT_REP_HI) != 0 2663 ? "LN_MSG_SENSOR_STATE_HIGH" : "LN_MSG_SENSOR_STATE_LOW"), 2664 bdl, 2665 otherBoardsNames, otherBoardsInputs); 2666 } 2667 2668 private static String interpretOpcSwRep(LocoNetMessage l, String turnoutPrefix) { 2669 int sn1 = l.getElement(1); 2670 int sn2 = l.getElement(2); 2671 // get system and user names 2672 String turnoutUserName = ""; 2673 2674 String turnoutSystemName = turnoutPrefix 2675 + SENSOR_ADR(sn1, sn2); 2676 Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); 2677 2678 String uname; 2679 if (turnout != null) { 2680 uname = turnout.getUserName(); 2681 if ((uname != null) && (!uname.isEmpty())) { 2682 turnoutUserName = uname; 2683 } else { 2684 turnoutUserName = ""; 2685 } 2686 } 2687 2688 if ((sn2 & LnConstants.OPC_SW_REP_INPUTS) != 0) { 2689 return Bundle.getMessage("LN_MSG_OPC_SW_REP_INPUTS_STATE", 2690 turnoutSystemName, turnoutUserName, 2691 Bundle.getMessage(((sn2 & LnConstants.OPC_SW_REP_SW) != 0 2692 ? "LN_MSG_SENSOR_SW_INPUT_TYPE_HI" 2693 : "LN_MSG_SENSOR_SW_INPUT_TYPE_LO")), 2694 Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_HI) != 0) 2695 ? "LN_MSG_SENSOR_SW_INPUT_STATE_HI" 2696 : "LN_MSG_SENSOR_SW_INPUT_STATE_LO"))); 2697 } 2698 return Bundle.getMessage("LN_MSG_OPC_SW_REP_OUTPUT_STATE", 2699 turnoutSystemName, turnoutUserName, 2700 Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_CLOSED) != 0) 2701 ? "LN_MSG_SENSOR_SW_OUTPUT_STATE_ON" 2702 : "LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF")), 2703 Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_THROWN) != 0) 2704 ? "LN_MSG_SENSOR_SW_OUTPUT_STATE_ON" 2705 : "LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF"))); 2706 } 2707 2708 private static String interpretOpcSwAck(LocoNetMessage l, String turnoutPrefix) { 2709 int sw2 = l.getElement(2); 2710 if ((sw2 & 0x40) == 0x40) { 2711 return ""; 2712 } 2713 // get system and user names 2714 String turnoutUserName = ""; 2715 2716 String turnoutSystemName = turnoutPrefix 2717 + SENSOR_ADR(l.getElement(1), l.getElement(2)); 2718 Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); 2719 2720 String uname; 2721 if (turnout != null) { 2722 uname = turnout.getUserName(); 2723 if ((uname != null) && (!uname.isEmpty())) { 2724 turnoutUserName = uname; 2725 } else { 2726 turnoutUserName = ""; 2727 } 2728 } 2729 2730 String pointsDirection = ((sw2 & LnConstants.OPC_SW_ACK_CLOSED) != 0 2731 ? Bundle.getMessage("LN_MSG_SW_POS_CLOSED") 2732 : Bundle.getMessage("LN_MSG_SW_POS_THROWN")); 2733 String outputState = (((sw2 & LnConstants.OPC_SW_ACK_OUTPUT) != 0) 2734 ? Bundle.getMessage("LN_MSG_SENSOR_SW_OUTPUT_STATE_ON") 2735 : Bundle.getMessage("LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF")); 2736 return Bundle.getMessage("LN_MSG_REQ_SWITCH", turnoutSystemName, 2737 turnoutUserName, pointsDirection, outputState); 2738 } 2739 2740 private static String interpretOpcSwState(LocoNetMessage l, String turnoutPrefix) { 2741 // get system and user names 2742 if ((l.getElement(2) & 0x40) != 0x00) { 2743 return ""; 2744 } 2745 String turnoutUserName = ""; 2746 String turnoutSystemName = turnoutPrefix 2747 + SENSOR_ADR(l.getElement(1), l.getElement(2)); 2748 Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); 2749 2750 String uname; 2751 if (turnout != null) { 2752 uname = turnout.getUserName(); 2753 if ((uname != null) && (!uname.isEmpty())) { 2754 turnoutUserName = uname; 2755 } else { 2756 turnoutUserName = ""; 2757 } 2758 } 2759 2760 2761 return Bundle.getMessage("LN_MSG_SW_STATE", turnoutSystemName, 2762 turnoutUserName); 2763 } 2764 2765 private static String interpretOpcRqSlData(LocoNetMessage l) { 2766 int slot = l.getElement(1) + 128 * (l.getElement(2) & 0x07); 2767 boolean expSlotRequ = (l.getElement(2) & 0x40) == 0X40; 2768 switch (slot) { 2769 // Slots > 120 & < 128 are all special, but these are the only ones we know to decode. 2770 // Extended System Slots 248 thru 251 dealt with separately, not here 2771 case LnConstants.FC_SLOT: 2772 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_FC_SLOT"); 2773 case LnConstants.CFG_SLOT: 2774 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_CFG_SLOT"); 2775 case LnConstants.CFG_EXT_SLOT: 2776 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_EXT_CFG_SLOT"); 2777 case LnConstants.PRG_SLOT: 2778 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_PRG_SLOT"); 2779 case 0x79: 2780 case 0x7a: 2781 case 0x7d: 2782 break; 2783 default: 2784 if (expSlotRequ) { 2785 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_LOCO_EXP_SLOT", slot); 2786 } else { 2787 return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_LOCO_SLOT", slot); 2788 } 2789 } 2790 return ""; 2791 } 2792 2793 private static String interpretOpcMoveSlots(LocoNetMessage l) { 2794 int src = l.getElement(1); 2795 int dest = l.getElement(2); 2796 if ((src >= 0x79) && (src <= 0x7f)) { 2797 return ""; 2798 } 2799 if ((dest >= 0x79) && (dest <= 0x7f)) { 2800 return ""; 2801 } 2802 2803 /* check special cases */ 2804 if (src == 0) { 2805 /* DISPATCH GET */ 2806 2807 return Bundle.getMessage("LN_MSG_MOVE_SL_GET_DISP"); 2808 } else if (src == dest) { 2809 /* IN USE */ 2810 2811 return Bundle.getMessage("LN_MSG_MOVE_SL_NULL_MOVE", src); 2812 } else if (dest == 0) { 2813 /* DISPATCH PUT */ 2814 2815 return Bundle.getMessage("LN_MSG_MOVE_SL_DISPATCH_PUT", src); 2816 } else { 2817 /* general move */ 2818 2819 return Bundle.getMessage("LN_MSG_MOVE_SL_MOVE", src, dest); 2820 } 2821 } 2822 2823 private static String interpretOpcConsistFunc(LocoNetMessage l) { 2824 int slot = l.getElement(1); 2825 int dirf = l.getElement(2); 2826 if ((dirf & 0x40) == 0x40) { 2827 return ""; 2828 } 2829 return Bundle.getMessage("LN_MSG_CONSIST_FUNC", 2830 slot, 2831 interpretDIRF(dirf)); 2832 } 2833 2834 private static String interpretOpcLocoSnd(LocoNetMessage l) { 2835 int slot = l.getElement(1); 2836 int snd = l.getElement(2); 2837 return Bundle.getMessage("LN_MSG_OPC_LOCO_SND", 2838 slot, 2839 Bundle.getMessage((snd & LnConstants.SND_F5) != 0 2840 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), 2841 Bundle.getMessage((snd & LnConstants.SND_F6) != 0 2842 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), 2843 Bundle.getMessage((snd & LnConstants.SND_F7) != 0 2844 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), 2845 Bundle.getMessage((snd & LnConstants.SND_F8) != 0 2846 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")); 2847 2848 } 2849 2850 protected static String interpretDIRF(int dirf) { 2851 if ((dirf & 0x40) == 0x40) { 2852 return ""; 2853 } 2854 String[] dirf0_4 = interpretF0_F4toStrings(dirf); 2855 return Bundle.getMessage("LN_MSG_HELPER_DIRF", 2856 Bundle.getMessage((dirf & LnConstants.DIRF_DIR) != 0 2857 ? "LN_MSG_DIRECTION_REV" : "LN_MSG_DIRECTION_FWD"), 2858 dirf0_4[0], dirf0_4[1], dirf0_4[2], dirf0_4[3], dirf0_4[4]); 2859 2860 } 2861 2862 private static String interpretOpcLocoDirf(LocoNetMessage l) { 2863 int slot = l.getElement(1); 2864 int dirf = l.getElement(2); 2865 2866 String dirFinfo = interpretDIRF(dirf); 2867 if (dirFinfo.isEmpty()) { 2868 return ""; 2869 } 2870 2871 return Bundle.getMessage("LN_MSG_OPC_LOCO_DIRF", 2872 slot, dirFinfo); 2873 } 2874 2875 private static String interpretOpcLocoSpd(LocoNetMessage l) { 2876 int slot = l.getElement(1); 2877 int spd = l.getElement(2); 2878 2879 if (spd == LnConstants.OPC_LOCO_SPD_ESTOP) { 2880 return Bundle.getMessage("LN_MSG_OPC_LOCO_SPD_ESTOP", slot); 2881 } else { 2882 return Bundle.getMessage("LN_MSG_OPC_LOCO_SPD_NORMAL", slot, spd); 2883 } 2884 2885 } 2886 2887 private static String interpretOpcPanelQuery(LocoNetMessage l) { 2888 switch (l.getElement(1)) { 2889 case 0x00: { 2890 return Bundle.getMessage("LN_MSG_OPC_DF_TETHERLESS_QUERY"); 2891 } 2892 case 0x40: { 2893 if (l.getElement(2) == 0x1F) { 2894 // Some UR devices treat this operation as a set plus query, others 2895 // treat this only as a set. 2896 return Bundle.getMessage("LN_MSG_OPC_DF_SET_LOCONETID", l.getElement(3)); 2897 } 2898 break; 2899 } 2900 default: { 2901 break; 2902 } 2903 } 2904 return ""; 2905 } 2906 2907 private static String interpretOpcSwReq(LocoNetMessage l, String turnoutPrefix) { 2908 int sw1 = l.getElement(1); 2909 int sw2 = l.getElement(2); 2910 if ((sw2 & 0x40) == 0x40) { 2911 return ""; 2912 } 2913 2914 if ((!(((sw2 & 0xCF) == 0x0F) && ((sw1 & 0xFC) == 0x78))) 2915 && (!(((sw2 & 0xCF) == 0x07) && ((sw1 & 0xFC) == 0x78)))) { 2916 // ordinary form, LPU V1.0 page 9 2917 // handle cases which are not "stationary decoder interrogate" messages 2918 // get system and user names 2919 String turnoutUserName = ""; 2920 2921 String turnoutSystemName = turnoutPrefix 2922 + SENSOR_ADR(l.getElement(1), l.getElement(2)); 2923 Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); 2924 2925 String uname; 2926 if (turnout != null) { 2927 uname = turnout.getUserName(); 2928 if ((uname != null) && (!uname.isEmpty())) { 2929 turnoutUserName = uname; 2930 } else { 2931 turnoutUserName = ""; 2932 } 2933 } 2934 2935 String pointsDirection = ((sw2 & LnConstants.OPC_SW_ACK_CLOSED) != 0 2936 ? Bundle.getMessage("LN_MSG_SW_POS_CLOSED") 2937 : Bundle.getMessage("LN_MSG_SW_POS_THROWN")); 2938 String outputState = ((sw2 & LnConstants.OPC_SW_ACK_OUTPUT) != 0 2939 ? Bundle.getMessage("LN_MSG_SW_OUTPUT_STATE_ON") 2940 : Bundle.getMessage("LN_MSG_SW_OUTPUT_STATE_OFF")); 2941 if (turnoutUserName.isEmpty()) { 2942 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_NORMAL_WITHOUT_USERNAME", 2943 turnoutSystemName, 2944 pointsDirection, outputState); 2945 } else { 2946 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_NORMAL_WITH_USERNAME", 2947 turnoutSystemName, turnoutUserName, 2948 pointsDirection, outputState); 2949 } 2950 } 2951 2952 /* 2953 Handle cases which are "stationary decoder interrogate" messages. 2954 */ 2955 2956 /* 2957 * Decodes a/c/b bits to allow proper creation of a list of addresses 2958 * which ought to reply to the "stationary decoder interrogate" message. 2959 */ 2960 int a = (sw2 & 0x20) >> 5; 2961 int c = (sw1 & 0x02) >> 1; 2962 int b = (sw1 & 0x01); 2963 2964 /* 2965 * All this blob does is loop through the ranges indicated by the 2966 * a/c/b bits, they are mask bits in the midde of the range. The 2967 * idea is to get 8 sensors at a time, since that is generally what 2968 * units have, and to query units 1, 9, 17... then 2, 10, 18... and 2969 * so on such that if they are all in a row they don't get hit at 2970 * the same time. 2971 */ 2972 int topbits; 2973 int midbits = (a << 2) + (c << 1) + b; 2974 int count = 0; 2975 StringBuilder addrListB = new StringBuilder(); 2976 for (topbits = 0; topbits < 32; topbits++) { 2977 // The extra "+1" adjusts for the fact that we show 1-2048, 2978 // rather than 0-2047 on the wire. 2979 int lval = (topbits << 6) + (midbits << 3) + 1; 2980 int hval = lval + 7; 2981 2982 if ((count % 8) != 0) { 2983 addrListB.append(", "); // NOI18N 2984 } else { 2985 if (count == 0) { 2986 addrListB.append("\t"); // NOI18N 2987 } else { 2988 addrListB.append(",\n\t"); // NOI18N 2989 } 2990 } 2991 addrListB.append(lval); // NOI18N 2992 addrListB.append("-").append(hval); // NOI18N 2993 count++; 2994 } 2995 2996 String addrList = addrListB.toString(); 2997 2998 if (((sw2 & 0xCF) == 0x0F) && ((sw1 & 0xFC) == 0x78)) { 2999 // broadcast address LPU V1.0 page 12 3000 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_INTERROGATE_TURNOUTS", 3001 a, c, b, addrList); 3002 } else { 3003 // broadcast address LPU V1.0 page 13 3004 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_INTERROGATE_SENSORS_TURNOUTS", 3005 a, c, b, addrList); 3006 } 3007 } 3008 3009 private static String interpretFastClockSlot(LocoNetMessage l, String mode, int id1, int id2) { 3010 /* 3011 * FAST Clock: The system FAST clock and parameters are implemented in 3012 * Slot#123 <7B>. Use <EF> to write new clock information, Slot read of 3013 * 0x7B,<BB><7B>.., will return current System clock information, and 3014 * other throttles will update to this SYNC. Note that all attached 3015 * display devices keep a current clock calculation based on this SYNC 3016 * read value, i.e. devices MUST not continuously poll the clock SLOT to 3017 * generate time, but use this merely to restore SYNC and follow current 3018 * RATE etc. This clock slot is typically "pinged" * or read SYNC'd 3019 * every 70 to 100 seconds, by a single user, so all attached devices 3020 * can synchronise any phase drifts. Upon seeing a SYNC read, all 3021 * devices should reset their local sub-minute phase counter and 3022 * invalidate the SYNC update ping generator. 3023 * <p> 3024 * Clock Slot Format: 3025 * <p> 3026 * <0xEF>,<0E>,<7B>,<CLK_RATE>,<FRAC_MINSL>,<FRAC_MINSH>,<256-MINS_60>, 3027 * <TRK><256-HRS_24>,<DAYS>,<CLK_CNTRL>,<ID1>,<1D2>,<CHK> 3028 * <p> 3029 * where: 3030 * <p> 3031 * <CLK_RATE> 0=Freeze clock, * 1=normal 1:1 rate, 10=10:1 etc., max 3032 * VALUE is 7F/128 to 1 3033 * <p> 3034 * <FRAC_MINSL> FRAC mins hi/lo are a sub-minute counter, depending on 3035 * the CLOCK generator 3036 * <p> 3037 * <FRAC_MINSH> Not for ext. usage. This counter is reset when valid 3038 * <E6><7B> 3039 * SYNC message is seen 3040 * <p> 3041 * <256-MINS_60> This is FAST clock MINUTES subtracted from 256. Modulo 3042 * 0-59 3043 * <p> 3044 * <256-HRS_24> This is FAST clock HOURS subtracted from 256. Modulo 3045 * 0-23 3046 * <p> 3047 * <DAYS> number of 24 Hr clock rolls, positive count 3048 * <p> 3049 * <CLK_CNTRL> Clock Control Byte D6- 1=This is valid Clock information, 3050 * 0=ignore this <E6><7B>, SYNC reply 3051 * <p> 3052 * <ID1>,<1D2> This is device ID last setting the clock. 3053 * <p> 3054 * <00><00> shows no set has happened 3055 * <p> 3056 * <7F><7x> are reserved for PC access * 3057 */ 3058 3059 int minutes; // temporary time values 3060 int hours; 3061 int clk_rate = l.getElement(3); // 0 = Freeze clock, 1 = normal, 3062 // 10 = 10:1 etc. Max is 0x7f 3063 int mins_60 = l.getElement(6); // 256 - minutes 3064 int track_stat = l.getElement(7); // track status 3065 int hours_24 = l.getElement(8); // 256 - hours 3066 int days = l.getElement(9); // clock rollovers 3067 int clk_cntrl = l.getElement(10); // bit 6 = 1; data is valid 3068 // clock info 3069 // " " 0; ignore this reply 3070 // id1/id2 is device id of last device to set the clock 3071 // " " = zero shows not set has happened 3072 3073 /* recover hours and minutes values */ 3074 minutes = ((255 - mins_60) & 0x7f) % 60; 3075 hours = ((256 - hours_24) & 0x7f) % 24; 3076 hours = (24 - hours) % 24; 3077 minutes = (60 - minutes) % 60; 3078 3079 return Bundle.getMessage("LN_MSG_SLOT_ACCESS_FAST_CLOCK", 3080 mode, 3081 ((clk_cntrl & 0x20) != 0 ? "" : Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_SYNC")), 3082 (clk_rate != 0 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_RUNNING") 3083 : Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_FROZEN")), 3084 clk_rate, 3085 days, 3086 fcTimeToString(hours, minutes), 3087 idString(id1, id2), 3088 trackStatusByteToString(track_stat)); 3089 } 3090 3091 private static String interpretProgSlot(LocoNetMessage l, String mode, int id1, int id2, int command) { 3092 /* 3093 * ******************************************************************************************** 3094 * Programmer track: 3095 * ================= 3096 * The programmer track is 3097 * accessed as Special slot #124 ( $7C, 0x7C). It is a full 3098 * asynchronous shared system resource. 3099 * 3100 * To start Programmer task, 3101 * write to slot 124. There will be an immediate LACK acknowledge 3102 * that indicates what programming will be allowed. If a valid programming 3103 * task is started, 3104 * then at the final (asynchronous) programming 3105 * completion, a Slot read <E7> from slot 124 will be sent. This is 3106 * the final task status reply. 3107 * 3108 * Programmer Task Start: 3109 * 3110 * ---------------------- 3111 * <p> 3112 * <0xEF>,<0E>,<7C>,<PCMD>,<0>,<HOPSA>,<LOPSA>,<TRK>;<CVH>,<CVL>, 3113 * <p> 3114 * <DATA7>,<0>,<0>,<CHK> * * This OPC leads to immediate LACK codes: 3115 * <p> 3116 * <B4>,<7F>,<7F>,<chk> Function NOT implemented, no reply. 3117 * <p> 3118 * <B4>,<7F>,<0>,<chk> Programmer BUSY , task aborted, no reply. 3119 * <p> 3120 * <B4>,<7F>,<1>,<chk> Task accepted , <E7> reply at completion. 3121 * <p> 3122 * <B4>,<7F>,<0x40>,<chk> Task accepted blind NO <E7> 3123 * reply at completion. * * Note that the <7F> code will occur in 3124 * Operations Mode Read requests if the System is not * configured for 3125 * and has no Advanced Acknowledgement detection installed.. Operations 3126 * Mode * requests can be made and executed whilst a current Service 3127 * Mode programming task is keeping * the Programming track BUSY. If a 3128 * Programming request is rejected, delay and resend the * complete 3129 * request later. Some readback operations can keep the Programming 3130 * track busy for up * to a minute. Multiple devices, throttles/PC's 3131 * etc, can share and sequentially use the * Programming track as long 3132 * as they correctly interpret the response messages. Any Slot RD * from 3133 * the master will also contain the Programmer Busy status in bit 3 of 3134 * the <TRK> byte. * * A <PCMD> value of 3135 * <00> will abort current SERVICE mode programming task and will echo 3136 * with an <E6> RD the command string that was aborted. 3137 * 3138 * <PCMD> 3139 * Programmer Command: 3140 * -------------------------- 3141 * Defined as 3142 * D7 -0 3143 * D6 -Write/Read 1= Write, 0=Read 3144 * D5 -Byte Mode 1= Byte operation, 0=Bit operation (if possible) 3145 * D4 -TY1 Programming Type select bit 3146 * D3 -TY0 Prog type select bit 3147 * D2 -Ops Mode 1=Ops Mode on Mainlines, 0=Service Mode on Programming Track 3148 * D1 -0 reserved 3149 * D0 -0-reserved 3150 * 3151 * Type codes: 3152 * ----------- * Byte Mode Ops Mode 3153 * TY1 TY0 Meaning * 1 0 0 0 Paged mode byte Read/Write on Service Track 3154 * * 1 0 0 0 Paged mode byte Read/Write on Service Track * 1 0 0 1 3155 * Direct mode byteRead/Write on Service Track * 0 0 0 1 Direct mode bit 3156 * Read/Write on Service Track * x 0 1 0 Physical Register byte 3157 * Read/Write on Service Track * x 0 1 1 Service Track- reserved 3158 * function * 1 1 0 0 Ops mode Byte program, no feedback * 1 1 0 1 Ops 3159 * mode Byte program, feedback * 0 1 0 0 Ops mode Bit program, no 3160 * feedback * 0 1 0 1 Ops mode Bit program, feedback * * 3161 * <HOPSA>Operations Mode Programming * 7 High address bits of Loco to 3162 * program, 0 if Service Mode 3163 * <p> 3164 * <LOPSA>Operations Mode Programming * 7 Low address bits of Loco to 3165 * program, 0 if Service Mode 3166 * <p> 3167 * <TRK> Normal Global Track status for this Master, * Bit 3 also is 1 3168 * WHEN Service Mode track is BUSY 3169 * <p> 3170 * <CVH> High 3 BITS of CV#, and ms bit of DATA.7 3171 * <p> 3172 * <0,0,CV9,CV8 - 0,0, D7,CV7> 3173 * <p> 3174 * <CVL> Low 7 bits of 10 bit CV address. 3175 * <p> 3176 * <0,CV6,CV5,CV4-CV3,CV2,CV1,CV0> 3177 * <p> 3178 * <DATA7>Low 7 BITS OF data to WR or RD COMPARE 3179 * <p> 3180 * <0,D6,D5,D4 - D3,D2,D1,D0> * ms bit is at CVH bit 1 position. * * 3181 * Programmer Task Final Reply: * ---------------------------- * (if saw 3182 * LACK 3183 * <B4>,<7F>,<1>,<chk> code reply at task start) 3184 * <p> 3185 * <0xE7>,<0E>,<7C>,<PCMD>,<PSTAT>,<HOPSA>,<LOPSA>,<TRK>;<CVH>,<CVL>, 3186 * <p> 3187 * <DATA7>,<0>,<0>,<CHK> * * <PSTAT> Programmer Status error flags. 3188 * Reply codes resulting from * completed task in PCMD * D7-D4 -reserved 3189 * * D3 -1= User Aborted this command * D2 -1= Failed to detect READ 3190 * Compare acknowledge response * from decoder * D1 -1= No Write 3191 * acknowledge response from decoder * D0 -1= Service Mode programming 3192 * track empty- No decoder detected * * This <E7> response is issued 3193 * whenever a Programming task is completed. It echos most of the * 3194 * request information and returns the PSTAT status code to indicate how 3195 * the task completed. * If a READ was requested <DATA7> and <CVH> 3196 * contain the returned data, if the PSTAT indicates * a successful 3197 * readback (typically =0). Note that if a Paged Read fails to detect a 3198 * * successful Page write acknowledge when first setting the Page 3199 * register, the read will be * aborted, showing no Write acknowledge 3200 * flag D1=1. * 3201 * ******************************************************************************************** 3202 */ 3203 int cvData; 3204 int cvNumber; 3205 3206 // progTask = (progTaskMsg *) msgBuf; 3207 // slot - slot number for this request - slot 124 is programmer 3208 int pcmd = l.getElement(3); // programmer command 3209 int pstat = l.getElement(4); // programmer status error flags in 3210 // reply message 3211 int hopsa = l.getElement(5); // Ops mode - 7 high address bits 3212 // of loco to program 3213 int lopsa = l.getElement(6); // Ops mode - 7 low address bits of 3214 // loco to program 3215 /* trk - track status. Note: bit 3 shows if prog track is busy */ 3216 int cvh = l.getElement(8); // hi 3 bits of CV# and msb of data7 3217 int cvl = l.getElement(9); // lo 7 bits of CV# 3218 int data7 = l.getElement(10); // 7 bits of data to program, msb 3219 // is in cvh above 3220 3221 cvData = (((cvh & LnConstants.CVH_D7) << 6) | (data7 & 0x7f)); // was 3222 // PROG_DATA 3223 cvNumber = (((((cvh & LnConstants.CVH_CV8_CV9) >> 3) | (cvh & LnConstants.CVH_CV7)) * 128) + (cvl & 0x7f)) + 1; // was 3224 // PROG_CV_NUM(progTask) 3225 3226 if (command == LnConstants.OPC_WR_SL_DATA) { 3227 /* interpret the programming mode request (to programmer) */ 3228 switch ((pcmd & (LnConstants.PCMD_MODE_MASK | LnConstants.PCMD_RW))) { 3229 case LnConstants.PAGED_ON_SRVC_TRK: 3230 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_PAGED_RD", 3231 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3232 cvNumber)); 3233 case LnConstants.PAGED_ON_SRVC_TRK | LnConstants.PCMD_RW: 3234 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_PAGED_WR", 3235 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3236 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3237 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3238 case LnConstants.DIR_BYTE_ON_SRVC_TRK: 3239 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BYTE_RD", 3240 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3241 cvNumber)); 3242 case LnConstants.DIR_BYTE_ON_SRVC_TRK | LnConstants.PCMD_RW: 3243 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BYTE_WR", 3244 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_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: 3248 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BIT_RD", 3249 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3250 cvNumber), cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3251 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true)); 3252 case LnConstants.DIR_BIT_ON_SRVC_TRK | LnConstants.PCMD_RW: 3253 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BIT_WR", 3254 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3255 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3256 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3257 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK: 3258 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE: 3259 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_REG_BYTE_RD", 3260 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3261 cvNumber)); 3262 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_RW: 3263 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: 3264 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_REG_BYTE_WR", 3265 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3266 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3267 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3268 case LnConstants.SRVC_TRK_RESERVED: 3269 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE: 3270 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_RD_RESERVED", 3271 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3272 cvNumber)); 3273 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_RW: 3274 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: 3275 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_WR_RESERVED", 3276 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3277 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3278 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3279 case LnConstants.OPS_BYTE_NO_FEEDBACK: 3280 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_RD_NO_FEEDBACK", 3281 convertToMixed(lopsa, hopsa), 3282 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3283 cvNumber)); 3284 case LnConstants.OPS_BYTE_NO_FEEDBACK | LnConstants.PCMD_RW: 3285 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_WR_NO_FEEDBACK", 3286 convertToMixed(lopsa, hopsa), 3287 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3288 cvNumber, cvData, 3289 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3290 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3291 case LnConstants.OPS_BYTE_FEEDBACK: 3292 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_RD_FEEDBACK", 3293 convertToMixed(lopsa, hopsa), 3294 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3295 cvNumber)); 3296 case LnConstants.OPS_BYTE_FEEDBACK | LnConstants.PCMD_RW: 3297 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_WR_FEEDBACK", 3298 convertToMixed(lopsa, hopsa), 3299 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3300 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3301 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3302 case LnConstants.OPS_BIT_NO_FEEDBACK: 3303 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_RD_NO_FEEDBACK", 3304 convertToMixed(lopsa, hopsa), 3305 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3306 cvNumber)); 3307 case LnConstants.OPS_BIT_NO_FEEDBACK | LnConstants.PCMD_RW: 3308 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_WR_NO_FEEDBACK", 3309 convertToMixed(lopsa, hopsa), 3310 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3311 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3312 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3313 case LnConstants.OPS_BIT_FEEDBACK: 3314 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_RD_FEEDBACK", 3315 convertToMixed(lopsa, hopsa), 3316 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3317 cvNumber)); 3318 case LnConstants.OPS_BIT_FEEDBACK | LnConstants.PCMD_RW: 3319 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_WR_FEEDBACK", 3320 convertToMixed(lopsa, hopsa), 3321 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3322 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3323 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3324 case 0: 3325 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UHLENBROCK_RD", 3326 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", 3327 cvNumber)); 3328 case LnConstants.PCMD_RW: 3329 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UHLENBROCK_WR", 3330 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3331 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3332 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3333 default: 3334 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UNKNOWN", 3335 pcmd, 3336 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3337 StringUtil.twoHexFromInt(pcmd)), 3338 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", 3339 cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3340 StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); 3341 } 3342 } else { 3343 /* interpret the programming mode response (from programmer) */ 3344 /* if we're reading the slot back, check the status 3345 * this is supposed to be the Programming task final reply 3346 * and will have the resulting status byte. 3347 */ 3348 String responseMessage = "(ODD BEHAVIOR - Default value not overwritten - report to developers!"; // NOI18N 3349 String hexMessage = ""; 3350 if (pstat != 0) { 3351 if ((pstat & LnConstants.PSTAT_USER_ABORTED) != 0) { 3352 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_USER_ABORT"); 3353 } else if ((pstat & LnConstants.PSTAT_READ_FAIL) != 0) { 3354 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_READ_COMPARE_ACK_DETECT"); 3355 } else if ((pstat & LnConstants.PSTAT_WRITE_FAIL) != 0) { 3356 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_WRITE_ACK_DETECT"); 3357 } else if ((pstat & LnConstants.PSTAT_NO_DECODER) != 0) { 3358 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_LOCO_ON_PROGRAMMING_TRACK"); 3359 } else if ((pstat & 0xF0) != 0) { 3360 if ((pstat & 0xF0) == 0x10) { 3361 // response from transponding decoder 3362 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_SUCCESS_VIA_RX4_BDL16X"); 3363 3364 } else { 3365 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_UNDECODED", 3366 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3367 StringUtil.twoHexFromInt(pstat))); 3368 hexMessage = Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 3369 } 3370 } 3371 } else { 3372 responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_SUCCEEDED"); 3373 } 3374 3375 switch ((pcmd & (LnConstants.PCMD_MODE_MASK | LnConstants.PCMD_RW))) { 3376 case LnConstants.PAGED_ON_SRVC_TRK: 3377 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_PAGED_RD", 3378 responseMessage, 3379 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3380 cvNumber, cvData, 3381 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3382 StringUtil.twoHexFromInt(cvData)), 3383 StringUtil.to8Bits(cvData, true)))+hexMessage; 3384 case LnConstants.PAGED_ON_SRVC_TRK | LnConstants.PCMD_RW: 3385 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_PAGED_WR", 3386 responseMessage, 3387 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3388 cvNumber, cvData, 3389 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3390 StringUtil.twoHexFromInt(cvData)), 3391 StringUtil.to8Bits(cvData, true)))+hexMessage; 3392 case LnConstants.DIR_BYTE_ON_SRVC_TRK: 3393 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BYTE_RD", 3394 responseMessage, 3395 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3396 cvNumber, cvData, 3397 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3398 StringUtil.twoHexFromInt(cvData)), 3399 StringUtil.to8Bits(cvData, true)))+hexMessage; 3400 case LnConstants.DIR_BYTE_ON_SRVC_TRK | LnConstants.PCMD_RW: 3401 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BYTE_WR", 3402 responseMessage, 3403 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3404 cvNumber, 3405 cvData, 3406 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3407 StringUtil.twoHexFromInt(cvData)), 3408 StringUtil.to8Bits(cvData, true)))+hexMessage; 3409 case LnConstants.DIR_BIT_ON_SRVC_TRK: 3410 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BIT_RD", 3411 responseMessage, 3412 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3413 cvNumber, cvData, 3414 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3415 StringUtil.twoHexFromInt(cvData)), 3416 StringUtil.to8Bits(cvData, true)))+hexMessage; 3417 case LnConstants.DIR_BIT_ON_SRVC_TRK | LnConstants.PCMD_RW: 3418 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BIT_WR", 3419 responseMessage, 3420 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3421 cvNumber, cvData, 3422 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3423 StringUtil.twoHexFromInt(cvData)), 3424 StringUtil.to8Bits(cvData, true)))+hexMessage; 3425 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK: 3426 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE: 3427 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_REG_BYTE_RD", 3428 responseMessage, 3429 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3430 cvNumber, cvData, 3431 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3432 StringUtil.twoHexFromInt(cvData)), 3433 StringUtil.to8Bits(cvData, true)))+hexMessage; 3434 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_RW: 3435 case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: 3436 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_REG_BYTE_WR", 3437 responseMessage, 3438 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3439 cvNumber, cvData, 3440 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3441 StringUtil.twoHexFromInt(cvData)), 3442 StringUtil.to8Bits(cvData, true)))+hexMessage; 3443 case LnConstants.SRVC_TRK_RESERVED: 3444 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE: 3445 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_RD_RESERVED", 3446 responseMessage, 3447 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3448 cvNumber, cvData, 3449 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3450 StringUtil.twoHexFromInt(cvData)), 3451 StringUtil.to8Bits(cvData, true)))+hexMessage; 3452 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_RW: 3453 case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: 3454 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_WR_RESERVED", 3455 responseMessage, 3456 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3457 cvNumber, cvData, 3458 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3459 StringUtil.twoHexFromInt(cvData)), 3460 StringUtil.to8Bits(cvData, true)))+hexMessage; 3461 case LnConstants.OPS_BYTE_NO_FEEDBACK: 3462 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_RD_NO_FEEDBACK", 3463 responseMessage, 3464 convertToMixed(lopsa, hopsa), 3465 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3466 cvNumber, cvData, 3467 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3468 StringUtil.twoHexFromInt(cvData)), 3469 StringUtil.to8Bits(cvData, true)))+hexMessage; 3470 case LnConstants.OPS_BYTE_NO_FEEDBACK | LnConstants.PCMD_RW: 3471 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_WR_NO_FEEDBACK", 3472 responseMessage, 3473 convertToMixed(lopsa, hopsa), 3474 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3475 cvNumber, cvData, 3476 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3477 StringUtil.twoHexFromInt(cvData)), 3478 StringUtil.to8Bits(cvData, true)))+hexMessage; 3479 case LnConstants.OPS_BYTE_FEEDBACK: 3480 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_RD_FEEDBACK", 3481 responseMessage, 3482 convertToMixed(lopsa, hopsa), 3483 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3484 cvNumber, cvData, 3485 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3486 StringUtil.twoHexFromInt(cvData)), 3487 StringUtil.to8Bits(cvData, true)))+hexMessage; 3488 case LnConstants.OPS_BYTE_FEEDBACK | LnConstants.PCMD_RW: 3489 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_WR_FEEDBACK", 3490 responseMessage, 3491 convertToMixed(lopsa, hopsa), 3492 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3493 cvNumber, cvData, 3494 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3495 StringUtil.twoHexFromInt(cvData)), 3496 StringUtil.to8Bits(cvData, true)))+hexMessage; 3497 case LnConstants.OPS_BIT_NO_FEEDBACK: 3498 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_RD_NO_FEEDBACK", 3499 responseMessage, 3500 convertToMixed(lopsa, hopsa), 3501 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3502 cvNumber, cvData, 3503 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3504 StringUtil.twoHexFromInt(cvData)), 3505 StringUtil.to8Bits(cvData, true)))+hexMessage; 3506 case LnConstants.OPS_BIT_NO_FEEDBACK | LnConstants.PCMD_RW: 3507 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_WR_NO_FEEDBACK", 3508 responseMessage, 3509 convertToMixed(lopsa, hopsa), 3510 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3511 cvNumber, cvData, 3512 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3513 StringUtil.twoHexFromInt(cvData)), 3514 StringUtil.to8Bits(cvData, true)))+hexMessage; 3515 case LnConstants.OPS_BIT_FEEDBACK: 3516 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_RD_FEEDBACK", 3517 responseMessage, 3518 convertToMixed(lopsa, hopsa), 3519 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3520 cvNumber, cvData, 3521 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3522 StringUtil.twoHexFromInt(cvData)), 3523 StringUtil.to8Bits(cvData, true)))+hexMessage; 3524 case LnConstants.OPS_BIT_FEEDBACK | LnConstants.PCMD_RW: 3525 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_WR_FEEDBACK", 3526 responseMessage, 3527 convertToMixed(lopsa, hopsa), 3528 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3529 cvNumber, cvData, 3530 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3531 StringUtil.twoHexFromInt(cvData)), 3532 StringUtil.to8Bits(cvData, true)))+hexMessage; 3533 case 0: 3534 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UHLENBROCK_RD", 3535 responseMessage, 3536 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3537 cvNumber, cvData, 3538 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3539 StringUtil.twoHexFromInt(cvData)), 3540 StringUtil.to8Bits(cvData, true)))+hexMessage; 3541 case LnConstants.PCMD_RW: 3542 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UHLENBROCK_WR", 3543 responseMessage, 3544 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3545 cvNumber, cvData, 3546 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3547 StringUtil.twoHexFromInt(cvData)), 3548 StringUtil.to8Bits(cvData, true)))+hexMessage; 3549 default: 3550 return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UNKNOWN", 3551 pcmd, 3552 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3553 StringUtil.twoHexFromInt(pcmd)), 3554 responseMessage, 3555 Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", 3556 cvNumber, cvData, 3557 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3558 StringUtil.twoHexFromInt(cvData)), 3559 StringUtil.to8Bits(cvData, true)))+hexMessage; 3560 } 3561 } 3562 } 3563 3564 private static String interpretCmdStnCfgSlotRdWr(LocoNetMessage l, int command) { 3565 /* 3566 * ************************************************ 3567 * Configuration slot, holding op switches 3568 * ************************************************ 3569 * <p> 3570 * NOTE: previously, this message provided specific text about the 3571 * meaning of each OpSw when it was closed. With the advent of newer 3572 * Digitrax command stations, the specific information was no longer 3573 * completely accurate. As such, this information now only shows bits as 3574 * "closed" or "thrown". 3575 */ 3576 String thrown = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); 3577 String closed = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED"); 3578 3579 String opswGroup1, opswGroup2, opswGroup3, opswGroup4, 3580 opswGroup5, opswGroup6, opswGroup7, opswGroup8; 3581 opswGroup1 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3582 1, ((l.getElement(3) & 0x01) != 0 ? closed : thrown), 3583 2, ((l.getElement(3) & 0x02) != 0 ? closed : thrown), 3584 3, ((l.getElement(3) & 0x04) != 0 ? closed : thrown), 3585 4, ((l.getElement(3) & 0x08) != 0 ? closed : thrown), 3586 5, ((l.getElement(3) & 0x10) != 0 ? closed : thrown), 3587 6, ((l.getElement(3) & 0x20) != 0 ? closed : thrown), 3588 7, ((l.getElement(3) & 0x40) != 0 ? closed : thrown), 3589 8, thrown); 3590 opswGroup2 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3591 9, ((l.getElement(4) & 0x01) != 0 ? closed : thrown), 3592 10, ((l.getElement(4) & 0x02) != 0 ? closed : thrown), 3593 11, ((l.getElement(4) & 0x04) != 0 ? closed : thrown), 3594 12, ((l.getElement(4) & 0x08) != 0 ? closed : thrown), 3595 13, ((l.getElement(4) & 0x10) != 0 ? closed : thrown), 3596 14, ((l.getElement(4) & 0x20) != 0 ? closed : thrown), 3597 15, ((l.getElement(4) & 0x40) != 0 ? closed : thrown), 3598 16, thrown); 3599 opswGroup3 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3600 17, ((l.getElement(5) & 0x01) != 0 ? closed : thrown), 3601 18, ((l.getElement(5) & 0x02) != 0 ? closed : thrown), 3602 19, ((l.getElement(5) & 0x04) != 0 ? closed : thrown), 3603 20, ((l.getElement(5) & 0x08) != 0 ? closed : thrown), 3604 21, ((l.getElement(5) & 0x10) != 0 ? closed : thrown), 3605 22, ((l.getElement(5) & 0x20) != 0 ? closed : thrown), 3606 23, ((l.getElement(5) & 0x40) != 0 ? closed : thrown), 3607 24, thrown); 3608 opswGroup4 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3609 25, ((l.getElement(6) & 0x01) != 0 ? closed : thrown), 3610 26, ((l.getElement(6) & 0x02) != 0 ? closed : thrown), 3611 27, ((l.getElement(6) & 0x04) != 0 ? closed : thrown), 3612 28, ((l.getElement(6) & 0x08) != 0 ? closed : thrown), 3613 29, ((l.getElement(6) & 0x10) != 0 ? closed : thrown), 3614 30, ((l.getElement(6) & 0x20) != 0 ? closed : thrown), 3615 31, ((l.getElement(6) & 0x40) != 0 ? closed : thrown), 3616 32, thrown); 3617 opswGroup5 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3618 33, ((l.getElement(8) & 0x01) != 0 ? closed : thrown), 3619 34, ((l.getElement(8) & 0x02) != 0 ? closed : thrown), 3620 35, ((l.getElement(8) & 0x04) != 0 ? closed : thrown), 3621 36, ((l.getElement(8) & 0x08) != 0 ? closed : thrown), 3622 37, ((l.getElement(8) & 0x10) != 0 ? closed : thrown), 3623 38, ((l.getElement(8) & 0x20) != 0 ? closed : thrown), 3624 39, ((l.getElement(8) & 0x40) != 0 ? closed : thrown), 3625 40, thrown); 3626 opswGroup6 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3627 41, ((l.getElement(9) & 0x01) != 0 ? closed : thrown), 3628 42, ((l.getElement(9) & 0x02) != 0 ? closed : thrown), 3629 43, ((l.getElement(9) & 0x04) != 0 ? closed : thrown), 3630 44, ((l.getElement(9) & 0x08) != 0 ? closed : thrown), 3631 45, ((l.getElement(9) & 0x10) != 0 ? closed : thrown), 3632 46, ((l.getElement(9) & 0x20) != 0 ? closed : thrown), 3633 47, ((l.getElement(9) & 0x40) != 0 ? closed : thrown), 3634 48, thrown); 3635 opswGroup7 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3636 49, ((l.getElement(10) & 0x01) != 0 ? closed : thrown), 3637 50, ((l.getElement(10) & 0x02) != 0 ? closed : thrown), 3638 51, ((l.getElement(10) & 0x04) != 0 ? closed : thrown), 3639 52, ((l.getElement(10) & 0x08) != 0 ? closed : thrown), 3640 53, ((l.getElement(10) & 0x10) != 0 ? closed : thrown), 3641 54, ((l.getElement(10) & 0x20) != 0 ? closed : thrown), 3642 55, ((l.getElement(10) & 0x40) != 0 ? closed : thrown), 3643 56, thrown); 3644 opswGroup8 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3645 57, ((l.getElement(11) & 0x01) != 0 ? closed : thrown), 3646 58, ((l.getElement(11) & 0x02) != 0 ? closed : thrown), 3647 59, ((l.getElement(11) & 0x04) != 0 ? closed : thrown), 3648 60, ((l.getElement(11) & 0x08) != 0 ? closed : thrown), 3649 61, ((l.getElement(11) & 0x10) != 0 ? closed : thrown), 3650 62, ((l.getElement(11) & 0x20) != 0 ? closed : thrown), 3651 63, ((l.getElement(11) & 0x40) != 0 ? closed : thrown), 3652 64, thrown); 3653 return Bundle.getMessage(((command == LnConstants.OPC_WR_SL_DATA) 3654 ? "LN_MSG_SLOT_CMD_STN_CFG_WRITE_REQ" 3655 : "LN_MSG_SLOT_CMD_STN_CFG_READ_REPORT"), 3656 opswGroup1, opswGroup2, opswGroup3, opswGroup4, 3657 opswGroup5, opswGroup6, opswGroup7, opswGroup8); 3658 3659 } 3660 3661 private static String interpretCmdStnExtCfgSlotRdWr(LocoNetMessage l, int command) { 3662 /* 3663 * ************************************************ 3664 * Extended Configuration slot, holding op switches 3665 * ************************************************ 3666 */ 3667 String thrown = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); 3668 String closed = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED"); 3669 3670 String opswGroup1, opswGroup2, opswGroup3, opswGroup4, 3671 opswGroup5, opswGroup6, opswGroup7, opswGroup8; 3672 opswGroup1 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3673 65, ((l.getElement(3) & 0x01) != 0 ? closed : thrown), 3674 66, ((l.getElement(3) & 0x02) != 0 ? closed : thrown), 3675 67, ((l.getElement(3) & 0x04) != 0 ? closed : thrown), 3676 68, ((l.getElement(3) & 0x08) != 0 ? closed : thrown), 3677 69, ((l.getElement(3) & 0x10) != 0 ? closed : thrown), 3678 70, ((l.getElement(3) & 0x20) != 0 ? closed : thrown), 3679 71, ((l.getElement(3) & 0x40) != 0 ? closed : thrown), 3680 72, thrown); 3681 opswGroup2 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3682 73, ((l.getElement(4) & 0x01) != 0 ? closed : thrown), 3683 74, ((l.getElement(4) & 0x02) != 0 ? closed : thrown), 3684 75, ((l.getElement(4) & 0x04) != 0 ? closed : thrown), 3685 76, ((l.getElement(4) & 0x08) != 0 ? closed : thrown), 3686 77, ((l.getElement(4) & 0x10) != 0 ? closed : thrown), 3687 78, ((l.getElement(4) & 0x20) != 0 ? closed : thrown), 3688 79, ((l.getElement(4) & 0x40) != 0 ? closed : thrown), 3689 80, thrown); 3690 opswGroup3 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3691 81, ((l.getElement(5) & 0x01) != 0 ? closed : thrown), 3692 82, ((l.getElement(5) & 0x02) != 0 ? closed : thrown), 3693 83, ((l.getElement(5) & 0x04) != 0 ? closed : thrown), 3694 84, ((l.getElement(5) & 0x08) != 0 ? closed : thrown), 3695 85, ((l.getElement(5) & 0x10) != 0 ? closed : thrown), 3696 86, ((l.getElement(5) & 0x20) != 0 ? closed : thrown), 3697 87, ((l.getElement(5) & 0x40) != 0 ? closed : thrown), 3698 88, thrown); 3699 opswGroup4 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3700 89, ((l.getElement(6) & 0x01) != 0 ? closed : thrown), 3701 90, ((l.getElement(6) & 0x02) != 0 ? closed : thrown), 3702 91, ((l.getElement(6) & 0x04) != 0 ? closed : thrown), 3703 92, ((l.getElement(6) & 0x08) != 0 ? closed : thrown), 3704 93, ((l.getElement(6) & 0x10) != 0 ? closed : thrown), 3705 94, ((l.getElement(6) & 0x20) != 0 ? closed : thrown), 3706 95, ((l.getElement(6) & 0x40) != 0 ? closed : thrown), 3707 96, thrown); 3708 opswGroup5 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3709 97, ((l.getElement(8) & 0x01) != 0 ? closed : thrown), 3710 98, ((l.getElement(8) & 0x02) != 0 ? closed : thrown), 3711 99, ((l.getElement(8) & 0x04) != 0 ? closed : thrown), 3712 100, ((l.getElement(8) & 0x08) != 0 ? closed : thrown), 3713 101, ((l.getElement(8) & 0x10) != 0 ? closed : thrown), 3714 102, ((l.getElement(8) & 0x20) != 0 ? closed : thrown), 3715 103, ((l.getElement(8) & 0x40) != 0 ? closed : thrown), 3716 104, thrown); 3717 opswGroup6 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3718 105, ((l.getElement(9) & 0x01) != 0 ? closed : thrown), 3719 106, ((l.getElement(9) & 0x02) != 0 ? closed : thrown), 3720 107, ((l.getElement(9) & 0x04) != 0 ? closed : thrown), 3721 108, ((l.getElement(9) & 0x08) != 0 ? closed : thrown), 3722 109, ((l.getElement(9) & 0x10) != 0 ? closed : thrown), 3723 110, ((l.getElement(9) & 0x20) != 0 ? closed : thrown), 3724 111, ((l.getElement(9) & 0x40) != 0 ? closed : thrown), 3725 112, thrown); 3726 opswGroup7 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3727 113, ((l.getElement(10) & 0x01) != 0 ? closed : thrown), 3728 114, ((l.getElement(10) & 0x02) != 0 ? closed : thrown), 3729 115, ((l.getElement(10) & 0x04) != 0 ? closed : thrown), 3730 116, ((l.getElement(10) & 0x08) != 0 ? closed : thrown), 3731 117, ((l.getElement(10) & 0x10) != 0 ? closed : thrown), 3732 118, ((l.getElement(10) & 0x20) != 0 ? closed : thrown), 3733 119, ((l.getElement(10) & 0x40) != 0 ? closed : thrown), 3734 120, thrown); 3735 opswGroup8 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 3736 121, ((l.getElement(11) & 0x01) != 0 ? closed : thrown), 3737 122, ((l.getElement(11) & 0x02) != 0 ? closed : thrown), 3738 123, ((l.getElement(11) & 0x04) != 0 ? closed : thrown), 3739 124, ((l.getElement(11) & 0x08) != 0 ? closed : thrown), 3740 125, ((l.getElement(11) & 0x10) != 0 ? closed : thrown), 3741 126, ((l.getElement(11) & 0x20) != 0 ? closed : thrown), 3742 127, ((l.getElement(11) & 0x40) != 0 ? closed : thrown), 3743 128, thrown); 3744 return Bundle.getMessage(((command == LnConstants.OPC_WR_SL_DATA) 3745 ? "LN_MSG_SLOT_CMD_STN_EXT_CFG_WRITE_REQ" 3746 : "LN_MSG_SLOT_CMD_STN_EXT_CFG_READ_REPORT"), 3747 opswGroup1, opswGroup2, opswGroup3, opswGroup4, 3748 opswGroup5, opswGroup6, opswGroup7, opswGroup8); 3749 } 3750 3751 private static String interpretStandardSlotRdWr(LocoNetMessage l, int id1, int id2, int command, int slot) { 3752 /* 3753 * ************************************************ 3754 * normal slot read/write message - see info above * 3755 * ************************************************ 3756 */ 3757 int trackStatus = l.getElement(7); // track status 3758 int stat = l.getElement(3); // slot status 3759 int adr = l.getElement(4); // loco address 3760 int spd = l.getElement(5); // command speed 3761 int dirf = l.getElement(6); // direction and F0-F4 bits 3762 String[] dirf0_4 = interpretF0_F4toStrings(dirf); 3763 int ss2 = l.getElement(8); // slot status 2 (tells how to use 3764 // ID1/ID2 & ADV Consist) 3765 int adr2 = l.getElement(9); // loco address high 3766 int snd = l.getElement(10); // Sound 1-4 / F5-F8 3767 String[] sndf5_8 = interpretF5_F8toStrings(snd); 3768 3769 String locoAdrStr = figureAddressIncludingAliasing(adr, adr2, ss2, id1, id2); 3770 return Bundle.getMessage(((command == LnConstants.OPC_WR_SL_DATA) 3771 ? "LN_MSG_SLOT_LOCO_INFO_WRITE" 3772 : "LN_MSG_SLOT_LOCO_INFO_READ"), 3773 slot, 3774 locoAdrStr, 3775 LnConstants.CONSIST_STAT(stat), 3776 LnConstants.LOCO_STAT(stat), 3777 LnConstants.DEC_MODE(stat), 3778 directionOfTravelString((dirf & LnConstants.DIRF_DIR) == 0), 3779 spd, // needs re-interpretation for some cases of slot consisting state 3780 dirf0_4[0], 3781 dirf0_4[1], 3782 dirf0_4[2], 3783 dirf0_4[3], 3784 dirf0_4[4], 3785 sndf5_8[0], 3786 sndf5_8[1], 3787 sndf5_8[2], 3788 sndf5_8[3], 3789 trackStatusByteToString(trackStatus), 3790 Bundle.getMessage("LN_MSG_SLOT_HELPER_SS2_SIMPLE", 3791 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 3792 StringUtil.twoHexFromInt(ss2))), 3793 Bundle.getMessage("LN_MSG_SLOT_HELPER_ID1_ID2_AS_THROTTLE_ID", 3794 idString(id1, id2))); 3795 } 3796 3797 private static String interpretOpcPanelResponse(LocoNetMessage l) { 3798 switch (l.getElement(1)) { 3799 case 0x12: { 3800 // Bit 3 (0x08 in hex) is set by every UR-92 we've ever captured. 3801 // The hypothesis is this indicates duplex enabled, but this has 3802 // not been confirmed with Digitrax. 3803 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR92", 3804 l.getElement(3) & 0x07, 3805 ((l.getElement(3) & 0x08) == 0x08 3806 ? Bundle.getMessage("LN_MSG_HELPER_D7_UR92_DUPLEX") 3807 : "")); 3808 } 3809 case 0x17: { 3810 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR90", 3811 l.getElement(3) & 0x07); 3812 } 3813 case 0x1F: { 3814 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR91", 3815 l.getElement(3) & 0x07); 3816 } 3817 case 0x22: { 3818 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR93", 3819 l.getElement(3) & 0x07); 3820 } 3821 case 0x14: { 3822 return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR90X", 3823 l.getElement(3) & 0x07); 3824 } 3825 default: { 3826 return ""; 3827 } 3828 } 3829 } 3830 3831 private static String interpretOpcLissyUpdate(LocoNetMessage l) { 3832 /* 3833 * OPC_LISSY_UPDATE 0xE4 3834 * 3835 * LISSY is an automatic train detection system made by Uhlenbrock. 3836 * All documentation appears to be in German. 3837 * Also used by LocoIO-based RFID readers eg. GCA51 3838 */ 3839 log.debug("Message from LISSY: {}", Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString())); 3840 switch (l.getElement(1)) { 3841 case 0x08: // Format LISSY message 3842 int unit = (l.getElement(4) & 0x7F); 3843 if ((l.getElement(3) & 0x40) != 0) { // Loco movement 3844 int category = l.getElement(2) + 1; 3845 int address = (l.getElement(6) & 0x7F) + 128 * (l.getElement(5) & 0x7F); 3846 return Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_LOCO_MOVEMENT", 3847 unit, 3848 Integer.toString(address), 3849 Integer.toString(category), 3850 ((l.getElement(3) & 0x20) == 0 3851 ? Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_DIRECTION_NORTH") 3852 : Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_DIRECTION_SOUTH"))); 3853 } else { // other messages 3854 switch (l.getElement(2)) { 3855 case 0x00: // Loco speed 3856 int speed = (l.getElement(6) & 0x7F) + 128 * (l.getElement(5) & 0x7F); 3857 return Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_LOCO_SPEED", 3858 unit, 3859 Integer.toString(speed)); 3860 3861 case 0x01: // Block status 3862 return Bundle.getMessage("LN_MSG_LISSY_BLOCK_REPORT", 3863 unit, 3864 ((l.getElement(6) & 0x01) == 0 3865 ? Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_BLOCK_FREE") 3866 : Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_BLOCK_OCCUPIED"))); 3867 default: 3868 break; 3869 } 3870 } 3871 break; 3872 3873 case 0x0A: // Format special message 3874 int element = l.getElement(2) * 128 + l.getElement(3); 3875 int stat1 = l.getElement(5); 3876 int stat2 = l.getElement(6); 3877 String status; 3878 switch (stat1 & 0x30) { 3879 case 0x30: 3880 status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_BOTH_RES"); 3881 break; 3882 case 0x10: 3883 status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_AX_RES"); 3884 break; 3885 case 0x20: 3886 status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_XA_RES"); 3887 break; 3888 default: 3889 status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_NO_RES"); 3890 break; 3891 } 3892 3893 return Bundle.getMessage("LN_MSG_SE_REPORT", 3894 (element + 1), element, 3895 l.getElement(7), l.getElement(8), 3896 status, 3897 Bundle.getMessage(((stat2 & 0x01) != 0) 3898 ? "LN_MSG_SWITCH_STATE_THROWN" 3899 : "LN_MSG_SWITCH_STATE_CLOSED"), 3900 Bundle.getMessage(((stat1 & 0x01) != 0) 3901 ? "LN_MSG_SE_REPORT_HELPER_OCCUPIED" 3902 : "LN_MSG_SE_REPORT_HELPER_UNOCCUPIED")); 3903 case 0x09: 3904 if (l.getElement(4) == 0x00) { 3905 return Bundle.getMessage("LN_MSG_UNRECOGNIZED_SIG_STATE_REPORT_MAY_BE_FROM_CML_HW")+ 3906 Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()); 3907 } 3908 break; 3909 case 0x0C: 3910 case 0x0E: 3911 if (l.getElement(2) == 0x41 ) { 3912 // RFID-5 or 7 reader report 3913 // RFID-7 reader report [E4 0E 41 00 02 04 3A 4B 4A 60 60 01 50 38] 3914 // elem[3] = sensorAddr >> 7; addr.high 3915 // elem[4] = sensorAddr & 0x7F; addr.low 3916 StringBuilder tg = new StringBuilder(); 3917 int max = l.getElement(1) - 2; // GCA51 RFID-7 elem(1) = size = 0x0E; RFID-5 elem(1) = size = 0x0C 3918 int rfidHi = l.getElement(max); // MSbits are transmitted via element(max) 3919 for (int j = 5; j < max; j++) { 3920 int shift = j-5; 3921 int hi = 0x0; 3922 if(((rfidHi >> shift) & 0x1) == 1) hi = 0x80; 3923 tg.append(String.format("%1$02X", l.getElement(j) + hi)); 3924 } 3925 int portAddress = l.getElement(3) << 7 | l.getElement(4); 3926 int msgType = 7; 3927 if (max == 9) msgType = 5; 3928 return Bundle.getMessage("LN_MSG_LISSY_RFIDX_REPORT", msgType, portAddress, tg.toString()); 3929 } 3930 break; 3931 default: 3932 break; 3933 } 3934 return ""; 3935 } 3936 3937 private static String interpretOpcImmPacket(LocoNetMessage l) { 3938 String result; 3939 3940 /* 3941 * OPC_IMM_PACKET 0xED 3942 */ 3943 if (l.getElement(1) == 0x0F) { // length = 15 3944 // check for a specific type - Uhlenbrock LNSV Programming messages format 3945 result = interpretLncvMessage(l); 3946 if (!result.isEmpty()) { 3947 return result; 3948 } 3949 3950 return ""; 3951 } 3952 3953 /* Else: 3954 * OPC_IMM_PACKET 0xED ;SEND n-byte packet immediate LACK 3955 * ; Follow on message: LACK 3956 * ; <0xED>,<0B>,<7F>,<REPS>,<DHI>,<IM1>,<IM2>, 3957 * ; <IM3>,<IM4>,<IM5>,<CHK> 3958 * ; <DHI>=<0,0,1,IM5.7-IM4.7,IM3.7,IM2.7,IM1.7> 3959 * ; <REPS> D4,5,6=#IM bytes, 3960 * ; D3=0(reserved); 3961 * ; D2,1,0=repeat CNT 3962 * ; IF Not limited MASTER then 3963 * ; LACK=<B4>,<7D>,<7F>,<chk> if CMD ok 3964 * ; IF limited MASTER then Lim Masters respond 3965 * ; with <B4>,<7E>,<lim adr>,<chk> 3966 * ; IF internal buffer BUSY/full respond 3967 * ; with <B4>,<7D>,<0>,<chk> 3968 * ; (NOT IMPLEMENTED IN DT200) 3969 * 3970 * This sends a raw NMRA packet across the LocoNet. 3971 * 3972 * Page 11 of LocoNet Personal Edition v1.0. 3973 * 3974 * Decodes for the F9-F28 functions taken from the NMRA standards and 3975 * coded by Leo Bicknell. 3976 */ 3977 3978 // sendPkt = (sendPktMsg *) msgBuf; 3979 int val7f = l.getElement(2); 3980 /* fixed value of 0x7f */ 3981 3982 int reps = l.getElement(3); 3983 /* repeat count */ 3984 3985 int dhi = l.getElement(4); 3986 /* high bits of data bytes */ 3987 3988 int im1 = l.getElement(5); 3989 int im2 = l.getElement(6); 3990 int im3 = l.getElement(7); 3991 int im4 = l.getElement(8); 3992 int im5 = l.getElement(9); 3993 int mobileDecoderAddress = -999; 3994 int nmraInstructionType = -999; 3995 int nmraSubInstructionType = -999; 3996 int playableWhistleLevel = -999; 3997 3998 // see if it really is a 'Send Packet' as defined in LocoNet PE 3999 if ((val7f == 0x7f) && (l.getElement(1) == 0x0B)) { 4000 int len = ((reps & 0x70) >> 4); 4001 if (len < 2) { 4002 return ""; // no valid NMRA packets of less than 2 bytes. 4003 } 4004 // duplication of packet data as packetInt was deemed necessary 4005 // due to issues with msBit loss when converting from "byte" to 4006 // integral forms 4007 byte[] packet = new byte[len]; 4008 int[] packetInt = new int[len]; 4009 packet[0] = (byte) (im1 + ((dhi & 0x01) != 0 ? 0x80 : 0)); 4010 packetInt[0] = (im1 + ((dhi & 0x01) != 0 ? 0x80 : 0)); 4011 4012 // len >= 2 always true at this point 4013 packet[1] = (byte) (im2 + ((dhi & 0x02) != 0 ? 0x80 : 0)); 4014 packetInt[1] = (im2 + ((dhi & 0x02) != 0 ? 0x80 : 0)); 4015 4016 if (len >= 3) { 4017 packet[2] = (byte) (im3 + ((dhi & 0x04) != 0 ? 0x80 : 0)); 4018 packetInt[2] = (im3 + ((dhi & 0x04) != 0 ? 0x80 : 0)); 4019 } 4020 if (len >= 4) { 4021 packet[3] = (byte) (im4 + ((dhi & 0x08) != 0 ? 0x80 : 0)); 4022 packetInt[3] = (im4 + ((dhi & 0x08) != 0 ? 0x80 : 0)); 4023 } 4024 if (len >= 5) { 4025 packet[4] = (byte) (im5 + ((dhi & 0x10) != 0 ? 0x80 : 0)); 4026 packetInt[4] = (im5 + ((dhi & 0x10) != 0 ? 0x80 : 0)); 4027 } 4028 4029 int address; 4030 // compute some information which is useful for decoding 4031 // the "Playable" whistle message 4032 // Information reverse-engineered by B. Milhaupt and used with permission 4033 if ((packetInt[0] & 0x80) == 0x0) { 4034 // immediate packet addresses a 7-bit multi-function (mobile) decoder 4035 mobileDecoderAddress = packetInt[0]; 4036 nmraInstructionType = (packetInt[1] & 0xE) >> 5; 4037 nmraSubInstructionType = (packetInt[1] & 0x1f); 4038 if ((nmraSubInstructionType == 0x1d) && (packetInt[2] == 0x7f)) { 4039 playableWhistleLevel = packetInt[3]; 4040 } 4041 } else if ((packetInt[0] & 0xC0) == 0xC0) { 4042 // immediate packet addresses a 14-bit multi-function (mobile) decoder 4043 mobileDecoderAddress = ((packetInt[0] & 0x3F) << 8) + packetInt[1]; 4044 nmraInstructionType = (packetInt[2] & 0xE0) >> 5; 4045 nmraSubInstructionType = (packetInt[2] & 0x1f); 4046 if ((nmraSubInstructionType == 0x1d) && (packetInt[3] == 0x7f)) { 4047 playableWhistleLevel = packetInt[4]; 4048 } 4049 } 4050 // immediate packet not addressed to a multi-function (mobile) decoder 4051 if ((mobileDecoderAddress >= 0) 4052 && (nmraInstructionType == 1) 4053 && (nmraSubInstructionType == 0x1D)) { 4054 // the "Playable" whistle message 4055 // Information reverse-engineered by B. Milhaupt and used with permission 4056 return Bundle.getMessage("LN_MSG_PLAYABLE_WHISTLE_CONTROL", 4057 Integer.toString(mobileDecoderAddress), 4058 playableWhistleLevel, 4059 (reps & 0x7)); 4060 } 4061 4062 // F9-F28 w/a long address. 4063 if ((packetInt[0] & 0xC0) == 0xC0) { 4064 address = ((packetInt[0] & 0x3F) << 8) + packetInt[1]; 4065 4066 if ((packetInt[2] & 0xFF) == 0xDF) { 4067 // Functions 21-28 4068 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F21_TO_F28", 4069 Integer.toString(address), 4070 Bundle.getMessage(((packetInt[3] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4071 Bundle.getMessage(((packetInt[3] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4072 Bundle.getMessage(((packetInt[3] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4073 Bundle.getMessage(((packetInt[3] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4074 Bundle.getMessage(((packetInt[3] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4075 Bundle.getMessage(((packetInt[3] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4076 Bundle.getMessage(((packetInt[3] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4077 Bundle.getMessage(((packetInt[3] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4078 } else if ((packetInt[2] & 0xFF) == 0xDE) { 4079 // Functions 13-20 4080 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F13_TO_F20", 4081 Integer.toString(address), 4082 Bundle.getMessage((((packetInt[3] & 0x01) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4083 Bundle.getMessage((((packetInt[3] & 0x02) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4084 Bundle.getMessage((((packetInt[3] & 0x04) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4085 Bundle.getMessage((((packetInt[3] & 0x08) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4086 Bundle.getMessage((((packetInt[3] & 0x10) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4087 Bundle.getMessage((((packetInt[3] & 0x20) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4088 Bundle.getMessage((((packetInt[3] & 0x40) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4089 Bundle.getMessage((((packetInt[3] & 0x80) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4090 } else if ((packetInt[2] & 0xF0) == 0xA0) { 4091 // Functions 9-12 4092 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F9_TO_F12", 4093 Integer.toString(address), 4094 Bundle.getMessage((((packetInt[2] & 0x01) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4095 Bundle.getMessage((((packetInt[2] & 0x02) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4096 Bundle.getMessage((((packetInt[2] & 0x04) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4097 Bundle.getMessage((((packetInt[2] & 0x08) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4098 } else { 4099 return Bundle.getMessage("LN_MSG_OPC_IMM_PKT_GENERIC", 4100 ((reps & 0x70) >> 4), 4101 (reps & 0x07), 4102 reps, 4103 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4104 StringUtil.twoHexFromInt(dhi)), 4105 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4106 StringUtil.twoHexFromInt(im1)), 4107 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4108 StringUtil.twoHexFromInt(im2)), 4109 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4110 StringUtil.twoHexFromInt(im3)), 4111 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4112 StringUtil.twoHexFromInt(im4)), 4113 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4114 StringUtil.twoHexFromInt(im5)), 4115 NmraPacket.format(packet)); 4116 } 4117 } else { // F9-F28 w/a short address. 4118 address = packetInt[0]; 4119 if ((packetInt[1] & 0xFF) == 0xDF) { 4120 // Functions 21-28 4121 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F21_TO_F28", 4122 address, 4123 Bundle.getMessage(((packetInt[2] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4124 Bundle.getMessage(((packetInt[2] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4125 Bundle.getMessage(((packetInt[2] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4126 Bundle.getMessage(((packetInt[2] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4127 Bundle.getMessage(((packetInt[2] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4128 Bundle.getMessage(((packetInt[2] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4129 Bundle.getMessage(((packetInt[2] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4130 Bundle.getMessage(((packetInt[2] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4131 4132 } else if ((packetInt[1] & 0xFF) == 0xDE) { 4133 // Functions 13-20 4134 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F13_TO_F20", 4135 address, 4136 Bundle.getMessage(((packetInt[2] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4137 Bundle.getMessage(((packetInt[2] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4138 Bundle.getMessage(((packetInt[2] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4139 Bundle.getMessage(((packetInt[2] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4140 Bundle.getMessage(((packetInt[2] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4141 Bundle.getMessage(((packetInt[2] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4142 Bundle.getMessage(((packetInt[2] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4143 Bundle.getMessage(((packetInt[2] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4144 } else if ((packetInt[1] & 0xF0) == 0xA0) { 4145 // Functions 9-12 4146 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F9_TO_F12", 4147 address, 4148 Bundle.getMessage(((packetInt[1] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4149 Bundle.getMessage(((packetInt[1] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4150 Bundle.getMessage(((packetInt[1] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4151 Bundle.getMessage(((packetInt[1] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4152 } else { 4153 // Unknown 4154 if ((packetInt[0] & 0xC0) == 0x80 ) { 4155 /* 4156 * 2.4.7 Extended Decoder Control Packet address for 4157 * operations mode programming and Aspect Setting. 4158 * 10AAAAAA 0 0AAA0AA1 4159 * Packets 3 bytes in length are Accessory Aspect packets (2.4.3) 4160 * {preamble} 10AAAAAA 0 0AAA0AA1 0 XXXXXXXX 0 EEEEEEEE 1 4161 * Please note that the use of 0 in bit 3 of byte 2 is to 4162 * ensure that this packet cannot be confused with the 4163 * legacy accessory-programming packets. The resulting packet 4164 * would be: 4165 * {preamble} 10AAAAAA 0 0AAA0AA1 0 (1110CCVV 0 VVVVVVVV 0 DDDDDDDD) 0 EEEEEEEE 1 4166 * A5 43 00 4167 * 10100101 0010011 00000000 4168 * Signal Decoder Address (Configuration Variable Access Instruction) Error Byte 4169 */ 4170 log.debug("Is an Extended Accessory Ops-mode CV access or Set Signal Aspect"); 4171 log.debug(" LocoNet message: {} {} {} {} {} {} {} {} {} {} {}", 4172 StringUtil.twoHexFromInt(l.getElement(0)), 4173 StringUtil.twoHexFromInt(l.getElement(1)), 4174 StringUtil.twoHexFromInt(l.getElement(2)), 4175 StringUtil.twoHexFromInt(l.getElement(3)), 4176 StringUtil.twoHexFromInt(l.getElement(4)), 4177 StringUtil.twoHexFromInt(l.getElement(5)), 4178 StringUtil.twoHexFromInt(l.getElement(6)), 4179 StringUtil.twoHexFromInt(l.getElement(7)), 4180 StringUtil.twoHexFromInt(l.getElement(8)), 4181 StringUtil.twoHexFromInt(l.getElement(9)), 4182 StringUtil.twoHexFromInt(l.getElement(10)) 4183 ); 4184 if (packetInt.length == 5) { 4185 log.debug(" NMRA packet: {} {} {} {} {}", 4186 StringUtil.twoHexFromInt(packetInt[0]), 4187 StringUtil.twoHexFromInt(packetInt[1]), 4188 StringUtil.twoHexFromInt(packetInt[2]), 4189 StringUtil.twoHexFromInt(packetInt[3]), 4190 StringUtil.twoHexFromInt(packetInt[4]) 4191 ); 4192 } else if (packetInt.length == 3) { 4193 log.debug(" NMRA packet: {} {} {}", 4194 StringUtil.twoHexFromInt(packetInt[0]), 4195 StringUtil.twoHexFromInt(packetInt[1]), 4196 StringUtil.twoHexFromInt(packetInt[2]) 4197 ); 4198 } else { 4199 log.warn(" Unknown Extended Accessory Packet length [{}])",packetInt.length); 4200 return ""; 4201 } 4202 if ((packetInt[2] & 0xF0) == 0xF0) { 4203 /* 4204 * 2.3.7.2 Configuration Variable Access Instruction - Short Form 4205 * This instruction has the format of: 4206 * {instruction bytes} = 1111GGGG 0 DDDDDDDD 0 DDDDDDDD 4207 * The 8-bit data DDDDDDDD is placed in the configuration 4208 * variable identified by GGGG according 4209 */ 4210 log.debug("Is an Short-form Extended Accessory Ops-mode CV access"); 4211 } 4212 if ((packetInt[2] & 0xf0) == 0xe0) { 4213 /* 4214 * 2.3.7.3 Configuration Variable Access Instruction - Long Form 4215 * The long form allows the direct manipulation of all CVs8. This 4216 * instruction is valid both when the Digital Decoder has its 4217 * long address active and short address active. Digital Decoders 4218 * shall not act on this instruction if sent to its consist 4219 * address. 4220 * 4221 * The format of the instructions using Direct CV 4222 * addressing is: 4223 * {instruction bytes}= 1110GGVV 0 VVVVVVVV 0 DDDDDDDD 4224 * 4225 * The actual Configuration Variable desired is selected 4226 * via the 10-bit address with the 2-bit address (VV) in 4227 * the first data byte being the most significant bits of 4228 * the address. The Configuration variable being addressed 4229 * is the provided 10-bit address plus 1. For example, to 4230 * address CV1 the 10 bit address is "00 00000000". 4231 * 4232 * The defined values for Instruction type (CC) are: 4233 * GG=00 Reserved for future use 4234 * GG=01 Verify byte 505 4235 * GG=11 Write byte 4236 * GG=10 Bit manipulation 4237 * */ 4238 int addr = getExtendedAccessoryAddressFromDCCPacket(packetInt); 4239 log.debug("Long-format Extended Accessory Ops-mode CV access: Extended Acceccory Address {}", addr); 4240 int cvnum = 1 + ((packetInt[2] & 0x03) << 8) + (packetInt[3] & 0xff); 4241 switch (packetInt[2] & 0x0C) { 4242 case 0x04: 4243 // GG=01 Verify byte 4244 /* 4245 * Type = "01" VERIFY BYTE 4246 * 4247 * The contents of the Configuration Variable as indicated 4248 * by the 10-bit address are compared with the data byte 4249 * (DDDDDDDD). If the decoder successfully receives this 4250 * packet and the values are identical, the Digital 4251 * Decoder shall respond with the contents of the CV as 4252 * the Decoder Response Transmission, if enabled. 4253 */ 4254 log.debug("CV # {}, Verify Byte: {}", cvnum, packetInt[4]); 4255 return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_VERIFY", 4256 addr, cvnum, packetInt[4] ); 4257 case 0x08: 4258 // GG=10 Bit manipulation 4259 /* 4260 * Type = "10" BIT MANIPULATION. 4261 * 4262 * The bit manipulation instructions use a special 4263 * format for the data byte (DDDDDDDD): 111FDBBB, where 4264 * BBB represents the bit position within the CV, 4265 * D contains the value of the bit to be verified 4266 * or written, and F describes whether the 4267 * operation is a verify bit or a write bit 4268 * operation. 4269 * 4270 * F = "1" : WRITE BIT 4271 * F = "0" : VERIFY BIT 4272 * The VERIFY BIT and WRITE BIT instructions operate 4273 * in a manner similar to the VERIFY BYTE and WRITE 4274 * BYTE instructions (but operates on a single bit). 4275 * Using the same criteria as the VERIFY BYTE 4276 * instruction, an operations mode acknowledgment 4277 * will be generated in response to a VERIFY BIT 4278 * instruction if appropriate. Using the same 4279 * criteria as the WRITE BYTE instruction, a 4280 * configuration variable access acknowledgment 4281 * will be generated in response to the second 4282 * identical WRITE BIT instruction if appropriate. 4283 */ 4284 if ((packetInt[4]& 0xE0) != 0xE0) { 4285 break; 4286 } 4287 log.debug("CV # {}, Bit Manipulation: {} {} (of bits 0-7) with {}", 4288 cvnum, (packetInt[4] & 0x10) == 0x10 ? "Write" : "Verify", 4289 (packetInt[4] & 0x7), 4290 (packetInt[4] >> 3) & 0x1); 4291 4292 // "Extended Accessory Decoder CV Bit {} bit, 4293 // Address {}, CV {}, bit # {} (of bits 0-7) 4294 // with value {}.\n" 4295 return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_BIT_ACCESS", 4296 ((packetInt[4] & 0x10) == 0x10 ? "Write" : "Verify"), 4297 addr, cvnum, (packetInt[4] & 0x7), 4298 ((packetInt[4] >>3) & 0x1) ); 4299 case 0x0c: 4300 // GG=11 Write byte 4301 /* 4302 * Type = "11" WRITE BYTE 4303 * 4304 * The contents of the Configuration Variable as indicated by the 10-bit 4305 * address are replaced by the data byte (DDDDDDDD). Two identical 4306 * packets are needed before the decoder shall modify a 4307 * configuration variable. These two packets need not be back 4308 * to back on the track. However any other packet to the same 4309 * decoder will invalidate the write operation. (This includes 4310 * broadcast packets.) If the decoder successfully receives 4311 * this second identical packet, it shall respond with a 4312 * configuration variable access acknowledgment. 4313 */ 4314 log.debug("CV # {}, Write Byte: {}", cvnum, packetInt[4]); 4315 return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_WRITE", 4316 addr, cvnum, packetInt[4] ); 4317 case 0x0: 4318 default: 4319 // GG=00 Reserved for future use 4320 log.debug("CV # {}, Reserved (GG=0); {}", cvnum, packetInt[4]); 4321 } 4322 } else if (packetInt.length == 3) { 4323 int addr = getExtendedAccessoryAddressFromDCCPacket(packetInt); 4324 return Bundle.getMessage("LN_MSG_EXTEND_ACCY_SET_ASPECT", 4325 addr, addr - 4, packetInt[2] ); 4326 } 4327 } 4328 return Bundle.getMessage("LN_MSG_OPC_IMM_PKT_GENERIC", 4329 ((reps & 0x70) >> 4), 4330 (reps & 0x07), 4331 reps, 4332 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4333 StringUtil.twoHexFromInt(dhi)), 4334 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4335 StringUtil.twoHexFromInt(im1)), 4336 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4337 StringUtil.twoHexFromInt(im2)), 4338 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4339 StringUtil.twoHexFromInt(im3)), 4340 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4341 StringUtil.twoHexFromInt(im4)), 4342 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4343 StringUtil.twoHexFromInt(im5)), 4344 NmraPacket.format(packet)); 4345 } 4346 } // else { // F9-F28 w/a short address. 4347 } else if (l.getElement(1) == 0x1F) { 4348 if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42 4349 && l.getElement(6) != 0x5E && l.getElement(10) == 0x70 && l.getElement(11) == 0x00 && l.getElement(15) == 0x10) { 4350 // Uhlenbrock IB-COM / Intellibox I and II read or write CV value on programming track 4351 String cv = Integer.toString(l.getElement(8) * 256 + ((l.getElement(5) & 0x02) * 64) + l.getElement(7)); 4352 int val = l.getElement(9) + 16 * (l.getElement(5) & 0x08); 4353 switch (l.getElement(6)) { 4354 case 0x6C: 4355 return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_REG_MODE_FROM_PT", cv); 4356 case 0x6D: 4357 return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_REG_MODE_FROM_PT", cv); 4358 case 0x6E: 4359 return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_PAGED_MODE_FROM_PT", cv); 4360 case 0x6F: 4361 return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_PAGED_MODE_FROM_PT", cv); 4362 case 0x71: 4363 return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_DIRECT_BYTE_MODE_FROM_PT", 4364 cv, val); 4365 case 0x70: // observed on Intellibox II, even though it does not work on IB-COM 4366 case 0x72: 4367 return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_DIRECT_BYTE_MODE_FROM_PT", cv); 4368 default: 4369 break; 4370 } 4371 return ""; 4372 } else if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42 4373 && l.getElement(6) == 0x5E) { 4374 // Uhlenbrock IB-COM / Intellibox I and II write CV value on main track 4375 int addr = l.getElement(8) * 256 + ((l.getElement(5) & 0x02) * 64) + l.getElement(7); 4376 String cv = Integer.toString(l.getElement(11) * 256 + ((l.getElement(5) & 0x08) << 4) + l.getElement(9)); 4377 int val = ((l.getElement(10) & 0x02) << 6) + l.getElement(12); 4378 return Bundle.getMessage("LN_MSG_UHLEN_CV_OPS_MODE_WRITE", 4379 addr, cv, val); 4380 } 4381 } 4382 return ""; // not an understood message. 4383 } 4384 4385 /* 4386 * Returns the Digitrax Extended Accessory Packet Address 4387 */ 4388 private static int getExtendedAccessoryAddressFromDCCPacket(int[] packetInt) { 4389 return ( 1 + ((packetInt[0] & 0x3F) << 2) + 4390 ((( ~ packetInt[1]) & 0x70) << 4) 4391 + ((packetInt[1] & 0x06) >> 1)); 4392 } 4393 4394 private static String interpretOpcPr3Mode(LocoNetMessage l) { 4395 /* 4396 * Sets the operating mode of the PR3 device, if present. 4397 * 4398 * Information reverse-engineered by B. Milhaupt and used with permission 4399 */ 4400 4401 if ((l.getElement(1) == 0x10) && ((l.getElement(2) & 0x7c) == 0) 4402 && (l.getElement(3) == 0) && (l.getElement(4) == 0)) { 4403 // set PR3 mode of operation, where LS 2 bits of byte 2 are encoded as: 4404 // 0x00 Set the PR3 mode to MS100 interface mode with PR3 LocoNet termination disabled 4405 // 0x01 Set the PR3 to decoder programming track mode 4406 // 0x03 Set the PR3 to MS100 interface mode with PR3 LocoNet termination enabled 4407 4408 switch (l.getElement(2) & 0x3) { 4409 case 0x00: { 4410 return Bundle.getMessage("LN_MSG_SET_PR3_MODE_LOCONET_IF_WITHOUT_TERM"); 4411 } 4412 case 0x02: { 4413 return Bundle.getMessage("LN_MSG_SET_PR3_MODE_PR3_PROGRAMMING_TRACK_ONLY"); 4414 } 4415 case 0x03: { 4416 return Bundle.getMessage("LN_MSG_SET_PR3_MODE_LN_MSG_SET_PR3_MODE_LOCONET_IF_WITH_TERM"); 4417 } 4418 default: { 4419 break; 4420 } 4421 } 4422 } 4423 return ""; 4424 } 4425 4426 private static String interpretIb2Special(LocoNetMessage l) { 4427 // Intellibox function control message for mobile decoder F0-F28 (IB-I) and F13-F28 (IB-II) 4428 if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) 4429 && ((l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN) 4430 || (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F13_F19_TOKEN) 4431 || (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F21_F27_TOKEN))) { 4432 // Intellibox-I function control message for mobile decoder F5 thru F27 except F12 and F20 4433 // Intellibox-II function control message for mobile decoder F13 thru F27 except F20 4434 // Note: Intellibox-II documentation implies capability to control 4435 // MANY more functions. This capability may be extended by 4436 // additional tokens in element 3, including the special-case encoding 4437 // for the "eighth bit" as handled in the following case, below, 4438 // for F12, F20 & F28 4439 int funcOffset = 5 + 8 * (l.getElement(3) - LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN); 4440 String encodingType; 4441 if (l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN) { 4442 encodingType = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_IB1"); 4443 } else { 4444 encodingType = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_IB2"); 4445 } 4446 String[] funcInfo = new String[7]; 4447 int mask = 1; 4448 for (int i = 0; i < 7; i++) { 4449 // handle 7 bits of data 4450 funcInfo[i] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", 4451 funcOffset + i, 4452 Bundle.getMessage(((l.getElement(4) & mask) != 0) 4453 ? "LN_MSG_FUNC_ON" 4454 : "LN_MSG_FUNC_OFF")); 4455 mask *= 2; 4456 } 4457 return Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL", 4458 encodingType, l.getElement(2), funcInfo[0], 4459 funcInfo[1], funcInfo[2], funcInfo[3], 4460 funcInfo[4], funcInfo[5], funcInfo[6]); 4461 } else if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) 4462 && (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F20_F28_TOKEN)) { 4463 // Special-case for F12, F20 and F28, since the tokens from the previous case 4464 // can only encode 7 bits of data in element(4). 4465 return Bundle.getMessage("LN_MSG_INTELLIBOX_SPECIAL_FUNC_CTL", 4466 l.getElement(2), 4467 Bundle.getMessage(((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F12_MASK) != 0) 4468 ? "LN_MSG_FUNC_ON" 4469 : "LN_MSG_FUNC_OFF"), 4470 Bundle.getMessage(((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F20_MASK) != 0) 4471 ? "LN_MSG_FUNC_ON" 4472 : "LN_MSG_FUNC_OFF"), 4473 Bundle.getMessage(((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F28_MASK) != 0) 4474 ? "LN_MSG_FUNC_ON" 4475 : "LN_MSG_FUNC_OFF")); 4476 } else if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) 4477 && (l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F0_F4_TOKEN)) { 4478 // For Intellibox-I "one" with SW version 2.x - Special-case for F0 to F4 4479 String[] funcInfo = new String[7]; 4480 funcInfo[0] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", 4481 0, 4482 (l.getElement(4) & LnConstants.RE_IB1_F0_MASK) == 0 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4483 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4484 int mask = 1; 4485 for (int i = 0; i < 4; i++) { 4486 // handle 7 bits of data 4487 funcInfo[i + 1] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", 4488 i + 1, 4489 Bundle.getMessage(((l.getElement(4) & mask) != 0) 4490 ? "LN_MSG_FUNC_ON" 4491 : "LN_MSG_FUNC_OFF")); 4492 mask *= 2; 4493 } 4494 return Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_F0_TO_F4", 4495 l.getElement(2), 4496 funcInfo[0], funcInfo[1], funcInfo[2], funcInfo[3], 4497 funcInfo[4]); 4498 } 4499 // Because the usage of other tokens in message element(3) are not yet 4500 // understood, let execution fall thru to the "default" case 4501 return ""; 4502 } 4503 4504 private static String interpretIb2F9_to_F12(LocoNetMessage l) { 4505 // Intellibox-II function control message for mobile decoder F9 thru F12. 4506 int slot = l.getElement(1); 4507 int funcs = l.getElement(2); 4508 return Bundle.getMessage("LN_MSG_INTELLIBOX_SLOT_SET_F9_TO_F12", 4509 slot, 4510 Bundle.getMessage(((funcs & LnConstants.RE_IB2_F9_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4511 Bundle.getMessage(((funcs & LnConstants.RE_IB2_F10_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4512 Bundle.getMessage(((funcs & LnConstants.RE_IB2_F11_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), 4513 Bundle.getMessage(((funcs & LnConstants.RE_IB2_F12_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); 4514 } 4515 4516 /** 4517 * Convert bytes from LocoNet packet into a locomotive address. 4518 * 4519 * @param a1 Byte containing the upper bits. 4520 * @param a2 Byte containing the lower bits. 4521 * @return a locomotive address in the range of 0-16383 4522 */ 4523 static private int LOCO_ADR(int a1, int a2) { 4524 return (((a1 & 0x7f) * 128) + (a2 & 0x7f)); 4525 } 4526 4527 /** 4528 * Convert bytes from LocoNet packet into a 1-based address for a sensor or 4529 * turnout. 4530 * 4531 * @param a1 Byte containing the upper bits 4532 * @param a2 Byte containing the lower bits 4533 * @return 1-4096 address 4534 */ 4535 static private int SENSOR_ADR(int a1, int a2) { 4536 return (((a2 & 0x0f) * 128) + (a1 & 0x7f)) + 1; 4537 } 4538 4539 /* 4540 * Take an int and convert it to a dotted version number 4541 * as used by the LocoIO protocol. 4542 * Example: 123 => 1.2.3 4543 */ 4544 /** 4545 * Take the LocoIO version number and convert to human friendly format, like 4546 * "1.4.8" or "9.1". 4547 * 4548 * @param val The LocoIO version. 4549 * @return String with human readable format 4550 */ 4551 public static String dotme(int val) { 4552 if ((val >= 0) && (val < 10)) { 4553 return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_ONE_DIGIT", val); 4554 } else if ((val >= 10) && (val < 100)) { 4555 return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_TWO_DIGITS", val / 10, val % 10); 4556 } else if ((val >= 100) && (val < 1000)) { 4557 int hundreds = val / 100; 4558 int tens = (val - (hundreds * 100)) / 10; 4559 int ones = val % 10; 4560 return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_THREE_DIGITS", hundreds, tens, ones); 4561 } 4562 return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_OUT_OF_RANGE", val); 4563 } 4564 4565 /** 4566 * Convert throttle ID to a human friendly format. 4567 * 4568 * @param id1 Byte #1 of the ID 4569 * @param id2 Byte #2 of the ID 4570 * @return String with human friendly format, without the influence of 4571 * Locale 4572 */ 4573 private static String idString(int id1, int id2) { 4574 /* the decimalIdValueWithoutLocale_SpecificFormatting variable 4575 is used to generate a string representation of the ID value 4576 without any local-specific formatting. In other words, in a 4577 us_EN locale, we want "14385", not "14,385". 4578 */ 4579 String decimalIdValueWithoutLocale_SpecificFormatting 4580 = Integer.toString(((id2 & 0x7F) * 128 + (id1 & 0x7F))); 4581 4582 return Bundle.getMessage("LN_MSG_THROTTLE_ID", 4583 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4584 StringUtil.twoHexFromInt(id2 & 0x7F)), 4585 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 4586 StringUtil.twoHexFromInt(id1 & 0x7F)), 4587 decimalIdValueWithoutLocale_SpecificFormatting); 4588 } 4589 4590 /** 4591 * Create a string representation of the loco address in 4592 * addressLow and addressHigh in a form appropriate for the type of address (2 4593 * or 4 digit) using the Digitrax 'mixed mode' if necessary. 4594 * <p> 4595 * "Mixed mode" is used by DT100 and DT200 throttles to display loco 4596 * addresses between 100 and 127 as a two-digit displayable value, where the 4597 * left digit is either 'a', 'b', or 'c', (for addresses in the 10x, 11x, 4598 * and 12x ranges, respectively), and the right digit is the "x" from the 4599 * ranges above. 4600 * 4601 * @param addressLow the least-significant 7 bits of the loco address 4602 * @param addressHigh the most-significant 7 bits of the loco address 4603 * @return a String containing the address, using Digitrax 'mixed mode' 4604 * representation of the loco address, if appropriate 4605 */ 4606 public static String convertToMixed(int addressLow, int addressHigh) { 4607 // if we have a 2 digit decoder address, proceed accordingly 4608 switch (addressHigh) { 4609 case 0x7d: 4610 log.debug("addressLow / 10 = {}", addressLow / 10); 4611 switch (addressLow) { 4612 case 100: case 101: case 102: case 103: case 104: case 105: 4613 case 106: case 107: case 108: case 109: 4614 // N (short, alternately 'An') (or long address NN) 4615 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Ax", 4616 addressLow, 4617 addressLow-100, 4618 String.valueOf(LOCO_ADR(addressHigh, addressLow))); 4619 // Note: .toString intentionally used here to remove the "internationalized" 4620 // presentation of integers, which, in US English, adds a "," between 4621 // the thousands digit and the hundreds digit. This comma is undesired 4622 // in this application. 4623 case 110: case 111: case 112: case 113: case 114: case 115: 4624 case 116: case 117: case 118: case 119: 4625 // N (short, alternately 'Bn') (or long address NN) 4626 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Bx", 4627 addressLow, 4628 addressLow-110, 4629 String.valueOf(LOCO_ADR(addressHigh, addressLow))); 4630 // Note: .toString intentionally used here to remove the "internationalized" 4631 // presentation of integers, which, in US English, adds a "," between 4632 // the thousands digit and the hundreds digit. This comma is undesired 4633 // in this application. 4634 case 120: case 121: case 122: case 123: case 124: case 125: 4635 case 126: case 127: 4636 // N (short, alternately 'Cn') (or long address NN) 4637 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Cx", 4638 addressLow, 4639 addressLow-120, 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 default: 4646 // N (short) (or long address NN) 4647 return Bundle.getMessage("LN_MSG_HELPER_IS_SHORT_AND_LONG_ADDRESS", 4648 addressLow, 4649 String.valueOf(LOCO_ADR(addressHigh, addressLow))); 4650 // Note: .toString intentionally used here to remove the "internationalized" 4651 // presentation of integers, which, in US English, adds a "," between 4652 // the thousands digit and the hundreds digit. This comma is undesired 4653 // in this application. 4654 } 4655 4656 case 0x00: 4657 case 0x7f: 4658 switch (addressLow) { 4659 case 100: case 101: case 102: case 103: case 104: case 105: 4660 case 106: case 107: case 108: case 109: 4661 // N (short, alternately 'An') 4662 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Ax", 4663 addressLow, 4664 addressLow-100); 4665 case 110: case 111: case 112: case 113: case 114: case 115: 4666 case 116: case 117: case 118: case 119: 4667 // N (short, alternately 'Bn') 4668 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Bx", 4669 addressLow, 4670 addressLow-110); 4671 case 120: case 121: case 122: case 123: case 124: case 125: 4672 case 126: case 127: 4673 // N (short, alternately 'Cn') 4674 return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Cx", 4675 addressLow, 4676 addressLow-120); 4677 default: 4678 // N (short) 4679 return Bundle.getMessage("LN_MSG_HELPER_IS_SHORT_ADDRESS", 4680 addressLow); 4681 } 4682 default: 4683 // return the full 4 digit address 4684 return String.valueOf(LOCO_ADR(addressHigh, addressLow)); 4685 // Note: .toString intentionally used here to remove the "internationalized" 4686 // presentation of integers, which, in US English, adds a "," between 4687 // the thousands digit and the hundreds digit. This comma is undesired 4688 // in this application. 4689 } 4690 } 4691 4692 private static String trackStatusByteToString(int trackStatusByte) { 4693 return Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STAT", 4694 (((trackStatusByte & LnConstants.GTRK_MLOK1) != 0) 4695 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_LN1_1") 4696 : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_DT200")), 4697 (((trackStatusByte & LnConstants.GTRK_POWER) != 0) 4698 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_ON") 4699 : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_OFF")), 4700 (((trackStatusByte & LnConstants.GTRK_IDLE) != 0) 4701 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_RUNNING") 4702 : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_PAUSED")), 4703 (((trackStatusByte & LnConstants.GTRK_PROG_BUSY) != 0) 4704 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_PRG_BUSY") 4705 : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_PRG_AVAILABLE")) 4706 ); 4707 } 4708 4709 /** 4710 * Return a string which is formatted by a bundle Resource Name. 4711 * 4712 * @param hour fast-clock hour 4713 * @param minute fast-clock minute 4714 * @return a formatted string containing the time 4715 */ 4716 private static String fcTimeToString(int hour, int minute) { 4717 return Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_TIME", 4718 LocalTime.of(hour, minute).toString()); 4719 } 4720 4721 protected static String[] interpretF0_F4toStrings(int dirf) { 4722 String[] s = new String[5]; 4723 4724 s[0] = (((dirf & LnConstants.DIRF_F0) == LnConstants.DIRF_F0) 4725 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4726 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4727 s[1] = (((dirf & LnConstants.DIRF_F1) == LnConstants.DIRF_F1) 4728 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4729 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4730 s[2] = (((dirf & LnConstants.DIRF_F2) == LnConstants.DIRF_F2) 4731 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4732 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4733 s[3] = (((dirf & LnConstants.DIRF_F3) == LnConstants.DIRF_F3) 4734 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4735 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4736 s[4] = (((dirf & LnConstants.DIRF_F4) == LnConstants.DIRF_F4) 4737 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4738 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4739 return s; 4740 } 4741 4742 protected static String directionOfTravelString(boolean isForward) { 4743 return Bundle.getMessage(isForward ? "LN_MSG_DIRECTION_FWD" 4744 : "LN_MSG_DIRECTION_REV"); 4745 } 4746 4747 protected static String[] interpretF5_F8toStrings(int snd) { 4748 String[] s = new String[4]; 4749 4750 s[0] = (((snd & LnConstants.SND_F5) == LnConstants.SND_F5) 4751 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4752 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4753 4754 s[1] = (((snd & LnConstants.SND_F6) == LnConstants.SND_F6) 4755 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4756 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4757 4758 s[2] = (((snd & LnConstants.SND_F7) == LnConstants.SND_F7) 4759 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4760 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4761 4762 s[3] = (((snd & LnConstants.SND_F8) == LnConstants.SND_F8) 4763 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4764 : Bundle.getMessage("LN_MSG_FUNC_OFF")); 4765 4766 return s; 4767 } 4768 4769 private static String figureAddressIncludingAliasing(int adr, int adr2, int ss2, int id1, int id2) { 4770 4771 /* 4772 * Build loco address string. String will be a simple 4773 * number, unless the address is between 100 and 127 4774 * (inclusive), where a Digitrax "mixed mode" version 4775 * of the address will be appended. 4776 */ 4777 String mixedAdrStr = convertToMixed(adr, adr2); 4778 4779 /* 4780 * If the address is a command station "alias" condition, 4781 * then note it in the string. 4782 */ 4783 if (adr2 == 0x7f) { 4784 if ((ss2 & LnConstants.STAT2_ALIAS_MASK) == LnConstants.STAT2_ID_IS_ALIAS) { 4785 /* this is an aliased address and we have the alias */ 4786 return Bundle.getMessage("LN_MSG_LOCO_ADDR_HELPER_ALIAS_2_DIGIT_WITH_KNOWN_4_DIGIT", 4787 Integer.toString(LOCO_ADR(id2, id1)), mixedAdrStr); 4788 } else { 4789 /* this is an aliased address and we don't have the alias */ 4790 return Bundle.getMessage("LN_MSG_LOCO_ADDR_HELPER_ALIAS_2_DIGIT_WITH_UNKNOWN_4_DIGIT", 4791 mixedAdrStr); 4792 } 4793 } else { 4794 /* a regular address which is not an alias */ 4795 return mixedAdrStr; 4796 } 4797 } 4798 4799 public static String getDeviceNameFromIPLInfo(int type) { 4800 switch (type) { 4801 case LnConstants.RE_IPL_DIGITRAX_HOST_ALL: 4802 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_ALLDEVICES"); 4803 case LnConstants.RE_IPL_DIGITRAX_HOST_LNRP: 4804 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_LNRP"); 4805 case LnConstants.RE_IPL_DIGITRAX_HOST_UT4: 4806 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UT4"); 4807 case LnConstants.RE_IPL_DIGITRAX_HOST_UT6: 4808 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UT6"); 4809 case LnConstants.RE_IPL_DIGITRAX_HOST_WTL12: 4810 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_WTL12"); 4811 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS210: 4812 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS210"); 4813 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS210PLUS: 4814 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS210PLUS"); 4815 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240: 4816 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS240"); 4817 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240PLUS: 4818 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS240PLUS"); 4819 case LnConstants.RE_IPL_DIGITRAX_HOST_PR3: 4820 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PR3"); 4821 case LnConstants.RE_IPL_DIGITRAX_HOST_DT402: 4822 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT402"); 4823 case LnConstants.RE_IPL_DIGITRAX_HOST_DT500: 4824 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT500"); 4825 case LnConstants.RE_IPL_DIGITRAX_HOST_DT602: 4826 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT602"); 4827 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS51: 4828 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS51"); 4829 case LnConstants.RE_IPL_DIGITRAX_HOST_DCS52: 4830 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS52"); 4831 case LnConstants.RE_IPL_DIGITRAX_HOST_UR92: 4832 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UR92"); 4833 case LnConstants.RE_IPL_DIGITRAX_HOST_UR93: 4834 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UR93"); 4835 case LnConstants.RE_IPL_DIGITRAX_HOST_PR4: 4836 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PR4"); 4837 case LnConstants.RE_IPL_DIGITRAX_HOST_LNWI: 4838 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_LNWI"); 4839 case LnConstants.RE_IPL_DIGITRAX_HOST_BXP88: 4840 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_BXP88"); 4841 case LnConstants.RE_IPL_DIGITRAX_HOST_BXPA1: 4842 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_BXPA1"); 4843 case LnConstants.RE_IPL_DIGITRAX_HOST_DS74: 4844 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DS74"); 4845 case LnConstants.RE_IPL_DIGITRAX_HOST_DS78V: 4846 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DS78V"); 4847 case LnConstants.RE_IPL_DIGITRAX_HOST_DB210: 4848 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB210"); 4849 case LnConstants.RE_IPL_DIGITRAX_HOST_DB210OPTO: 4850 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB210OPTO"); 4851 case LnConstants.RE_IPL_DIGITRAX_HOST_DB220: 4852 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB220"); 4853 case LnConstants.RE_IPL_DIGITRAX_HOST_PM74: 4854 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PM74"); 4855 case LnConstants.RE_IPL_DIGITRAX_HOST_SE74: 4856 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_SE74"); 4857 case LnConstants.RE_IPL_DIGITRAX_HOST_BDL716: 4858 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_BDL716"); 4859 case LnConstants.RE_IPL_DIGITRAX_HOST_UR90X: 4860 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UR90X"); 4861 default: 4862 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UNKNOWN", type); 4863 } 4864 } 4865 4866 public static String getSlaveNameFromIPLInfo(int slaveNum) { 4867 switch (slaveNum) { 4868 case LnConstants.RE_IPL_DIGITRAX_SLAVE_ALL: 4869 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_ALLDEVICES"); 4870 case LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24: 4871 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_RF24"); 4872 default: 4873 return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_UNKNOWN", slaveNum); 4874 } 4875 } 4876 4877 /** 4878 * Interpret messages with Opcode of OPC_ALM_READ, OPC_ALM_WRITE. 4879 * 4880 * @param l LocoNet Message to interpret 4881 * @return String containing interpreted message or empty string if 4882 * message is not interpretable. 4883 */ 4884 public static String interpretAlm(LocoNetMessage l) { 4885 if (l.getElement(1) == 0x10) { 4886 String ret; 4887 ret = jmri.jmrix.loconet.alm.almi.Almi.interpretAlm(l); 4888 if (ret.length() > 1) { 4889 return ret; 4890 } 4891 } 4892 4893 if (l.getElement(1) == 0x15) { 4894 int slot = ( (l.getElement(2) & 0x07 ) *128) + l.getElement(3); // slot number for this request 4895 4896 String result = interpretExtendedSlotRdWr(l, slot) ; 4897 if (!result.isEmpty()) { 4898 return result; 4899 } 4900 } 4901 return ""; 4902 4903 } 4904 private static String interpretOpcExpMoveSlots(LocoNetMessage l) { 4905 int src = ((l.getElement(1) & 0x03) * 128) + (l.getElement(2) & 0x7f); 4906 int dest = ((l.getElement(3) & 0x03) * 128) + (l.getElement(4) & 0x7f); 4907 4908 if ((src >= 0x79) && (src <= 0x7f)) { 4909 return ""; 4910 } 4911 if ((dest >= 0x79) && (dest <= 0x7f)) { 4912 return ""; 4913 } 4914 4915 if ((l.getElement(1) & 0x78) != 0x38) { 4916 // Ignore if message is not one from a DCS240 (or newer) command 4917 // station's "Expanded" slot messaging. The message is probably 4918 // from an Intellibox or other non-Digitrax command station. 4919 return ""; 4920 } 4921 4922 boolean isSettingStatus = ((l.getElement(3) & 0b01110000) == 0b01100000); 4923 if (isSettingStatus) { 4924 int stat = l.getElement(4); 4925 return Bundle.getMessage("LN_MSG_OPC_EXP_SET_STATUS", 4926 src, 4927 LnConstants.CONSIST_STAT(stat), 4928 LnConstants.LOCO_STAT(stat), 4929 LnConstants.DEC_MODE(stat)); 4930 } 4931 boolean isUnconsisting = ((l.getElement(3) & 0b01110000) == 0b01010000); 4932 if (isUnconsisting) { 4933 // source and dest same, returns slot contents 4934 return Bundle.getMessage("LN_MSG_OPC_EXP_UNCONSISTING", 4935 src); 4936 } 4937 boolean isConsisting = ((l.getElement(3) & 0b01110000) == 0b01000000); 4938 if (isConsisting) { 4939 //add dest to src, returns dest slot contents 4940 return Bundle.getMessage("LN_MSG_OPC_EXP_CONSISTING", 4941 src,dest); 4942 } 4943 /* check special cases */ 4944 if (src == 0) { 4945 /* DISPATCH GET */ 4946 return Bundle.getMessage("LN_MSG_MOVE_SL_GET_DISP"); 4947 } else if (src == dest) { 4948 /* IN USE */ 4949 return Bundle.getMessage("LN_MSG_MOVE_SL_NULL_MOVE", src); 4950 } else if (dest == 0) { 4951 /* DISPATCH PUT */ 4952 return Bundle.getMessage("LN_MSG_MOVE_SL_DISPATCH_PUT", src); 4953 } else { 4954 /* general move */ 4955 return Bundle.getMessage("LN_MSG_MOVE_SL_MOVE", src, dest); 4956 } 4957 } 4958 4959 private static String interpretPocExpLocoSpdDirFunction(LocoNetMessage l) { 4960 int slot = ((l.getElement(1) & 0x03) * 128) + (l.getElement(2) & 0x7f); 4961 if ((l.getElement(1) & LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_SPEED) == LnConstants.OPC_EXP_SEND_SPEED_AND_DIR_FWD) { 4962 // speed and direction 4963 int spd = l.getElement(4); 4964 String direction = Bundle.getMessage((l.getElement(1) & LnConstants.OPC_EXP_SEND_SPEED_AND_DIR_REV) != 0 4965 ? "LN_MSG_DIRECTION_REV" : "LN_MSG_DIRECTION_FWD"); 4966 String throttleID = Integer.toHexString(l.getElement(3)); 4967 return Bundle.getMessage("LN_MSG_OPC_EXP_SPEED_DIRECTION", slot, spd, direction, throttleID); 4968 } 4969 // Build a string for the functions on off 4970 String[] fn = new String[8]; 4971 for (int bitIndex = 0; bitIndex < 8; bitIndex++) { 4972 fn[bitIndex] = (l.getElement(4) >> (7 - bitIndex) & 1) == 1 ? Bundle.getMessage("LN_MSG_FUNC_ON") 4973 : Bundle.getMessage("LN_MSG_FUNC_OFF"); 4974 } 4975 if ((l.getElement(1) & 4976 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F0F6) { 4977 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F0_F6", slot, fn[3], fn[7], fn[6], fn[5], fn[4], fn[2], 4978 fn[1]); 4979 } else if ((l.getElement(1) & 4980 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F7F13) { 4981 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F7_F13", slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], 4982 fn[1]); 4983 } else if ((l.getElement(1) & 4984 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F14F20) { 4985 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F14_F20",slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], 4986 fn[1]); 4987 } else if ((l.getElement(1) & 4988 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F21F28_F28OFF) { 4989 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F21_F28",slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], 4990 fn[1], Bundle.getMessage("LN_MSG_FUNC_OFF")); 4991 } else if ((l.getElement(1) & 4992 LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F21F28_F28ON) { 4993 return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F21_F28", slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], 4994 fn[1], Bundle.getMessage("LN_MSG_FUNC_ON")); 4995 } 4996 return ""; 4997 } 4998 4999 private static String interpretExtendedSlotRdWr(LocoNetMessage l, int slot) { 5000 /* 5001 * ************************************************ 5002 * extended slot read/write message * 5003 * ************************************************ 5004 */ 5005 /* 5006 * If it's a "Special" slot (Stats etc) use a different routine 5007 */ 5008 if (slot > 247 && slot < 253) { 5009 return interpretExtendedSlot_StatusData(l,slot); 5010 } 5011 int trackStatus = l.getElement(7); // track status 5012 int id1 = l.getElement(19); 5013 int id2 = l.getElement(18); 5014 int command = l.getOpCode(); 5015 int stat = l.getElement(4); // slot status 5016 //int adr = l.getElement(5) + 128 * l.getElement(6); // loco address 5017 int adr = l.getElement(5); 5018 int spd = l.getElement(8); // command speed 5019 int dirf = l.getElement(10) & 0b00111111; // direction and F0-F4 bits 5020 String[] dirf0_4 = interpretF0_F4toStrings(dirf); 5021 int ss2 = l.getElement(18); // slot status 2 (tells how to use 5022 // ID1/ID2 & ADV Consist) 5023 int adr2 = l.getElement(6); // loco address high 5024 int snd = l.getElement(10); // Sound 1-4 / F5-F8 5025 String[] sndf5_8 = interpretF5_F8toStrings(snd); 5026 5027 String locoAdrStr = figureAddressIncludingAliasing(adr, adr2, ss2, id1, id2); 5028 return Bundle.getMessage(((command == 0xEE) 5029 ? "LN_MSG_SLOT_LOCO_INFO_WRITE" 5030 : "LN_MSG_SLOT_LOCO_INFO_READ"), 5031 slot, 5032 locoAdrStr, 5033 LnConstants.CONSIST_STAT(stat), 5034 LnConstants.LOCO_STAT(stat), 5035 LnConstants.DEC_MODE(stat), 5036 directionOfTravelString((dirf & LnConstants.DIRF_DIR) == 0), 5037 spd, // needs re-interpretation for some cases of slot consisting state 5038 dirf0_4[0], 5039 dirf0_4[1], 5040 dirf0_4[2], 5041 dirf0_4[3], 5042 dirf0_4[4], 5043 sndf5_8[0], 5044 sndf5_8[1], 5045 sndf5_8[2], 5046 sndf5_8[3], 5047 trackStatusByteToString(trackStatus), 5048 Bundle.getMessage("LN_MSG_SLOT_HELPER_SS2_SIMPLE", 5049 Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", 5050 StringUtil.twoHexFromInt(ss2))), 5051 Bundle.getMessage("LN_MSG_SLOT_HELPER_ID1_ID2_AS_THROTTLE_ID", 5052 idString(id1, id2))); 5053 } 5054 5055 /** 5056 * Interprets an Enhanced Slot Report message in the "Query Mode" range of 5057 * slot numbers. 5058 * <p> 5059 * Only the primary slot numbers are interpreted, not any "aliases". 5060 * 5061 * @param l Enhanced Slot report LocoNetMessage to be interpreted 5062 * @param slot Slot number 5063 * @return String showing interpretation. 5064 */ 5065 private static String interpretExtendedSlot_StatusData(LocoNetMessage l, int slot) { 5066 String baseInfo; 5067 String detailInfo = ""; 5068 switch (slot) { 5069 case 248: 5070 5071 baseInfo = interpretExtendedSlot_StatusData_Base_Detail(l, slot); // Basic Identifying information 5072 detailInfo = interpretExtendedSlot_Query_Mode_248(l); // Flags 5073 5074 break; 5075 case 249: 5076 baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information 5077 detailInfo = interpretExtendedSlot_Query_Mode_249(l); // Electrical properties 5078 5079 break; 5080 case 250: 5081 baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information 5082 detailInfo = interpretExtendedSlot_Query_Mode_250(l); // Slots info 5083 break; 5084 case 251: 5085 baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information 5086 detailInfo = interpretExtendedSlot_Query_Mode_251(l); // LocoNet events and messages and stats 5087 break; 5088 case 252: 5089 baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information 5090 detailInfo = interpretExtendedSlot_Query_Mode_252(l); // DCC track status info 5091 break; 5092 default: 5093 baseInfo = "Wrong Slot # ("+ slot +")"; 5094 } 5095 return Bundle.getMessage("LN_MSG_OPC_EXP_QUERY_MODE_OVERALL", 5096 slot, baseInfo, detailInfo); 5097 } 5098 5099 /** 5100 * Interpret the base information in bytes 16,18,19 5101 * for slots 249,250,251, but not 248. 5102 * 5103 * @param l LocoNetMessage to be interpreted 5104 * @param slot slot number 5105 * @return formatted message 5106 */ 5107 private static String interpretExtendedSlot_StatusData_Base(LocoNetMessage l, int slot) { 5108 String hwType = LnConstants.IPL_NAME(l.getElement(16)); 5109 int hwSerial = ((l.getElement(19) & 0x3f) * 128 ) + l.getElement(18); 5110 String serNumHex = "0000"+Integer.toHexString(hwSerial).toUpperCase(); 5111 serNumHex = serNumHex.substring(serNumHex.length()-4); 5112 5113 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_BASE", 5114 hwType, 5115 hwSerial + "(0x" + serNumHex + ")"); 5116 } 5117 5118 /** 5119 * Interpret slot 248 base details. 5120 * 5121 * @param l LocoNetMessage to be interpreted 5122 * @param slot slot number 5123 * @return formatted message 5124 */ 5125 private static String interpretExtendedSlot_StatusData_Base_Detail(LocoNetMessage l, int slot) { 5126 String hwType = LnConstants.IPL_NAME(l.getElement(14)); 5127 if ((l.getElement(19) & 0x40) == 0x40) { 5128 hwType = hwType + Bundle.getMessage("LN_MSG_COMMAND_STATION"); 5129 } 5130 int hwSerial = ((l.getElement(19) & 0x3f) * 128 ) + l.getElement(18); 5131 String serNumHex = "0000"+Integer.toHexString(hwSerial).toUpperCase(); 5132 serNumHex = serNumHex.substring(serNumHex.length()-4); 5133 5134 float hwVersion = ((float)(l.getElement(17) & 0x78) / 8 ) + ((float)(l.getElement(17) & 0x07) / 10 ) ; 5135 float swVersion = ((float)(l.getElement(16) & 0x78) / 8 ) + ((float)(l.getElement(16) & 0x07) / 10 ) ; 5136 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_BASEDETAIL", 5137 hwType, 5138 hwSerial + "(0x" + serNumHex + ")", 5139 hwVersion, swVersion); 5140 } 5141 5142 private static String queryOnOff(int val, int bit) { 5143 return (((val & 1 << bit) == 1 << bit)?Ln_On:Ln_Off); 5144 } 5145 5146 /** 5147 * Interprets _some_ of the data in Query Mode report of slot 248 (and aliases!) 5148 * - "Flags" info. 5149 * 5150 * @param l LocoNetMessage to be interpreted 5151 * @return formatted message 5152 */ 5153 private static String interpretExtendedSlot_Query_Mode_248(LocoNetMessage l) { 5154 5155 int b = l.getElement(4); 5156 String lnetVmin = Bundle.getMessage("LNET_QUERY_LNETVMIN", queryOnOff(b, 6)); 5157 String overTemp = Bundle.getMessage("LNET_QUERY_OVERTEMP", queryOnOff(b, 5)); 5158 String fuseBad = Bundle.getMessage("LNET_QUERY_FUSEBAD", queryOnOff(b, 4)); 5159 String rsynMax = Bundle.getMessage("LNET_QUERY_RSYNMAX", queryOnOff(b, 3)); 5160 String vinHi = Bundle.getMessage("LNET_QUERY_VINHI", queryOnOff(b, 2)); 5161 String vinLo = Bundle.getMessage("LNET_QUERY_VINLO", queryOnOff(b, 1)); 5162 String iTrk = Bundle.getMessage("LNET_QUERY_ITRK", queryOnOff(b, 0)); 5163 5164 b = l.getElement(5); 5165 String usbLink = Bundle.getMessage("LNET_QUERY_ULINK", queryOnOff(b, 5)); 5166 String iLim = Bundle.getMessage("LNET_QUERY_ILIM", queryOnOff(b, 3)); 5167 String PTrkMaxI = Bundle.getMessage("LNET_QUERY_PTRKMAXI", queryOnOff(b, 2)); 5168 String PtrkIsol = Bundle.getMessage("LNET_QUERY_PTRKISOL", queryOnOff(b, 1)); 5169 5170 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_FLAGS", 5171 rsynMax, usbLink, iTrk, vinLo, vinHi, fuseBad, 5172 overTemp, lnetVmin, PtrkIsol, PTrkMaxI, iLim); 5173 } 5174 5175 /** 5176 * Interprets _some_ of the data in Query Mode report of slot 249 (and aliases!) 5177 * - "Electrical" info. 5178 * 5179 * @param l LocoNetMessage to be interpreted 5180 * @return formatted message 5181 */ 5182 private static String interpretExtendedSlot_Query_Mode_249(LocoNetMessage l) { 5183 float voltsTrack = ((float)l.getElement(4)) * 2 / 10 ; 5184 float voltsIn = ((float)l.getElement(5)) * 2 / 10; 5185 float ampsIn = ((float)l.getElement(6)) / 10; 5186 float ampsLimit = ((float)l.getElement(7)) / 10; 5187 float voltsRsLoaded = ((float)l.getElement(12)) * 2 / 10; 5188 float voltsRsUnLoaded = ((float)l.getElement(10)) * 2 / 10; 5189 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_ELECTRIC", 5190 voltsTrack, 5191 voltsIn, 5192 ampsIn, 5193 ampsLimit, 5194 voltsRsLoaded, 5195 voltsRsUnLoaded); 5196 } 5197 5198 /** 5199 * Interprets _some_ of the data in Query Mode report of slot 250 (and aliases!) 5200 * - "Slots" info. 5201 * 5202 * @param l LocoNetMessage to be interpreted 5203 * @return formatted message 5204 */ 5205 private static String interpretExtendedSlot_Query_Mode_250(LocoNetMessage l) { 5206 int msgInUse = (l.getElement(4) + ( l.getElement(5) * 128)) ; 5207 int msgIdle = (l.getElement(6) + ( l.getElement(7) * 128)) ; 5208 int msgFree = (l.getElement(8) + ( l.getElement(9) * 128)) ; 5209 int ctop = (l.getElement(10) + ( l.getElement(11) * 128)) ; 5210 int cup = (l.getElement(12) + ( l.getElement(13) * 128)) ; 5211 5212 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_SLOTS", 5213 msgInUse, msgIdle, msgFree, ctop, cup); 5214 } 5215 5216 /** 5217 * Interprets _some_ of the data in Query Mode report of slot 251 (and aliases!) 5218 * - "LocoNet message" info. 5219 * 5220 * @param l LocoNetMessage to be interpreted 5221 * @return formatted message 5222 */ 5223 private static String interpretExtendedSlot_Query_Mode_251(LocoNetMessage l) { 5224 int msgTotal = (l.getElement(4) + ( l.getElement(5) * 128)) ; 5225 int msgErrors = (l.getElement(6) + ( l.getElement(7) * 128)) ; 5226 int sleeps = (l.getElement(10) + ( l.getElement(11) * 128)); 5227 5228 return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_LOCONET", 5229 msgTotal, msgErrors, sleeps); 5230 } 5231 5232 /** 5233 * Interprets _some_ of the data in Query Mode report of slot 252 (and aliases!) 5234 * - "DCC status" info. 5235 * 5236 * @param l LocoNetMessage to be interpreted 5237 * @return formatted message 5238 */ 5239 private static String interpretExtendedSlot_Query_Mode_252(LocoNetMessage l) { 5240 int flt = (l.getElement(4) & 0x7f) + ((l.getElement(5) & 0x7f) << 7); 5241 int arv = (l.getElement(6) & 0x7f) + ((l.getElement(7) & 0x7f) << 7); 5242 int dst = (l.getElement(8) & 0x7f) + ((l.getElement(9) & 0x7f) << 7); 5243 return Bundle.getMessage("LN_MSG_OPC_EXP_QUERY_LOCONET_STAT2_LOCONET", 5244 flt, arv, dst); 5245 } 5246 5247 private static final String[] ds54sensors = {"AuxA", "SwiA", "AuxB", "SwiB", "AuxC", "SwiC", "AuxD", "SwiD"}; // NOI18N 5248 private static final String[] ds64sensors = {"A1", "S1", "A2", "S2", "A3", "S3", "A4", "S4"}; // NOI18N 5249 private static final String[] se8csensors = {"DS01", "DS02", "DS03", "DS04", "DS05", "DS06", "DS07", "DS08"}; // NOI18N 5250 5251 private final static Logger log = LoggerFactory.getLogger(LocoNetMessageInterpret.class); 5252}