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