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