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