Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jmri.jmrix.loconet.messageinterp; import java.time.LocalTime; import java.util.ArrayList; import jmri.InstanceManager; import jmri.NmraPacket; import jmri.Reporter; import jmri.ReporterManager; import jmri.Sensor; import jmri.SensorManager; import jmri.Turnout; import jmri.TurnoutManager; import jmri.jmrix.loconet.LnConstants; import jmri.jmrix.loconet.LocoNetMessage; import jmri.jmrix.loconet.lnsvf2.LnSv2MessageContents; import jmri.util.StringUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A utility class for formatting LocoNet packets into human-readable text. * <p> * Note that the formatted strings end in a \n, and may contain more than one * line separated by \n. Someday this should be converted to proper Java line * handling. * <p> * Much of this file is a Java-recoding of the display.c file from the llnmon * package of John Jabour. Some of the conversions involve explicit decoding of * structs defined in loconet.h in that same package. Those parts are (C) * Copyright 2001 Ron W. Auld. Use of these parts is by direct permission of the * author. * <p> * This class is derived from and replaces JMRI's * jmri.jmrix.loconet.locomon.Llnmon.java . * <p> * Many major comment blocks here are quotes from the Digitrax LocoNet(r) OPCODE * SUMMARY: found in the LocoNet(r) Personal Edition 1. * <p> * Some of the message formats used in this class are Copyright Digitrax, Inc. * and used with permission as part of the JMRI project. That permission does * not extend to uses in other software products. If you wish to use this code, * algorithm or these message formats outside of JMRI, please contact Digitrax * Inc for separate permission. * <p> * Reverse engineering of OPC_MULTI_SENSE was provided by Al Silverstein, used * with permission. * <p> * Reverse engineering of the Duplex Group/Password/Channel management was * provided by Leo Bicknell with help from B. Milhaupt, used with permission. * <p> * Reverse-engineering of device-specific OpSw messages, throttle text message, * and throttle semaphore message was provided by B. Milhaupt, used with * permission. * <p> * @author Bob Jacobsen Copyright 2001, 2002, 2003 * @author B. Milhaupt Copyright 2015, 2016, 2018 * @author Randall Wood Copyright 2016 */ public class LocoNetMessageInterpret { /** * Format the message into a text string. * <p> * Where the code is unable to determine a correct interpretation, the returned * string contains a message indicating that the message is not decoded followed * by the individual bytes of the message (in hexadecimal). * * @param l Message to parse * @param turnoutPrefix "System Name+ prefix which designates the connection's * Turnouts, such as "LT" * @param sensorPrefix "System Name+ prefix which designates the connection's * Turnouts, such as "LS" * @param reporterPrefix "System Name+ prefix which designates the connection's * Turnouts, such as "LR" * @return String representation of the interpretation of the message */ public static String interpretMessage(LocoNetMessage l, String turnoutPrefix, String sensorPrefix, String reporterPrefix) { String result; result = ""; /* * 2 Byte MESSAGE OPCODES * ; FORMAT = <OPC>,<CKSUM> * ; * * 4 byte MESSAGE OPCODES * ; FORMAT = <OPC>,<ARG1>,<ARG2>,<CKSUM> * : * CODES 0xA8 to 0xAF have responses * CODES 0xB8 to 0xBF have responses * * 6 byte MESSAGE OPCODES * ; FORMAT = <OPC>,<ARG1>,<ARG2>,<ARG3>,<ARG4>,<CKSUM> * : * CODES 0xC8 to 0xCF have responses * CODES 0xD8 to 0xDF have responses */ switch (l.getOpCode()) { /* * OPC_IDLE 0x85 ;FORCE IDLE state, Broadcast emergency STOP * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_IDLE: { return Bundle.getMessage("LN_MSG_IDLE"); } /* * OPC_GPON 0x83 ;GLOBAL power ON request * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_GPON: { return Bundle.getMessage("LN_MSG_GPON"); } /* * OPC_GPOFF 0x82 ;GLOBAL power OFF request * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_GPOFF: { return Bundle.getMessage("LN_MSG_GPOFF"); } /* * OPC_GPBUSY 0x81 ;MASTER busy code, NULL * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_GPBUSY: { return Bundle.getMessage("LN_MSG_MASTER_BUSY"); } /* * OPC_LOCO_ADR 0xBF ; REQ loco ADR * ; Follow on message: <E7>SLOT READ * ; <0xBF>,<0>,<ADR>,<CHK> REQ loco ADR * ; DATA return <E7>, is SLOT#, DATA that ADR was * : found in. * ; IF ADR not found, MASTER puts ADR in FREE slot * ; and sends DATA/STATUS return <E7>...... * ; IF no FREE slot, Fail LACK,0 is returned * ; [<B4>,<3F>,<0>,<CHK>] * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_LOCO_ADR: { String locoAddress = convertToMixed(l.getElement(2), l.getElement(1)); return Bundle.getMessage("LN_MSG_REQ_SLOT_FOR_ADDR", locoAddress); } case LnConstants.OPC_EXP_REQ_SLOT: { String locoAddress = convertToMixed(l.getElement(2), l.getElement(1)); return Bundle.getMessage("LN_MSG_REQ_EXP_SLOT_FOR_ADDR", locoAddress); } /* * OPC_SW_ACK 0xBD ; REQ SWITCH WITH acknowledge function (not DT200) * ; Follow on message: LACK * ; <0xBD>,<SW1>,<SW2>,<CHK> REQ SWITCH function * ; <SW1> =<0,A6,A5,A4- A3,A2,A1,A0> * ; 7 ls adr bits. * ; A1,A0 select 1 of 4 input pairs * ; in a DS54 * ; <SW2> =<0,0,DIR,ON- A10,A9,A8,A7> * ; Control bits and 4 MS adr bits. * ; DIR=1 for Closed/GREEN * ; =0 for Thrown/RED * ; ON=1 for Output ON * ; =0 FOR output OFF * ; response is: * ; <0xB4><3D><00> if DCS100 FIFO is full, rejected. * ; <0xB4><3D><7F> if DCS100 accepted * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_SW_ACK: { result = interpretOpcSwAck(l, turnoutPrefix); if (result.length() > 0) { return result; } break; } /* * OPC_SW_STATE 0xBC ; REQ state of SWITCH * ; Follow on message: LACK * ; <0xBC>,<SW1>,<SW2>,<CHK> REQ state of SWITCH * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_SW_STATE: { result = interpretOpcSwState(l, turnoutPrefix); if (result.length() > 0) { return result; } break; } /* * OPC_RQ_SL_DATA 0xBB ; Request SLOT DATA/status block * ; Follow on message: <E7>SLOT READ * ; <0xBB>,<SLOT>,<0>,<CHK> Request SLOT DATA/status block. * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_RQ_SL_DATA: { result = interpretOpcRqSlData(l); if (result.length() > 0) { return result; } break; } /* * OPC_MOVE_SLOTS 0xBA ; MOVE slot SRC to DEST * ; Follow on message: <E7>SLOT READ * ; <0xBA>,<SRC>,<DEST>,<CHK> Move SRC to DEST if * ; SRC or LACK etc is NOT IN_USE, clr SRC * ; SPECIAL CASES: * ; If SRC=0 ( DISPATCH GET) , DEST=dont care, * ; Return SLOT READ DATA of DISPATCH Slot * ; IF SRC=DEST (NULL move) then SRC=DEST is set to * ; IN_USE , if legal move. * ; If DEST=0, is DISPATCH Put, mark SLOT as DISPATCH * ; RETURN slot status <0xE7> of DESTINATION slot * ; DEST if move legal * ; RETURN Fail LACK code if illegal move * ; <B4>,<3A>,<0>,<chk>, illegal to move to/from * ; slots 120/127 * * Page 8 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_MOVE_SLOTS: { result = interpretOpcMoveSlots(l); if (result.length() > 0) { return result; } break; } // case LnConstants.OPC_EXP_SLOT_MOVE: { // result = interpretOpcExpMoveSlots(l); // if (result.length() > 0) { // return result; // } // break; // } /* * OPC_LINK_SLOTS 0xB9 ; LINK slot ARG1 to slot ARG2= * ; Follow on message: <E7>SLOT READ= * ; <0xB9>,<SL1>,<SL2>,<CHK> SLAVE slot SL1 to slot SL2 * ; Master LINKER sets the SL_CONUP/DN flags * ; appropriately. Reply is return of SLOT Status * ; <0xE7>. Inspect to see result of Link, invalid * ; Link will return Long Ack Fail <B4>,<39>,<0>,<CHK> * * Page 9 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_LINK_SLOTS: { int src = l.getElement(1); int dest = l.getElement(2); return Bundle.getMessage("LN_MSG_LINK_SLOTS", src, dest); } /* * OPC_UNLINK_SLOTS 0xB8 ;UNLINK slot ARG1 from slot ARG2 * ; Follow on message: <E7>SLOT READ * ; <0xB8>,<SL1>,<SL2>,<CHK> UNLINK slot SL1 from SL2 * ; UNLINKER executes unlink STRATEGY and returns new SLOT# * ; DATA/STATUS of unlinked LOCO . Inspect data to evaluate UNLINK * * Page 9 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_UNLINK_SLOTS: { int src = l.getElement(1); int dest = l.getElement(2); return Bundle.getMessage("LN_MSG_UNLINK_SLOTS", src, dest); } // case LnConstants.OPC_UNLINK_SLOTS /* * OPC_CONSIST_FUNC 0xB6 ; SET FUNC bits in a CONSIST uplink element * ; <0xB6>,<SLOT>,<DIRF>,<CHK> UP consist FUNC bits * ; NOTE this SLOT adr is considered in UPLINKED slot space. * * Page 9 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_CONSIST_FUNC: { result = interpretOpcConsistFunc(l); if (result.length() > 0) { return result; } break; } /* * OPC_SLOT_STAT1 0xB5 ; WRITE slot stat1 * ; <0xB5>,<SLOT>,<STAT1>,<CHK> WRITE stat1 * * Page 9 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_SLOT_STAT1: { int slot = l.getElement(1); int stat = l.getElement(2); return Bundle.getMessage("LN_MSG_SLOT_STAT1", slot, stat, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(stat)), LnConstants.CONSIST_STAT(stat), LnConstants.LOCO_STAT(stat), LnConstants.DEC_MODE(stat)); } /* * OPC_LONG_ACK 0xB4 ; Long acknowledge * ; <0xB4>,<LOPC>,<ACK1>,<CHK> Long acknowledge * ; <LOPC> is COPY of OPCODE responding to (msb=0). * ; LOPC=0 (unused OPC) is also VALID fail code * ; <ACK1> is appropriate response code for the OPCode * * Page 9 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_LONG_ACK: { result = interpretLongAck(l); if (result.length() > 0) { return result; } break; } /* * OPC_INPUT_REP 0xB2 ; General SENSOR Input codes * ; <0xB2>, <IN1>, <IN2>, <CHK> * ; <IN1> =<0,A6,A5,A4- A3,A2,A1,A0>, * ; 7 ls adr bits. * ; A1,A0 select 1 of 4 inputs pairs in a DS54. * ; <IN2> =<0,X,I,L- A10,A9,A8,A7>, * ; Report/status bits and 4 MS adr bits. * ; "I"=0 for DS54 "aux" inputs * ; =1 for "switch" inputs mapped to 4K SENSOR space. * ; * ; (This is effectively a least significant adr bit when * ; using DS54 input configuration) * ; * ; "L"=0 for input SENSOR now 0V (LO), * ; =1 for Input sensor >=+6V (HI) * ; "X"=1, control bit, * ; =0 is RESERVED for future! * * Page 9 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_INPUT_REP: { result = interpretOpcInputRep(l, sensorPrefix); if (result.length() > 0) { return result; } break; } // case LnConstants.OPC_INPUT_REP /* * OPC_SW_REP 0xB1 ; Turnout SENSOR state REPORT * ; <0xB1>,<SN1>,<SN2>,<CHK> SENSOR state REPORT * ; <SN1> =<0,A6,A5,A4- A3,A2,A1,A0>, * ; 7 ls adr bits. * ; A1,A0 select 1 of 4 input pairs in a DS54 * ; <SN2> =<0,1,I,L- A10,A9,A8,A7> * ; Report/status bits and 4 MS adr bits. * ; this <B1> opcode encodes input levels * ; for turnout feedback * ; "I" =0 for "aux" inputs (normally not feedback), * ; =1 for "switch" input used for * ; turnout feedback for DS54 * ; ouput/turnout # encoded by A0-A10 * ; "L" =0 for this input 0V (LO), * ; =1 this input > +6V (HI) * ; * ; alternately; * ; * ; <SN2> =<0,0,C,T- A10,A9,A8,A7> * ; Report/status bits and 4 MS adr bits. * ; this <B1> opcode encodes current OUTPUT levels * ; "C" =0 if "Closed" ouput line is OFF, * ; =1 "closed" output line is ON * ; (sink current) * ; "T" =0 if "Thrown" output line is OFF, * ; =1 "thrown" output line is ON * ; (sink I) * * Page 9 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_SW_REP: { result = interpretOpcSwRep(l, turnoutPrefix); if (result.length() > 0) { return result; } break; } /* * OPC_SW_REQ 0xB0 ; REQ SWITCH function * ; <0xB0>,<SW1>,<SW2>,<CHK> REQ SWITCH function * ; <SW1> =<0,A6,A5,A4- A3,A2,A1,A0>, * ; 7 ls adr bits. * ; A1,A0 select 1 of 4 input pairs in a DS54 * ; <SW2> =<0,0,DIR,ON- A10,A9,A8,A7> * ; Control bits and 4 MS adr bits. * ; DIR =1 for Closed,/GREEN, * ; =0 for Thrown/RED * ; ON =1 for Output ON, * ; =0 FOR output OFF * ; * ; Note-Immediate response of <0xB4><30><00> if command failed, * ; otherwise no response "A" CLASS codes * * Page 9 of LocoNet Personal Edition v1.0. * Page 12 special form Broadcast. * Page 13 special form LocoNet interrogate. */ case LnConstants.OPC_SW_REQ: { result = interpretOpcSwReq(l, turnoutPrefix); if (result.length() > 0) { return result; } break; } /* * OPC_LOCO_SND 0xA2 ;SET SLOT sound functions * * Page 10 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_LOCO_SND: { result = interpretOpcLocoSnd(l); if (result.length() > 0) { return result; } break; } // case LnConstants.OPC_LOCO_SND /* * OPC_LOCO_DIRF 0xA1 ;SET SLOT dir, F0-4 state * * Page 10 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_LOCO_DIRF: { result = interpretOpcLocoDirf(l); if (result.length() > 0) { return result; } break; } /* * OPC_LOCO_SPD 0xA0 ;SET SLOT speed e.g. <0xA0><SLOT#><SPD><CHK> * * Page 10 of LocoNet Personal Edition v1.0. */ case LnConstants.OPC_LOCO_SPD: { result = interpretOpcLocoSpd(l); if (result.length() > 0) { return result; } break; } case LnConstants.OPC_EXP_SEND_FUNCTION_OR_SPEED_AND_DIR: { result = interpretPocExpLocoSpdDirFunction(l); if (result.length() > 0) { return result; } break; } /* * OPC_PANEL_QUERY 0xDF messages used by throttles to discover * panels * * This op code is not documented by Digitrax. Some reverse engineering * performed by Leo Bicknell. The opcode "name" OPC_PANEL_QUERY * is not necessarily the name used by Digitrax. */ case LnConstants.OPC_PANEL_QUERY: { result = interpretOpcPanelQuery(l); if (result.length() > 0) { return result; } break; } /* * OPC_PANEL_RESPONSE 0xD7 messages used by throttles to discover * panels * * This op code is not documented by Digitrax. Reverse engineering * performed by Leo Bicknell. The opcode "name" OPC_PANEL_RESPONSE * is not necessarily the name used by Digitrax. */ case LnConstants.OPC_PANEL_RESPONSE: { result = interpretOpcPanelResponse(l); if (result.length() > 0) { return result; } break; } /* * OPC_MULTI_SENSE 0xD0 messages about power management and * transponding * * If byte 1 high nibble is 0x20 or 0x00 this is a transponding * message * * This op code is not documented by Digitrax. Reverse engineering * performed by Al Silverstein, and corrections added by B. Milhaupt. */ case LnConstants.OPC_MULTI_SENSE: { result = interpretOpcMultiSense(l, reporterPrefix); if (result.length() > 0) { return result; } break; } /** * ******************************************************************************************** * OPC_WR_SL_DATA 0xEF ; WRITE SLOT DATA, 10 bytes * ; Follow on * message: LACK * ; <0xEF>,<0E>,<SLOT#>,<STAT>,<ADR>,<SPD>,<DIRF>, * * ; <TRK>,<SS2>,<ADR2>,<SND>,<ID1>,<ID2>,<CHK> * ; SLOT DATA * WRITE, 10 bytes data /14 byte MSG * * ********************************************************************************************** * OPC_SL_RD_DATA 0xE7 ; SLOT DATA return, 10 bytes * ; * <0xE7>,<0E>,<SLOT#>,<STAT>,<ADR>,<SPD>,<DIRF>, * ; * <TRK>,<SS2>,<ADR2>,<SND>,<ID1>,<ID2>,<CHK> * ; SLOT DATA READ, 10 * bytes data /14 byte MSG * ; * ; NOTE; If STAT2.2=0 EX1/EX2 * encodes an ID#, * ; [if STAT2.2=1 the STAT.3=0 means EX1/EX2 * ; * are ALIAS] * ; * ; ID1/ID2 are two 7 bit values encoding a 14 bit * * ; unique DEVICE usage ID. * ; * ; 00/00 - means NO ID being * used * ; * ; 01/00 - ID shows PC usage. * ; to Lo nibble is TYP * PC# * ; 7F/01 (PC can use hi values) * ; * ; 00/02 -SYSTEM * reserved * ; to * ; 7F/03 * ; * ; 00/04 -NORMAL throttle RANGE * * ; to * ; 7F/7E * * ********************************************************************************************** * Notes: * The SLOT DATA bytes are, in order of TRANSMISSION for * <E7> READ or <EF> WRITE. * NOTE SLOT 0 <E7> read will return * MASTER config information bytes. * * 0) SLOT NUMBER: * * ; 0-7FH, * 0 is special SLOT, * ; 070H-07FH DIGITRAX reserved: * * 1) SLOT * STATUS1: * * D7-SL_SPURGE ; 1=SLOT purge en, * ; ALSO adrSEL * (INTERNAL use only) (not seen on NET!) * * D6-SL_CONUP ; * CONDN/CONUP: bit encoding-Control double linked Consist List * ; * 11=LOGICAL MID CONSIST , Linked up AND down * ; 10=LOGICAL * CONSIST TOP, Only linked downwards * ; 01=LOGICAL CONSIST * SUB-MEMBER, Only linked upwards * ; 00=FREE locomotive, no * CONSIST indirection/linking * ; ALLOWS "CONSISTS of CONSISTS". * Uplinked means that * ; Slot SPD number is now SLOT adr of * SPD/DIR and STATUS * ; of consist. i.e. is ;an Indirect pointer. * This Slot * ; has same BUSY/ACTIVE bits as TOP of Consist. TOP is * * ; loco with SPD/DIR for whole consist. (top of list). * ; * BUSY/ACTIVE: bit encoding for SLOT activity * * D5-SL_BUSY ; * 11=IN_USE loco adr in SLOT -REFRESHED * * D4-SL_ACTIVE ; 10=IDLE * loco adr in SLOT -NOT refreshed * ; 01=COMMON loco adr IN SLOT * -refreshed * ; 00=FREE SLOT, no valid DATA -not refreshed * * * D3-SL_CONDN ; shows other SLOT Consist linked INTO this slot, see * SL_CONUP * * D2-SL_SPDEX ; 3 BITS for Decoder TYPE encoding for * this SLOT * * D1-SL_SPD14 ; 011=send 128 speed mode packets * * * D0-SL_SPD28 ; 010=14 step MODE * ; 001=28 step. Generate Trinary * packets for this * ; Mobile ADR * ; 000=28 step. 3 BYTE PKT * regular mode * ; 111=128 Step decoder, Allow Advanced DCC * consisting * ; 100=28 Step decoder ,Allow Advanced DCC consisting * * * 2) SLOT LOCO ADR: * * LOCO adr Low 7 bits (byte sent as ARG2 * in ADR req opcode <0xBF>) * * 3) SLOT SPEED: * 0x00=SPEED 0 ,STOP * inertially * 0x01=SPEED 0 EMERGENCY stop * 0x02->0x7F increasing * SPEED,0x7F=MAX speed * (byte also sent as ARG2 in SPD opcode * <0xA0> ) * * 4) SLOT DIRF byte: (byte also sent as ARG2 in DIRF * opcode <0xA1>) * * D7-0 ; always 0 * D6-SL_XCNT ; reserved , set * 0 * D5-SL_DIR ; 1=loco direction FORWARD * D4-SL_F0 ; * 1=Directional lighting ON * D3-SL_F4 ; 1=F4 ON * D2-SL_F3 ; 1=F3 * ON * D1-SL_F2 ; 1=F2 ON * D0-SL_F1 ; 1=F1 ON * * * * * 5) TRK * byte: (GLOBAL system /track status) * * D7-D4 Reserved * D3 * GTRK_PROG_BUSY 1=Programming TRACK in this Master is BUSY. * D2 * GTRK_MLOK1 1=This Master IMPLEMENTS LocoNet 1.1 capability, * * 0=Master is DT200 * D1 GTRK_IDLE 0=TRACK is PAUSED, B'cast EMERG * STOP. * D0 GTRK_POWER 1=DCC packets are ON in MASTER, Global * POWER up * * 6) SLOT STATUS: * * D3 1=expansion IN ID1/2, * 0=ENCODED alias * D2 1=Expansion ID1/2 is NOT ID usage * D0 * 1=this slot has SUPPRESSED ADV consist-7) * * 7) SLOT LOCO ADR * HIGH: * * Locomotive address high 7 bits. If this is 0 then Low * address is normal 7 bit NMRA SHORT * address. If this is not zero * then the most significant 6 bits of this address are used in * * the first LONG address byte ( matching CV17). The second DCC LONG * address byte matches CV18 * and includes the Adr Low 7 bit value * with the LS bit of ADR high in the MS postion of this * track adr * byte. * * Note a DT200 MASTER will always interpret this as 0. * * * 8) SLOT SOUND: * * Slot sound/ Accesory Function mode II * packets. F5-F8 * (byte also sent as ARG2 in SND opcode) * * D7-D4 * reserved * D3-SL_SND4/F8 * D2-SL_SND3/F7 * D1-SL_SND2/F6 * * D0-SL_SND1/F5 1= SLOT Sound 1 function 1active (accessory 2) * * * 9) EXPANSION RESERVED ID1: * * 7 bit ls ID code written by * THROTTLE/PC when STAT2.4=1 * * 10) EXPANSION RESERVED ID2: * * 7 * bit ms ID code written by THROTTLE/PC when STAT2.4=1 * * ******************************************************************************************** * page 10 of LocoNet PE */ case LnConstants.OPC_WR_SL_DATA: case LnConstants.OPC_SL_RD_DATA: { result = interpretOpcWrSlDataOpcSlRdData(l); if (result.length() > 0) { return result; } break; } case LnConstants.OPC_ALM_WRITE: case LnConstants.OPC_ALM_READ: { result = interpretAlm(l); if (result.length() > 0) { return result; } break; } /* * OPC_PEER_XFER 0xE5 ; move 8 bytes PEER to PEER, SRC->DST NO resp * ; <0xE5>,<10>,<SRC>,<DSTL><DSTH>,<PXCT1>,<D1>,<D2>,<D3>,<D4>, * ; <PXCT2>,<D5>,<D6>,<D7>,<D8>,<CHK> * ; SRC/DST are 7 bit args. DSTL/H=0 is BROADCAST msg * ; SRC=0 is MASTER * ; SRC=0x70-0x7E are reserved * * Page 10 of LocoNet Personal Edition v1.0. * * Duplex group management reverse engineered by Leo Bicknell, with input from * B. Milhaupt. */ case LnConstants.OPC_PEER_XFER: { result = interpretOpcPeerXfer(l, reporterPrefix); if (result.length() > 0) { return result; } break; } case LnConstants.OPC_LISSY_UPDATE: { result = interpretOpcLissyUpdate(l); if (result.length() > 0) { return result; } break; } case LnConstants.OPC_IMM_PACKET: { result = interpretOpcImmPacket(l); if (result.length() > 0) { return result; } break; } case LnConstants.RE_OPC_PR3_MODE: { result = interpretOpcPr3Mode(l); if (result.length() > 0) { return result; } break; } case LnConstants.RE_OPC_IB2_F9_F12: { result = interpretIb2F9_to_F12(l); if (result.length() > 0) { return result; } break; } // TODO: put this back for intelibox cmd station. // it conflicts with loconet speed/dire etc. case LnConstants.RE_OPC_IB2_SPECIAL: { // 0xD4 result = interpretIb2Special(l); if (result.length() > 0) { return result; } result = interpretOpcExpMoveSlots(l); if (result.length() > 0) { return result; } break; } // case LnConstants.RE_OPC_IB2_SPECIAL: { //0xD4 //$FALL-THROUGH$ default: break; } // end switch over opcode type return Bundle.getMessage("LN_MSG_UNKNOWN_MESSAGE") + Bundle.getMessage("LN_MONITOR_MESSGAGE_RAW_HEX_INFO", l.toString()); } private static String interpretOpcPeerXfer20_1(LocoNetMessage l) { switch (l.getElement(3)) { case 0x08: { return Bundle.getMessage("LN_MSG_DUPLEX_RECEIVER_QUERY"); } case 0x10: { return Bundle.getMessage("LN_MSG_DUPLEX_RECEIVER_RESPONSE"); } default: { break; } } return ""; } private static String interpretOpcPeerXfer20_2(LocoNetMessage l) { switch (l.getElement(3)) { case 0x00: { int channel = l.getElement(5) | ((l.getElement(4) & 0x01) << 7); return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_SET", Integer.toString(channel)); } case 0x08: { return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_QUERY"); } case 0x10: { int channel = l.getElement(5) | ((l.getElement(4) & 0x01) << 7); return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_REPORT", Integer.toString(channel)); } default: { break; } } return ""; } private static String interpretOpcPeerXfer20_3(LocoNetMessage l) { // Characters appear to be 8 bit values, but transmitted over a 7 bit // encoding, so high order bits are stashed in element 4 and 9. char[] groupNameArray = { (char) (l.getElement(5) | ((l.getElement(4) & 0x01) << 7)), (char) (l.getElement(6) | ((l.getElement(4) & 0x02) << 6)), (char) (l.getElement(7) | ((l.getElement(4) & 0x04) << 5)), (char) (l.getElement(8) | ((l.getElement(4) & 0x08) << 4)), (char) (l.getElement(10) | ((l.getElement(9) & 0x01) << 7)), (char) (l.getElement(11) | ((l.getElement(9) & 0x02) << 6)), (char) (l.getElement(12) | ((l.getElement(9) & 0x04) << 5)), (char) (l.getElement(13) | ((l.getElement(9) & 0x08) << 4)) }; String groupName = new String(groupNameArray); // The pass code is stuffed in here, each digit in 4 bits. But again, it's a // 7 bit encoding, so the MSB of the "upper" half is stuffed into byte 14. int p1 = ((l.getElement(14) & 0x01) << 3) | ((l.getElement(15) & 0x70) >> 4); int p2 = l.getElement(15) & 0x0F; int p3 = ((l.getElement(14) & 0x02) << 2) | ((l.getElement(16) & 0x70) >> 4); int p4 = l.getElement(16) & 0x0F; // It's not clear you can set A-F from throttles or Digitrax's tools, but // they do take and get returned if you send them on the wire... String passcode = StringUtil.twoHexFromInt(p1) + StringUtil.twoHexFromInt(p2) + StringUtil.twoHexFromInt(p3) + StringUtil.twoHexFromInt(p4); // The MSB is stuffed elsewhere again... int channel = l.getElement(17) | ((l.getElement(14) & 0x04) << 5); // The MSB is stuffed elsewhere one last time. int id = l.getElement(18) | ((l.getElement(14) & 0x08) << 4); switch (l.getElement(3)) { case 0x00: { return Bundle.getMessage("LN_MSG_DUPLEX_NAME_WRITE", groupName); } case 0x08: { return Bundle.getMessage("LN_MSG_DUPLEX_NAME_QUERY"); } case 0x10: { return Bundle.getMessage("LN_MSG_DUPLEX_NAME_REPORT", groupName, passcode, channel, id); } default: { break; } } return ""; } private static String interpretOpcPeerXfer20_4(LocoNetMessage l) { // The MSB is stuffed elsewhere again... int id = l.getElement(5) | ((l.getElement(4) & 0x01) << 7); switch (l.getElement(3)) { case 0x00: { return Bundle.getMessage("LN_MSG_DUPLEX_ID_SET", id); } case 0x08: { return Bundle.getMessage("LN_MSG_DUPLEX_ID_QUERY"); } case 0x10: { return Bundle.getMessage("LN_MSG_DUPLEX_ID_REPORT", id); } default: { break; } } return ""; } private static String interpretOpcPeerXfer20_7(LocoNetMessage l) { if (l.getElement(3) == 0x08) { return Bundle.getMessage("LN_MSG_DUPLEX_PASSWORD_QUERY"); } if ((l.getElement(5) < 0x30) || (l.getElement(5) > 0x3c) || (l.getElement(6) < 0x30) || (l.getElement(6) > 0x3c) || (l.getElement(7) < 0x30) || (l.getElement(7) > 0x3c) || (l.getElement(8) < 0x30) || (l.getElement(8) > 0x3c)) { return ""; } char[] groupPasswordArray = { (char) l.getElement(5), (char) l.getElement(6), (char) l.getElement(7), (char) l.getElement(8) }; if ((groupPasswordArray[0] > 0x39) && (groupPasswordArray[0] < 0x3d)) { groupPasswordArray[0] += ('A' - '9' - 1); } if ((groupPasswordArray[1] > 0x39) && (groupPasswordArray[1] < 0x3d)) { groupPasswordArray[1] += ('A' - '9' - 1); } if ((groupPasswordArray[2] > 0x39) && (groupPasswordArray[2] < 0x3d)) { groupPasswordArray[2] += ('A' - '9' - 1); } if ((groupPasswordArray[3] > 0x39) && (groupPasswordArray[3] < 0x3d)) { groupPasswordArray[3] += ('A' - '9' - 1); } String groupPassword = new String(groupPasswordArray); switch (l.getElement(3)) { case 0x00: { return Bundle.getMessage("LN_MSG_DUPLEX_PASSWORD_SET", groupPassword); } case 0x10: { return Bundle.getMessage("LN_MSG_DUPLEX_PASSWORD_REPORT", groupPassword); } default: { break; } } return ""; } private static String interpretOpcPeerXfer20_10(LocoNetMessage l) { switch (l.getElement(3)) { case 0x08: { return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_SCAN_QUERY", l.getElement(5)); } case 0x10: { // High order bit stashed in another element again. int level = (l.getElement(6) & 0x7F) + ((l.getElement(4) & 0x02) << 6); return Bundle.getMessage("LN_MSG_DUPLEX_CHANNEL_SCAN_REPORT", l.getElement(5), level); } default: { break; } } return ""; } private static String interpretOpcPeerXfer20_8(LocoNetMessage l) { /** * ********************************************************************************** * IPL-capable device ping - OPC_RE_IPL (Device Ping Operations) * The * message bytes as assigned as follows: * <p> * <E5> <14> <08> <GR_OP_T> <DI_F2> <DI_Ss0> * <DI_Ss1> ... * <p> * <DI_Ss2> <DI_Ss3> <DI_U1> <00> <00> <DI_U2> * <DI_U3> ... * <p> * <00> <00><00> <00><00> <CHK> * where: * <p> * <DI_F2> encodes additional bits for the Slave device serial number. * * bits 7-4 always 0000b * bit 3 Bit 31 of Slave Device Serial Number * * bit 2 Bit 23 of Slave Device Serial Number * bit 1 Bit 15 of Slave * device Serial Number * bit 0 Bit 7 of Slave device Serial Number * <p> * <DI_Ss0> encodes 7 bits of the 32 bit Host device serial number: * * bit 7 always 0 * bits 6-0 Bits 6:0 of Slave device serial number * <p> * <DI_Ss1> encodes 7 bits of the 32 bit Host device serial number: * * bit 7 always 0 * bits 6-0 Bits 14:8 of Slave device serial number * <p> * <DI_Ss2> encodes 7 bits of the 32 bit Host device serial number: * * bit 7 always 0 * bits 6-0 Bits 22:16 of Slave device serial number * <p> * <DI_Ss3> encodes 7 bits of the 32 bit Host device serial number: * * bit 7 always 0 * bits 6-0 Bits 30:24 of Slave device serial number * <p> * <DI_U1> unknown data * when <GR_OP_T> = 0x08 * is always 0 * when * <GR_OP_T> = 0x10 * is not reverse-engineered and may be non-zero. * <p> * <DI_U2> unknown data * when <GR_OP_T> = 0x08 * is always 0 * when * <GR_OP_T> = 0x10 * is not reverse-engineered and may be non-zero. * <p> * <DI_U3> unknown data * when <GR_OP_T> = 0x08 * is always 0 * when * <GR_OP_T> = 0x10 * is not reverse-engineered and may be non-zero. * * * Information reverse-engineered by B. Milhaupt and used with * permission * * ********************************************************************************** */ /* OPC_RE_IPL (IPL Ping Operation) */ // Operations related to DigiIPL Device "Ping" operations // // "Ping" request issued from DigiIPL ver 1.09 issues this message on LocoNet. // The LocoNet request message encodes a serial number but NOT a device type. // // Depending on which devices are selected in DigiIPL when the "ping" // is selected, (and probably the S/Ns of the devices attached to the LocoNet, // the response is as follows: // DT402D LocoNet message includes the serial number from the DT402D's // Slave (RF24) serial number. If a UR92 is attached to LocoNet, // it will send the message via its RF link to the addressed // DT402D. (UR92 apparantly assumes that the long 802.15.4 // address of the DT402D is based on the serial number embedded // in the LocoNet message, with the MS 32 bits based on the UR92 // long address MS 32 bits). If more than one UR92 is attached // to LocoNet, all will pass the message to the RF interface. // UR92 LocoNet message includes the Slave serial number from the UR92. // These messages are not passed to the RF link by the addressed // UR92. If more than one UR92 is attached to LocoNet, and the // addressed UR92 hears the RF version of the LocoNet message, it // will respond via the RF interface with an acknowledge packet, // and a UR92 (not sure which one) responds on LocoNet with a // Ping report <e5><14><08><10>. // PR3 LocoNet message includes an effective serial number of all // zeros. There is no LocoNet message reply generated to a // request to a PR3 S/N, but there will be a reply on the PR3's // computer interface if the ping request was sent via the PR3's // computer interface (i.e. not from some other LocoNet agent). // UT4D While it has been suggested that the UT4D supports firmware // updates, the UT4D does not respond to the Ping message. // LNRP While it has been suggested that the LNRP supports firmware // updates, the LNRP does not respond to the Ping message. // // Ping Report values: // <unkn1> Seems always to be <0C>. None of the bytes relate to // Duplex Channel Number. // <unkn2> Matches byte 15 of the MAC payload of the reply sent by the // targeted UR92. // <unkn3> Unclear what this byte means. // // Information reverse-engineered by B. Milhaupt and used with permission switch (l.getElement(3)) { case 0x08: /* OPC_RE_IPL (IPL Ping Query) */ // Ping Request: <e5><14><08><08><msBits><Sn0><Sn1><Sn2><Sn3><0><0><0><0><0><0><0><0><0><0><0><Chk> if ((((l.getElement(4) & 0xF) != 0) || (l.getElement(5) != 0) || (l.getElement(6) != 0) || (l.getElement(7) != 0) || (l.getElement(8) != 0)) && (l.getElement(9) == 0) && (l.getElement(10) == 0) && (l.getElement(11) == 0) && (l.getElement(12) == 0) && (l.getElement(13) == 0) && (l.getElement(14) == 0) && (l.getElement(15) == 0) && (l.getElement(16) == 0) && (l.getElement(17) == 0) && (l.getElement(18) == 0)) { int hostSnInt; hostSnInt = (l.getElement(5) + (((l.getElement(4) & 0x1) == 1) ? 128 : 0)) + ((l.getElement(6) + (((l.getElement(4) & 0x2) == 2) ? 128 : 0)) * 256) + ((l.getElement(7) + (((l.getElement(4) & 0x4) == 4) ? 128 : 0)) * 256 * 256) + ((l.getElement(8) + (((l.getElement(4) & 0x8) == 8) ? 128 : 0)) * 256 * 256 * 256); return Bundle.getMessage("LN_MSG_DUPLEX_PING_REQUEST", Integer.toHexString(hostSnInt).toUpperCase()); } break; case 0x10: /* OPC_RE_IPL (IPL Ping Report) */ // Ping Report: <e5><14><08><10><msbits><Sn0><Sn1><Sn2><Sn3><unkn1><0><0><Unkn2><Unkn3><0><0><0><0><0><Chk> if (((l.getElement(4) & 0xF) != 0) || (l.getElement(5) != 0) || (l.getElement(6) != 0) || (l.getElement(7) != 0) || (l.getElement(8) != 0)) { // if any serial number bit is non-zero // int hostSnInt = (l.getElement(5) + (((l.getElement(4) & 0x1) == 1) ? 128 : 0)) + ((l.getElement(6) + (((l.getElement(4) & 0x2) == 2) ? 128 : 0)) * 256) + ((l.getElement(7) + (((l.getElement(4) & 0x4) == 4) ? 128 : 0)) * 256 * 256) + ((l.getElement(8) + (((l.getElement(4) & 0x8) == 8) ? 128 : 0)) * 256 * 256 * 256); return Bundle.getMessage("LN_MSG_DUPLEX_PING_REPORT", Integer.toHexString(hostSnInt).toUpperCase(), StringUtil.twoHexFromInt(l.getElement(12) + (((l.getElement(9)) & 0x4) == 0x4 ? 128 : 0)) .toUpperCase(), StringUtil.twoHexFromInt(l.getElement(13) + (((l.getElement(9)) & 0x8) == 0x8 ? 128 : 0)) .toUpperCase()); } break; default: break; } return ""; } private static String interpretOpcPeerXfer20_0f(LocoNetMessage l) { String device; switch (l.getElement(3)) { case 0x08: { if ((l.getElement(4) == 0) && (l.getElement(5) == 0) && (l.getElement(6) == 0) && (l.getElement(7) == 0) && (l.getElement(8) == 0) && (l.getElement(9) == 0) && (l.getElement(10) == 0) && (l.getElement(11) == 1) && (l.getElement(12) == 0) && (l.getElement(13) == 0) && (l.getElement(14) == 0) && (l.getElement(15) == 0) && (l.getElement(16) == 0) && (l.getElement(17) == 0) && (l.getElement(18) == 0)) { /** * ********************************************************************************** * IPL capable device query - RE_IPL_IDENTITY_OPERATION * (Device Query) * The message bytes are assigned as * follows: * <p> * <E5> <14> <0F> <08> <00> <00> * <00> <00> <00> <00> <00> <01> * <00> <00> ... * <p> * <00> <00> <00> <00> <00> <CHK> * * Information * reverse-engineered by B. Milhaupt and used with * permission * * ********************************************************************************** */ // Request for all IPL-queryable devices to report their presence // // Information reverse-engineered by B. Milhaupt and used with permission return Bundle.getMessage("LN_MSG_IPL_DISCOVER_ALL_DEVICES"); } else if (((l.getElement(5) != 0) || (l.getElement(6) != 0))) { /** * ********************************************************************************** * IPL device query by type - RE_IPL_IDENTITY_OPERATION * (Device Query) * The message bytes are assigned as * follows: * <p> * <E5> <14> <0F> <08> <DI_Hmf> * <DI_Hst> <DI_Slv> <00> <00> <00> * <00> <01> ... * <p> * <00> <00> <00> <00> <00> <00> * <00> <CHK> * where: * <p> * <DI_Hmf> DigiIPL-capable Host device manufacturer number. * This is not * the same as an NMRA Manufacturer ID. * 0x00 * Digitrax * Others No other Host device manufacturer * * numbers have been reverse- * engineered * <p> * <DI_Hst> encodes the DigiIPL-capable Host device type as * follows: * When <DI_Hmf> = 0x00 * 0x00 (0 decimal) No * Host device type reported * 0x04 (4 decimal) UT4D (Note * that UT4D, UT4 and UT4R do * not respond to this DigiIPL * * request) * 0x18 (24 decimal) RF24 - not typically a * Host device * 0x23 (35 decimal) PR3 * 0x2A (42 decimal) * DT402 (or DT402R or DT402D) * 0x33 (51 decimal) DCS51 * * 0x5C (92 decimal) UR92 * Others No other Host device * types have been * reverse-engineered * When * <DI_Hmf> is not 0x00 * All values Not reverse-engineered * <p> * <DI_Slv> encodes the DigiIPL-capable Slave device type as * follows: * When <DI_Smf> = 0x00 * 0x00 (0 decimal) Report * for all Slave device types * 0x18 (24 decimal) RF24 * * Others No other Slave device types have been * * reverse-engineered * * Information reverse-engineered by * B. Milhaupt and used with permission * * ********************************************************************************** */ // Request for IPL-queryable devices of given manufacturer and type to report // their presence // // Note that standard definitions are provided for UT4D and RF24, even though these // devices do not respond to this query. Note that UT4D will respond to IPL capable // device query with DI_Hmf = 0, DI_Hst = 0, DI_Slv = 0, and DI_Smf = 0. // // Information reverse-engineered by B. Milhaupt and used with permission device = getDeviceNameFromIPLInfo(l.getElement(4), l.getElement(5)); String slave = getSlaveNameFromIPLInfo(l.getElement(4), l.getElement(6)); return Bundle.getMessage("LN_MSG_IPL_DISCOVER_SPECIFIC_DEVICES", device, slave); } break; } // end case 0x08, which decodes 0xe5 0x14 0x0f 0x08 case 0x10: { return interpretOpcPeerXfer20Sub10(l); } // end case 0x10, which decodes 0xe5 0x14 0x0f 0x10 default: { break; } } // end of switch (l.getElement(3)), which decodes 0xe5 0x14 0x0f 0x?? return ""; } private static String interpretOpcPeerXfer20(LocoNetMessage l) { // Duplex Radio Management // DigiIPL messages // LocoIO, LocoServo, LocoBuffer, LocoBooster configuration messages switch (l.getElement(2)) { case 0x01: { // Seems to be a query for just duplex devices. String result = interpretOpcPeerXfer20_1(l); if (result.length() > 0) { return result; } break; } case 0x02: { // Request Duplex Radio Channel String result = interpretOpcPeerXfer20_2(l); if (result.length() > 0) { return result; } break; } case 0x03: { // Duplex Group Name String result = interpretOpcPeerXfer20_3(l); if (result.length() > 0) { return result; } break; } case 0x04: { // Duplex Group ID String result = interpretOpcPeerXfer20_4(l); if (result.length() > 0) { return result; } break; } case 0x07: { // Duplex Group Password String result = interpretOpcPeerXfer20_7(l); if (result.length() > 0) { return result; } break; } case 0x10: { // Radio Channel Noise/Activity String result = interpretOpcPeerXfer20_10(l); if (result.length() > 0) { return result; } break; } case LnConstants.RE_IPL_PING_OPERATION: { // case 0x08, which decodes 0xe5 0x14 0x08 String result = interpretOpcPeerXfer20_8(l); if (result.length() > 0) { return result; } break; } case LnConstants.RE_IPL_IDENTITY_OPERATION: { // case 0x0f, which decodes 0xe5 0x14 0x0f // Operations related to DigiIPL "Ping", "Identify" and "Discover" String result = interpretOpcPeerXfer20_0f(l); if (result.length() > 0) { return result; } break; } default: { break; } } return ""; } private static String interpretOpcPeerXfer20Sub10(LocoNetMessage l) { /** * ********************************************************************************** * IPL device identity report - RE_IPL_IDENTITY_OPERATION (Device * Report) * The message bytes are assigned as follows: * <p> * <E5> <14> <0F> <08> <DI_Hmf> <DI_Hst> * <DI_Slv> <DI_Smf> <DI_Hsw> ... * <p> * <DI_F1> <DI_Ssw> <DI_Hs0> <DI_Hs1> * <DI_Hs2> <DI_F2> <DI_Ss0> ... * <p> * <DI_Ss1> <DI_Ss2> <DI_Ss3> <CHK> * where: * <p> * <DI_Hmf> DigiIPL-capable Host device manufacturer number. This is not * * the same as an NMRA Manufacturer ID. * 0x00 Digitrax * Others No * other Host device manufacturer * numbers have been reverse- * * engineered * <p> * <DI_Hst> encodes the DigiIPL-capable Host device type as follows: * * When * <DI_Hmf> = 0x00 * 0x00 (0 decimal) No Host device type reported * * 0x04 (4 decimal) UT4D * 0x23 (35 decimal) PR3 * 0x2A (42 decimal) * DT402 (or DT402R or DT402D) * 0x33 (51 decimal) DCS51 * 0x5C (92 * decimal) UR92 * Others No other Host device types have been * * reverse-engineered * When <DI_Hmf> is not 0x00 * All values Not * reverse-engineered * <p> * <DI_Slv> encodes the DigiIPL-capable Slave device type as follows: * * When * <DI_Smf> = 0x00 * 0x00 (0 decimal) Report for all Slave device types * * 0x18 (24 decimal) RF24 * Others No other Slave device types have * been * reverse-engineered * <p> * <DI_Smf> DigiIPL-capable Slave device manufacturer number. This is * not * the same as an NMRA Manufacturer ID. * 0x00 Digitrax * Others * No other Slave device manufacturer * numbers have been reverse- * * engineered * <p> * <DI_Hsw> encodes the DigiIPL-capable Host device firmware revision * * number as follows: * bit 7 always 0 * bits 6-3 Host device firmware * major revision number * bits 2-0 Host device firmware minor revision * number * <p> * <DI_F1> encodes additional bits for the Slave device firmware major * * revision number and for the Host device serial number. * bits 7-4 * always 0000b * bit 3 Bit 23 of Host Device Serial Number * bit 2 Bit * 15 of Host Device Serial Number * bit 1 Bit 7 of Host Device Serial * Number * bit 0 bit 4 of Slave device firmware Major number * <p> * <DI_Ssw> encodes the DigiIPL-capable Slave device firmware revision * * number as follows: * bit 7 always 0 * bits 6-3 Host device firmware * major revision number * bits 6-3 4 least-significant bits of Slave * device firmware major * revision number (see also <DI_F1>[0]) * bits * 2-0 Slave device firmware minor revision number * <p> * <DI_Hs0> encodes 7 bits of the 24 bit Host device serial number: * * bit 7 always 0 * bits 6-3 Bits 6-0 of Host device serial number * <p> * <DI_Hs1> encodes 7 bits of the 24 bit Host device serial number: * * bit 7 always 0 * bits 6-3 Bits 14-9 of Host device serial number * <p> * <DI_Hs2> encodes 7 bits of the 24 bit Host device serial number: * * bit 7 always 0 * bits 6-3 Bits 22-16 of Host device serial number * <p> * <DI_F2> encodes additional bits for the Slave device serial number. * * bits 7-4 always 0000b * bit 3 Bit 31 of Slave Device Serial Number * * bit 2 Bit 23 of Slave Device Serial Number * bit 1 Bit 15 of Slave * Device Serial Number * bit 0 Bit 7 of Slave Device Serial Number * <p> * <DI_Ss0> encodes 7 bits of the 32 bit Slave device serial number: * * bit 7 always 0 * bits 6-3 Bits 6-0 of Slave device serial number * <p> * <DI_Ss1> encodes 7 bits of the 32 bit Slave device serial number: * * bit 7 always 0 * bits 6-3 Bits 14-9 of Slave device serial number * <p> * <DI_Ss2> encodes 7 bits of the 32 bit Slave device serial number: * * bit 7 always 0 * bits 6-3 Bits 22-16 of Slave device serial number * <p> * <DI_Ss3> encodes 7 bits of the 32 bit Slave device serial number: * * bit 7 always 0 * bits 6-3 Bits 30-24 of Slave device serial number * * * Information reverse-engineered by B. Milhaupt and used with * permission * * ********************************************************************************** */ // Request for one specific IPL-queryable device to return its identity information. // Expected response is of type <E5><14><10>... // // Note that standard definitions are provided for RF24, even though these // devices do not generate this report. // // Information reverse-engineered by B. Milhaupt and used with permission String hostType = getDeviceNameFromIPLInfo(l.getElement(4), l.getElement(5)); String hostVer = ((l.getElement(8) & 0x78) >> 3) + "." + ((l.getElement(8) & 0x7)); int hostSnInt = ((l.getElement(13) + (((l.getElement(9) & 0x8) == 8) ? 128 : 0)) * 256 * 256) + ((l.getElement(12) + (((l.getElement(9) & 0x4) == 4) ? 128 : 0)) * 256) + (l.getElement(11) + (((l.getElement(9) & 0x2) == 2) ? 128 : 0)); String hostSN = Integer.toHexString(hostSnInt).toUpperCase(); String hostInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_HOST_DETAILS", hostType, hostSN, hostVer); String slaveType = getSlaveNameFromIPLInfo(l.getElement(4), l.getElement(6)); String slaveInfo; if (l.getElement(6) != 0) { String slaveVer = (((l.getElement(10) & 0x78) >> 3) + ((l.getElement(9) & 1) << 4)) + "." + ((l.getElement(10) & 0x7)); int slaveSnInt = ((l.getElement(15) + (((l.getElement(14) & 0x1) == 1) ? 128 : 0))) + ((l.getElement(16) + (((l.getElement(14) & 0x2) == 2) ? 128 : 0)) * 256) + ((l.getElement(17) + (((l.getElement(14) & 0x4) == 4) ? 128 : 0)) * 256 * 256) + ((l.getElement(18) + (((l.getElement(14) & 0x8) == 8) ? 128 : 0)) * 256 * 256 * 256); slaveInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_SLAVE_DETAILS", slaveType, Integer.toHexString(slaveSnInt).toUpperCase(), slaveVer); } else { slaveInfo = Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_SLAVE_NO_SLAVE"); } return Bundle.getMessage("LN_MSG_IPL_DEVICE_IDENTITY_REPORT", hostInfo, slaveInfo); } private static String interpretOpcPeerXfer16(LocoNetMessage l) { /* * SRC=7F is THROTTLE msg xfer * ; <DSTL><DSTH> encode ID#, * ; <0><0> is THROT B'CAST * ; <PXCT1>=<0,XC2,XC1,XC0 - D4.7,D3.7,D2.7,D1.7> * ; XC0-XC2=ADR type CODE-0=7 bit Peer * TO Peer adrs * * ; 1=<D1>is SRC HI,<D2>is DST HI * ; <PXCT2>=<0,XC5,XC4,XC3 - D8.7,D7.7,D6.7,D5.7> * ; XC3-XC5=data type CODE- 0=ANSI TEXT string, * ; balance RESERVED * * **************************************************** * SV programming format 1 * * This is the message format as implemented by the certain * existing devices. New designs should not use this format. The * message bytes are assigned as follows: * ; <0xE5> <0x10> <SRC> <DST> <0x01> <PXCT1> * ; <D1> <D2> <D3> <D4> <PXCT2> * ; <D5> <D6> <D7> <D8> <CHK> * * The upper nibble of PXCT1 must be 0, * and the upper nibble of PXCT2 must be 1. The meanings of the * remaining bytes are as defined in the LocoNet Personal * Edition specification. * ********************************************* * SV programming format 2 * * This is the recommended format for new designs. * The message bytes as assigned as follows: * * ; <0xE5> <0x10> <SRC> <SV_CMD> <SV_TYPE> <SVX1> * ; <DST_L> <DST_H> <SV_ADRL> <SV_ADRH> <SVX2> * ; <D1> <D2> <D3> <D4> <CHK> * * The upper nibble of both SVX1 (PXCT1) and SVX2 (PXCT2) must be 1. */ int src = l.getElement(2); // source of transfer int dst_l = l.getElement(3); // ls 7 bits of destination int dst_h = l.getElement(4); // ms 7 bits of destination int pxct1 = l.getElement(5); int pxct2 = l.getElement(10); int d[] = l.getPeerXfrData(); if ((src == 0x7F) && (dst_l == 0x7F) && (dst_h == 0x7F) && ((pxct1 & 0x70) == 0x40)) { // Download (firmware?) messages. int sub = pxct2 & 0x70; switch (sub) { case 0x00: // setup return Bundle.getMessage("LN_MSG_IPL_SETUP", l.getElement(6), l.getElement(8), l.getElement(9), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(7))), l.getElement(11)); case 0x10: // set address return Bundle.getMessage("LN_MSG_IPL_SET_ADDRESS", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[0]) + StringUtil.twoHexFromInt(d[1]) + StringUtil.twoHexFromInt(d[2]))); case 0x20: // send data case 0x30: // verify return Bundle.getMessage((sub == 0x20) ? "LN_MSG_IPL_SEND_DATA" : "LN_MSG_IPL_VERIFY_REQUEST", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[0])), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[1])), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[2])), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[3])), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[4])), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[5])), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[6])), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(d[7]))); case 0x40: // end op return Bundle.getMessage("LN_MSG_IPL_END"); default: // everything else isn't understood, go to default break; } } if ((src == 0x7F) && (dst_l == 0x0) && (dst_h == 0x0) && ((pxct1 & 0x3) == 0x00) && ((pxct2 & 0x70) == 0x70)) { // throttle semaphore symbol message return Bundle.getMessage("LN_MSG_THROTTLE_SEMAPHORE", ((d[0] * 128) + d[1]), Bundle.getMessage(((d[2] & 0x10) == 0x10) ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), Bundle.getMessage(((d[2] & 0x08) == 0x08) ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), Bundle.getMessage(((d[2] & 0x04) == 0x04) ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), Bundle.getMessage(((d[2] & 0x02) == 0x02) ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_LIT" : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNLIT"), Bundle.getMessage(((d[2] & 0x01) == 0x01) ? "LN_MSG_THROTTLE_SEMAPHORE_HELPER_BLINKING" : "LN_MSG_THROTTLE_SEMAPHORE_HELPER_UNBLINKING")); } if ((src == 0x7F) && ((pxct1 & 0x70) == 0x00)) { if ((dst_l == 0x00) && (dst_h == 0x00)) { char c[] = new char[] { 0, 0, 0, 0, 0, 0, 0, 0 }; c[0] = (char) d[0]; c[1] = (char) d[1]; c[2] = (char) d[2]; c[3] = (char) d[3]; c[4] = (char) d[4]; c[5] = (char) d[5]; c[6] = (char) d[6]; c[7] = (char) d[7]; return Bundle.getMessage("LN_MSG_THROTTLE_TEXT_MESSAGE_ALL_THROTTLES", c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); } else { return Bundle.getMessage("LN_MSG_THROTTLE_TEXT_MESSAGE_SPECIFIC_THROTTLE", (char) d[0], (char) d[1], (char) d[2], (char) d[3], (char) d[4], (char) d[5], (char) d[6], (char) d[7], convertToMixed(dst_l, dst_h)); } } String result = interpretSV1Message(l); if (result.length() > 0) { return result; } result = interpretSV0Message(l); if (result.length() > 0) { return result; } // check for a specific type - SV Programming messages format 2 result = interpretSV2Message(l); if (result.length() > 0) { return result; } return ""; } private static String interpretSV1Message(LocoNetMessage l) { int d[] = l.getPeerXfrData(); if ((l.getElement(4) != 1) || ((l.getElement(5) & 0x70) != 0) || ((l.getElement(10) & 0x70) != 0x10)) { // is not an SV1 message return ""; } if (l.getElement(2) == 0x50) { // Packets from the LocoBuffer String dst_subaddrx = (l.getElement(4) != 0x01 ? "" : ((d[4] != 0) ? "/" + Integer.toHexString(d[4]) : "")); // LocoBuffer to LocoIO return "LocoBuffer => LocoIO@" + ((l.getElement(3) == 0) ? "broadcast" : Integer.toHexString(l.getElement(3)) + dst_subaddrx) + " " + (d[0] == 2 ? "Query SV" + d[1] : "Write SV" + d[1] + "=0x" + Integer.toHexString(d[3])) + ((d[2] != 0) ? " Firmware rev " + dotme(d[2]) : "") + ".\n"; } return ""; } private static String interpretSV0Message(LocoNetMessage l) { int dst_h = l.getElement(4); int pxct1 = l.getElement(5); int pxct2 = l.getElement(10); if ((dst_h != 0x01) || ((pxct1 & 0xF0) != 0x00) || ((pxct2 & 0xF0) != 0x00)) { return ""; } // (Jabour/Deloof LocoIO), SV Programming messages format 1 int dst_l = l.getElement(3); int d[] = l.getPeerXfrData(); int src = l.getElement(2); String src_subaddrx = ((d[4] != 0) ? "/" + Integer.toHexString(d[4]) : ""); String dst_subaddrx = ((d[4] != 0) ? "/" + Integer.toHexString(d[4]) : ""); String src_dev = ((src == 0x50) ? "Locobuffer" : "LocoIO@" + "0x" + Integer.toHexString(src) + src_subaddrx); String dst_dev = ((dst_l == 0x50) ? "LocoBuffer " : ((dst_l == 0x0) ? "broadcast" : "LocoIO@0x" + Integer.toHexString(dst_l) + dst_subaddrx)); String operation = (src == 0x50) ? ((d[0] == 2) ? "Query" : "Write") : ((d[0] == 2) ? "Report" : "Write"); return src_dev + "=> " + dst_dev + " " + operation + " SV" + d[1] + ((src == 0x50) ? (d[0] != 2 ? ("=0x" + Integer.toHexString(d[3])) : "") : " = " + ((d[0] == 2) ? ((d[2] != 0) ? (d[5] < 10) ? "" + d[5] : d[5] + " (0x" + Integer.toHexString(d[5]) + ")" : (d[7] < 10) ? "" + d[7] : d[7] + " (0x" + Integer.toHexString(d[7]) + ")") : (d[7] < 10) ? "" + d[7] : d[7] + " (0x" + Integer.toHexString(d[7]) + ")")) + ((d[2] != 0) ? " Firmware rev " + dotme(d[2]) : "") + ".\n"; } private static String interpretSV2Message(LocoNetMessage l) { // (New Designs) String svReply = ""; LnSv2MessageContents svmc = null; try { // assume the message is an SV2 message svmc = new LnSv2MessageContents(l); } catch (IllegalArgumentException e) { // message is not an SV2 message. Ignore the exception. } if (svmc != null) { // the message was indeed an SV2 message try { // get string representation of the message from an // available translation which is best suited to // the currently-active "locale" svReply = svmc.toString(); } catch (IllegalArgumentException e) { // message is not a properly-formatted SV2 message. Ignore the exception. } } return svReply; } private static String interpretOpcPeerXfer10(LocoNetMessage l) { // throttle status int tcntrl = l.getElement(2); String stat; switch (tcntrl) { case 0x40: stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_OK"); break; case 0x7F: stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_NO_KEYPRESS"); break; case 0x43: stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_PLUS_KEY"); break; case 0x42: stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_MINUS_KEY"); break; case 0x41: stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_RUNSTOP_KEY"); break; case 0x4e: stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_RESP_SEM_DISP_CMD"); break; default: stat = Bundle.getMessage("LN_MSG_THROTTLE_STATUS_HELPER_UNKONWN"); break; } return Bundle.getMessage("LN_MSG_THROTTLE_STATUS", StringUtil.twoHexFromInt(tcntrl), stat, idString(l.getElement(3), l.getElement(4)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(7))), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(8)))); } private static String interpretOpcPeerXfer9(LocoNetMessage l, String reporterPrefix) { /* * Transponding "find" query and report messages. * Information reverse-engineered by B. Milhaupt and used with permission */ switch (l.getElement(2)) { case 0x40: { /** * ********************************************************************************** * Transponding "find" query message * The message bytes are * assigned as follows: * <p> * <0xE5> <0x09> <0x40> <AD_H> <AD_L> <0x00> * <0x00> <0x00> <CHK> * where: * <p> * <AD_H> is encoded as shown below: * When * <AD_H> = 0x7D, * Address is a 7 bit value defined solely by * <AD_L>. * When <AD_H> is not 0x7D, * Address is a 14 bit * value; AD_H{6:0} represent the upper 7 bits * of the 14 bit * address. * <p> * <AD_L> contains the least significant 7 bits of the 14 or 7 * bit address. * * Information reverse-engineered by B. * Milhaupt and used with permission * * ********************************************************************************** */ String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); return Bundle.getMessage("LN_MSG_TRANSP_FIND_QUERY", locoAddr); } case 0x00: { /** * ********************************************************************************** * Transponding "find" report message * The message bytes are * assigned as follows: * <p> * <0xE5> <0x09> <0x00> <AD_H> <AD_L> <TR_ST> * <TR_ZS> <0x00> <CHK> * where: * <p> * <AD_H> is encoded as shown below: * When * <AD_H> = 0x7D, * Address is a 7 bit value defined solely by * <AD_L>. * When <AD_H> is not 0x7D, * Address is a 14 bit * value; AD_H{6:0} represent the upper 7 bits * of the 14 bit * address. * <p> * <AD_L> contains the least significant 7 bits of the 14 or 7 * bit address. * <p> * <TR_ST> contains the transponding status for the addressed * equipment, * encoded as: * bits 7-6 always 00b * bit 5 * encodes transponding presence * 0 = Addressed equipment is * absent * 1 = Addressed equipment is present * bits 4-0 encode * bits 7-3 of the Detection Section * <p> * <TR_ZS> contains the zone number and detection section, * encoded as: * bit 7 always 0 * bits 6-4 encode bits 2-0 of * the Detection Section * bits 3-1 encode the Transponding Zone * as shown below * 000b Zone A * 001b Zone B * 010b Zone C * * 011b Zone D * 100b Zone E * 101b Zone F * 110b Zone G * 111b * Zone H * bit 0 always 0 * * Information reverse-engineered by * B. Milhaupt and used with permission * * ********************************************************************************** */ int section = ((l.getElement(5) & 0x1F) << 3) + ((l.getElement(6) & 0x70) >> 4) + 1; String zone; String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); switch (l.getElement(6) & 0x0F) { case 0x00: zone = "A"; break; case 0x02: zone = "B"; break; case 0x04: zone = "C"; break; case 0x06: zone = "D"; break; case 0x08: zone = "E"; break; case 0x0A: zone = "F"; break; case 0x0C: zone = "G"; break; case 0x0E: zone = "H"; break; default: zone = Bundle.getMessage("LN_MSG_TRANSP_HELPER_UNKNOWN_ZONE", l.getElement(6) & 0x0F); break; } // get system and user names String reporterSystemName = reporterPrefix + ((l.getElement(5) & 0x1F) * 128 + l.getElement(6) + 1); Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName); String uname = ""; if (reporter != null) { uname = reporter.getUserName(); } if ((uname != null) && (!uname.isEmpty())) { return Bundle.getMessage("LN_MSG_TRANSP_REPORT_KNOWN_REPORTER_USERNAME", locoAddr, reporterSystemName, uname, section, zone); } return Bundle.getMessage("LN_MSG_TRANSP_REPORT_KNOWN_REPORTER_UNKNOWN_USERNAME", locoAddr, reporterSystemName, section, zone); } default: { break; } } return ""; } private static String interpretOpcPeerXfer7(LocoNetMessage l) { // This might be Uhlenbrock IB-COM start/stop programming track if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42) { switch (l.getElement(5)) { case 0x40: { return Bundle.getMessage("LN_MSG_UHLENBROCK_STOP_PROGRAMMING_TRACK"); } case 0x41: { return Bundle.getMessage("LN_MSG_UHLENBROCK_START_PROGRAMMING_TRACK"); } default: break; } } return ""; } private static String interpretOpcPeerXfer(LocoNetMessage l, String reporterPrefix) { String result = ""; // The first byte seems to determine the type of message. switch (l.getElement(1)) { case 0x10: { //l.getZElement(1) result = interpretOpcPeerXfer16(l); if (result.length() > 0) { return result; } break; } case 0x0A: { result = interpretOpcPeerXfer10(l); if (result.length() > 0) { return result; } break; } case 0x14: { result = interpretOpcPeerXfer20(l); if (result.length() > 0) { return result; } break; } case 0x09: { // l.getZElement(1) result = interpretOpcPeerXfer9(l, reporterPrefix); if (result.length() > 0) { return result; } break; } case 0x07: { result = interpretOpcPeerXfer7(l); if (result.length() > 0) { return result; } break; } default: { break; } } return ""; } private static String interpretLongAck(LocoNetMessage l) { int opcode = l.getElement(1); int ack1 = l.getElement(2); switch (opcode | 0x80) { case (LnConstants.OPC_LOCO_ADR): // response for OPC_LOCO_ADR return Bundle.getMessage("LN_MSG_LONG_ACK_LOCO_ADR"); case (LnConstants.OPC_LINK_SLOTS): // response for OPC_LINK_SLOTS return Bundle.getMessage("LN_MSG_LONG_ACK_LINK_SLOTS"); case (LnConstants.OPC_SW_ACK): // response for OPC_SW_ACK switch (ack1) { case 0: return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_FULL"); case 0x7f: return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_ACCEPT"); default: return Bundle.getMessage("LN_MSG_LONG_ACK_SW_ACK_UNKNOWN", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(ack1))) + Bundle.getMessage("LN_MONITOR_MESSGAGE_RAW_HEX_INFO", l.toString()); } case (LnConstants.OPC_SW_REQ): // response for OPC_SW_REQ return Bundle.getMessage("LN_MSG_LONG_ACK_SW_REQ_FAIL"); case (LnConstants.OPC_WR_SL_DATA): // response for OPC_WR_SL_DATA switch (ack1) { case 0: return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_FAIL"); case 0x01: return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_OK"); case 0x23: case 0x2b: case 0x6B: return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_PROG_DCS51_OK"); case 0x40: return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_BLIND"); case 0x7f: return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_NOT_IMPL"); default: return Bundle.getMessage("LN_MSG_LONG_ACK_WR_SL_UNKNOWN", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(ack1))) + Bundle.getMessage("LN_MONITOR_MESSGAGE_RAW_HEX_INFO", l.toString()); } case (LnConstants.OPC_SW_STATE): // response for OPC_SW_STATE return Bundle.getMessage("LN_MSG_LONG_ACK_SW_STATE", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(ack1)), Bundle.getMessage( (((ack1 & 0x20) != 0) ? "LN_MSG_SWITCH_STATE_CLOSED" : "LN_MSG_SWITCH_STATE_THROWN"))); case (LnConstants.OPC_MOVE_SLOTS): // response for OPC_MOVE_SLOTS switch (ack1) { case 0: return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_REJECT"); case 0x7f: return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_ACCEPT"); default: return Bundle.getMessage("LN_MSG_LONG_ACK_MOVE_SL_UNKNOWN", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(ack1))) + Bundle.getMessage("LN_MONITOR_MESSGAGE_RAW_HEX_INFO", l.toString()); } case LnConstants.OPC_IMM_PACKET: // response for OPC_IMM_PACKET if (ack1 == 0) { return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_REJECT"); } else if (ack1 == 0x7f) { return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_ACCEPT"); } else if (l.getElement(1) == 0x6D && l.getElement(2) == 0x01) { return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_UHL_PROG"); } else { return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_UNKNOWN", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(ack1))) + Bundle.getMessage("LN_MONITOR_MESSGAGE_RAW_HEX_INFO", l.toString()); } case LnConstants.OPC_IMM_PACKET_2: // response for OPC_IMM_PACKET return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LIM_MASTER", ack1, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(ack1))); case (LnConstants.RE_LACK_SPEC_CASE1 | 0x80): // 0x50 plus opcode bit so can match the switch'd value: case (LnConstants.RE_LACK_SPEC_CASE2 | 0x80): //0x00 plus opcode bit so can match the switch'd value: // OpSwitch read response reverse-engineered by B. Milhaupt and // used with permission int responseValue = l.getElement(2); if (responseValue == 0x7f) { return Bundle.getMessage("LN_MSG_LONG_ACK_SPEC_CASE1_2_ACCEPTED"); } else { return Bundle.getMessage("LN_MSG_LONG_ACK_SPEC_CASE1_2_REPORT", (((responseValue & 0x20) == 0x20) ? 1 : 0), (((responseValue & 0x20) == 0x20) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"))); } case LnConstants.OPC_ALM_READ: if (l.getElement(2) == 0) { return Bundle.getMessage("LN_MSG_LONG_ACK_SLOT_NOT_SUPPORTED", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(opcode))); } break; default: break; } return ""; } private static String interpretPm4xPowerEvent(LocoNetMessage l) { int pCMD = (l.getElement(3) & 0xF0); if ((pCMD == 0x30) || (pCMD == 0x10)) { // autoreverse int cm1 = l.getElement(3); int cm2 = l.getElement(4); String sect1Mode, sect1State; String sect2Mode, sect2State; String sect3Mode, sect3State; String sect4Mode, sect4State; if ((cm1 & 1) != 0) { sect1Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); sect1State = ((cm2 & 1) != 0) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); } else { sect1Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); sect1State = ((cm2 & 1) != 0) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); } if ((cm1 & 2) != 0) { sect2Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); sect2State = ((cm2 & 2) != 0) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); } else { sect2Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); sect2State = ((cm2 & 2) != 0) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); } if ((cm1 & 4) != 0) { sect3Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); sect3State = ((cm2 & 4) != 0) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); } else { sect3Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); sect3State = ((cm2 & 4) != 0) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); } if ((cm1 & 8) != 0) { sect4Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_AUTOREV"); sect4State = ((cm2 & 8) != 0) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_REV") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NORM"); } else { sect4Mode = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_PROTECT"); sect4State = ((cm2 & 8) != 0) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_SHORT") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X_HELPER_MODE_NONSHORT"); } return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_PM4X", (l.getElement(2) + 1) + ((l.getElement(1) & 0x1) << 7), sect1Mode, sect1State, sect2Mode, sect2State, sect3Mode, sect3State, sect4Mode, sect4State); } if ((pCMD == 0x20)) { //BXP88 int cm1 = l.getElement(3); int cm2 = l.getElement(4); ArrayList<Integer> sectsShorted = new ArrayList<>(); ArrayList<Integer> sectsUnshorted = new ArrayList<>(); if ((cm2 & 0x01) != 0) { sectsShorted.add(1); } else { sectsUnshorted.add(1); } if ((cm2 & 0x02) != 0) { sectsShorted.add(2); } else { sectsUnshorted.add(2); } if ((cm2 & 0x04) != 0) { sectsShorted.add(3); } else { sectsUnshorted.add(3); } if ((cm2 & 0x08) != 0) { sectsShorted.add(4); } else { sectsUnshorted.add(4); } if ((cm1 & 0x01) != 0) { sectsShorted.add(5); } else { sectsUnshorted.add(5); } if ((cm1 & 0x02) != 0) { sectsShorted.add(6); } else { sectsUnshorted.add(6); } if ((cm1 & 0x04) != 0) { sectsShorted.add(7); } else { sectsUnshorted.add(7); } if ((cm1 & 0x08) != 0) { sectsShorted.add(8); } else { sectsUnshorted.add(8); } return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXP88", (l.getElement(2) + 1) + ((l.getElement(1) & 0x1) << 7), StringUtils.join(sectsShorted, ','), StringUtils.join(sectsUnshorted, ',')); } if ((pCMD == 0x50) || (pCMD == 0x40)) { //BXPA1 int cm1 = l.getElement(3); String RevState = ""; String BreakState = ""; if ((cm1 & 0x10) != 0) { // reversing state if ((cm1 & 0x08) != 0) { RevState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_REV"); } else { RevState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_NORM"); } } else { // breaker state if ((cm1 & 0x08) != 0) { BreakState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_SHORT"); } else { BreakState = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1_HELPER_MODE_NONSHORT"); } } int bxpa1_Id = ((l.getElement(2) << 3) + (l.getElement(3) & 0x07) + 1); // Due to a problem with the firmware messages from x and x+4 are identical return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_POWER_BXPA1", bxpa1_Id, bxpa1_Id + 4, RevState, BreakState); } return ""; } private static String interpretOpSws(LocoNetMessage l) { int pCMD = (l.getElement(3) & 0xF0); if (pCMD == 0x70) { // programming int deviceType = l.getElement(3) & 0x7; String device; switch (deviceType) { case LnConstants.RE_MULTI_SENSE_DEV_TYPE_PM4X: device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_PM4X"); break; case LnConstants.RE_MULTI_SENSE_DEV_TYPE_BDL16X: device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_BDL16X"); break; case LnConstants.RE_MULTI_SENSE_DEV_TYPE_SE8: device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_SE8C"); break; case LnConstants.RE_MULTI_SENSE_DEV_TYPE_DS64: device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_DS64"); break; default: return ""; } int val = (l.getElement(4) & 0x01); int opsw = (l.getElement(4) & 0x7E) / 2 + 1; int bdaddr = l.getElement(2) + 1; if ((l.getElement(1) & 0x1) != 0) { bdaddr += 128; } if ((deviceType == 0) && (bdaddr == 1) && (l.getElement(4) == 0)) { return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_ACCESS_QUERY_ALL"); } if ((l.getElement(1) & 0x10) != 0) { // write String valType = (val == 1) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_WRITE_ACCESS", device, bdaddr, opsw, val, valType); } else { // query return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_QUERY_ACCESS", device, bdaddr, opsw); } } return ""; } private static String interpretDeviceType(LocoNetMessage l) { int pCMD = (l.getElement(3) & 0xF0); if (pCMD == 0x00) { /** * ************************************************** * Device type report * The message bytes as assigned as follows: * <p> * <0xD0> <DQT_REQ> <DQT_BRD> <DQT_B3> <DQT_B4> * <CHK> * * where: * <p> * <DQT_REQ> contains the device query request, * encoded as: * bits * 7-4 always 0110b * bits 3-1 always 001b * bit 0 (BoardID-1)<7> * <p> * <DQT_BRD> contains most the device board ID number, * encoded as: * * bit 7 always 0b * bits 6-0 (BoardID-1)<6:0> * <p> * <DQT_B3> contains the board type identification, * encoded as: * * bits 7-4 always 0000b * bits 3-0 contain the encoded device type, * * encoded as: * 0000b PM4x device * 0001b BDL16x device * 0010b * SE8C device * 0011b DS64 device * others Unknown device type * <p> * <DQT_B4> contains device version number: * bit 7 always 0b * bits * 6-0 VersionNumber(6:0) * * Information reverse-engineered by B. * Milhaupt and used with permission * * ************************************************** */ // This message is a report which is sent by a LocoNet device // in response to a query of attached devices // Note - this scheme is supported by only some Digitrax devices. // // A VersionNumber of 0 implies the hardware does not report // a valid version number. // // Device type report reverse-engineered by B. Milhaupt and // used with permission int deviceType = l.getElement(3) & 0x7; String device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_UNKNOWN"); switch (deviceType) { case LnConstants.RE_MULTI_SENSE_DEV_TYPE_PM4X: device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_PM4X"); break; case LnConstants.RE_MULTI_SENSE_DEV_TYPE_BDL16X: device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_BDL16X"); break; case LnConstants.RE_MULTI_SENSE_DEV_TYPE_SE8: device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_SE8C"); break; case LnConstants.RE_MULTI_SENSE_DEV_TYPE_DS64: device = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_DS64"); break; default: log.warn("Unhandled device type: {}", deviceType); break; } int bdaddr = l.getElement(2) + 1; if ((l.getElement(1) & 0x1) != 0) { bdaddr += 128; } String versionNumber = Integer.toString(l.getElement(4)); if (l.getElement(4) == 0) { versionNumber = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_DEV_RPT_HELPER_VER_UNKNOWN"); } return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_DEV_TYPE_RPT", device, bdaddr, versionNumber); } return ""; } private static String interpretOpcMultiSense(LocoNetMessage l, String reporterPrefix) { int type = l.getElement(1) & LnConstants.OPC_MULTI_SENSE_MSG; switch (type) { case LnConstants.OPC_MULTI_SENSE_POWER: // This is a PM42 power event. String result = interpretPm4xPowerEvent(l); if (result.length() > 0) { return result; } result = interpretOpSws(l); if (result.length() > 0) { return result; } result = interpretDeviceType(l); if (result.length() > 0) { return result; } else { break; } case LnConstants.OPC_MULTI_SENSE_PRESENT: case LnConstants.OPC_MULTI_SENSE_ABSENT: result = interpretOpcMultiSenseTranspPresence(l, reporterPrefix); if (result.length() > 0) { return result; } break; default: break; } return ""; } private static String interpretOpcMultiSenseTranspPresence(LocoNetMessage l, String reporterPrefix) { // Transponding Event // get system and user names String reporterSystemName; String reporterUserName; String zone; int bxp88Zone = 1 + (l.getElement(2) & 0x07); switch (l.getElement(2) & 0x0f) { // ignore bit 0 which seems to provide some unknown info from the BXP88 case 0x00: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEA"); break; case 0x02: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEB"); break; case 0x04: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEC"); break; case 0x06: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONED"); break; case 0x08: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEE"); break; case 0x0A: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEF"); break; case 0x0C: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEG"); break; case 0x0E: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONEH"); break; default: zone = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_ZONE_UNKNOWN", (l.getElement(2) & 0x0F)); break; } int type = l.getElement(1) & LnConstants.OPC_MULTI_SENSE_MSG; reporterSystemName = reporterPrefix + ((l.getElement(1) & 0x1F) * 128 + l.getElement(2) + 1); Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName); reporterUserName = ""; if (reporter != null) { String uname = reporter.getUserName(); if ((uname != null) && (!uname.isEmpty())) { reporterUserName = uname; } } int bxpa1Number = 1 + l.getElement(2) + (l.getElement(1) & 0x1F) * 128; int bxp88Number = 1 + (l.getElement(2) / 8) + (l.getElement(1) & 0x1F) * 16; int section = 1 + (l.getElement(2) / 16) + (l.getElement(1) & 0x1F) * 8; String locoAddr = convertToMixed(l.getElement(4), l.getElement(3)); String transpActivity = (type == LnConstants.OPC_MULTI_SENSE_PRESENT) ? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT") : Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_ABSENT"); if ((l.getElement(2) & 0x1) == 0) { return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_REPORT_WITH_BXP88", locoAddr, transpActivity, reporterSystemName, reporterUserName, section, zone, bxp88Number, bxp88Zone, bxpa1Number); } else { return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_REPORT_NOT_BDL16X", locoAddr, transpActivity, reporterSystemName, reporterUserName, bxp88Number, bxp88Zone, bxpa1Number); } } private static String interpretOpcWrSlDataOpcSlRdData(LocoNetMessage l) { int slot = l.getElement(2); // slot number for this request String mode; int command = l.getElement(0); int id1 = l.getElement(11); // ls 7 bits of ID code int id2 = l.getElement(12); // ms 7 bits of ID code /* * These messages share a common data format with the only difference being * whether we are reading or writing the slot data. */ if (command == LnConstants.OPC_WR_SL_DATA) { mode = Bundle.getMessage("LN_MSG_SLOT_HELPER_ACCESS_TYPE_REQUEST"); } else { mode = Bundle.getMessage("LN_MSG_SLOT_HELPER_ACCESS_TYPE_RESPONSE"); } switch (slot) { case LnConstants.FC_SLOT: String result; result = interpretFastClockSlot(l, mode, id1, id2); if (result.length() > 0) { return result; } break; case LnConstants.PRG_SLOT: result = interpretProgSlot(l, mode, id1, id2, command); if (result.length() > 0) { return result; } break; case 0x79: case 0x7a: case 0x7D: return ""; case LnConstants.CFG_EXT_SLOT: result = interpretCmdStnExtCfgSlotRdWr(l, command); if (result.length() > 0) { return result; } break; // end programming track block case LnConstants.CFG_SLOT: result = interpretCmdStnCfgSlotRdWr(l, command); if (result.length() > 0) { return result; } break; default: result = interpretStandardSlotRdWr(l, id1, id2, command, slot); if (result.length() > 0) { return result; } break; } return ""; } private static String interpretOpcInputRep(LocoNetMessage l, String sensorPrefix) { int in1 = l.getElement(1); int in2 = l.getElement(2); int contactNum = ((SENSOR_ADR(in1, in2) - 1) * 2 + ((in2 & LnConstants.OPC_INPUT_REP_SW) != 0 ? 2 : 1)); // get system and user names String sensorSystemName = sensorPrefix + contactNum; String sensorUserName = ""; Sensor sensor = InstanceManager.getDefault(SensorManager.class).getSensor(sensorSystemName); sensorUserName = ""; if (sensor != null) { String uname = sensor.getUserName(); if ((uname != null) && (!uname.isEmpty())) { sensorUserName = uname; } } int sensorid = (SENSOR_ADR(in1, in2) - 1) * 2 + ((in2 & LnConstants.OPC_INPUT_REP_SW) != 0 ? 2 : 1); int bdlid = ((sensorid - 1) / 16) + 1; int bdlin = ((sensorid - 1) % 16) + 1; String bdl = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_BDL_INFO", bdlid, bdlin); int boardid = ((sensorid - 1) / 8) + 1; int boardindex = ((sensorid - 1) % 8); String otherBoardsNames; String otherBoardsInputs; if (sensorid < 289) { otherBoardsNames = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_ALL_EQUIV_BOARDS", boardid); otherBoardsInputs = Bundle.getMessage("LN_MSG_OPC_INPUT_REPORT_INPUT_NAMES_ALL_EQUIV_BOARDS", ds54sensors[boardindex], ds64sensors[boardindex], se8csensors[boardindex]); } else { otherBoardsNames = Bundle.getMessage("LN_MSG_OPC_INPUT_REP_NO_SE8C", boardid); otherBoardsInputs = Bundle.getMessage("LN_MSG_OPC_INPUT_REPORT_INPUT_NAMES_NO_SE8C", ds54sensors[boardindex], ds64sensors[boardindex]); } // There is no way to tell what kind of a board sent the message. // To be user friendly, we just print all the known combos. return Bundle.getMessage("LN_MSG_OPC_INPUT_REP", sensorSystemName, sensorUserName, Bundle.getMessage( (in2 & LnConstants.OPC_INPUT_REP_HI) != 0 ? "LN_MSG_SENSOR_STATE_HIGH" : "LN_MSG_SENSOR_STATE_LOW"), bdl, otherBoardsNames, otherBoardsInputs); } private static String interpretOpcSwRep(LocoNetMessage l, String turnoutPrefix) { int sn1 = l.getElement(1); int sn2 = l.getElement(2); // get system and user names String turnoutUserName = ""; String turnoutSystemName = turnoutPrefix + SENSOR_ADR(sn1, sn2); Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); String uname = ""; if (turnout != null) { uname = turnout.getUserName(); if ((uname != null) && (!uname.isEmpty())) { turnoutUserName = uname; } else { turnoutUserName = ""; } } if ((sn2 & LnConstants.OPC_SW_REP_INPUTS) != 0) { return Bundle.getMessage("LN_MSG_OPC_SW_REP_INPUTS_STATE", turnoutSystemName, turnoutUserName, Bundle.getMessage(((sn2 & LnConstants.OPC_SW_REP_SW) != 0 ? "LN_MSG_SENSOR_SW_INPUT_TYPE_HI" : "LN_MSG_SENSOR_SW_INPUT_TYPE_LO")), Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_HI) != 0) ? "LN_MSG_SENSOR_SW_INPUT_STATE_HI" : "LN_MSG_SENSOR_SW_INPUT_STATE_LO"))); } return Bundle.getMessage("LN_MSG_OPC_SW_REP_OUTPUT_STATE", turnoutSystemName, turnoutUserName, Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_CLOSED) != 0) ? "LN_MSG_SENSOR_SW_OUTPUT_STATE_ON" : "LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF")), Bundle.getMessage((((sn2 & LnConstants.OPC_SW_REP_THROWN) != 0) ? "LN_MSG_SENSOR_SW_OUTPUT_STATE_ON" : "LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF"))); } private static String interpretOpcSwAck(LocoNetMessage l, String turnoutPrefix) { int sw2 = l.getElement(2); if ((sw2 & 0x40) == 0x40) { return ""; } // get system and user names String turnoutUserName = ""; String turnoutSystemName = turnoutPrefix + SENSOR_ADR(l.getElement(1), l.getElement(2)); Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); String uname = ""; if (turnout != null) { uname = turnout.getUserName(); if ((uname != null) && (!uname.isEmpty())) { turnoutUserName = uname; } else { turnoutUserName = ""; } } String pointsDirection = ((sw2 & LnConstants.OPC_SW_ACK_CLOSED) != 0 ? Bundle.getMessage("LN_MSG_SW_POS_CLOSED") : Bundle.getMessage("LN_MSG_SW_POS_THROWN")); String outputState = (((sw2 & LnConstants.OPC_SW_ACK_OUTPUT) != 0) ? Bundle.getMessage("LN_MSG_SENSOR_SW_OUTPUT_STATE_ON") : Bundle.getMessage("LN_MSG_SENSOR_SW_OUTPUT_STATE_OFF")); return Bundle.getMessage("LN_MSG_REQ_SWITCH", turnoutSystemName, turnoutUserName, pointsDirection, outputState); } private static String interpretOpcSwState(LocoNetMessage l, String turnoutPrefix) { // get system and user names if ((l.getElement(2) & 0x40) != 0x00) { return ""; } String turnoutUserName = ""; String turnoutSystemName = turnoutPrefix + SENSOR_ADR(l.getElement(1), l.getElement(2)); Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); String uname = ""; if (turnout != null) { uname = turnout.getUserName(); if ((uname != null) && (!uname.isEmpty())) { turnoutUserName = uname; } else { turnoutUserName = ""; } } return Bundle.getMessage("LN_MSG_SW_STATE", turnoutSystemName, turnoutUserName); } private static String interpretOpcRqSlData(LocoNetMessage l) { int slot = l.getElement(1) + 128 * (l.getElement(2) & 0x07); boolean expSlotRequ = (l.getElement(2) & 0x40) == 0X40 ? true : false; switch (slot) { // Slots > 120 & < 128 are all special, but these are the only ones we know to decode. // Extended System Slots 248 thru 251 delt with seperately, not here case LnConstants.FC_SLOT: return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_FC_SLOT"); case LnConstants.CFG_SLOT: return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_CFG_SLOT"); case LnConstants.CFG_EXT_SLOT: return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_EXT_CFG_SLOT"); case LnConstants.PRG_SLOT: return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_PRG_SLOT"); case 0x79: case 0x7a: case 0x7d: break; default: if (expSlotRequ) { return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_LOCO_EXP_SLOT", slot); } else { return Bundle.getMessage("LN_MSG_SLOT_REQ_SLOT_LOCO_SLOT", slot); } } return ""; } private static String interpretOpcMoveSlots(LocoNetMessage l) { int src = l.getElement(1); int dest = l.getElement(2); if ((src >= 0x79) && (src <= 0x7f)) { return ""; } if ((dest >= 0x79) && (dest <= 0x7f)) { return ""; } /* check special cases */ if (src == 0) { /* DISPATCH GET */ return Bundle.getMessage("LN_MSG_MOVE_SL_GET_DISP"); } else if (src == dest) { /* IN USE */ return Bundle.getMessage("LN_MSG_MOVE_SL_NULL_MOVE", src); } else if (dest == 0) { /* DISPATCH PUT */ return Bundle.getMessage("LN_MSG_MOVE_SL_DISPATCH_PUT", src); } else { /* general move */ return Bundle.getMessage("LN_MSG_MOVE_SL_MOVE", src, dest); } } private static String interpretOpcConsistFunc(LocoNetMessage l) { int slot = l.getElement(1); int dirf = l.getElement(2); if ((dirf & 0x40) == 0x40) { return ""; } return Bundle.getMessage("LN_MSG_CONSIST_FUNC", slot, interpretDIRF(dirf)); } private static String interpretOpcLocoSnd(LocoNetMessage l) { int slot = l.getElement(1); int snd = l.getElement(2); return Bundle.getMessage("LN_MSG_OPC_LOCO_SND", slot, Bundle.getMessage((snd & LnConstants.SND_F5) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), Bundle.getMessage((snd & LnConstants.SND_F6) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), Bundle.getMessage((snd & LnConstants.SND_F7) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), Bundle.getMessage((snd & LnConstants.SND_F8) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")); } protected static String interpretDIRF(int dirf) { if ((dirf & 0x40) == 0x40) { return ""; } String dirf0_4[] = interpretF0_F4toStrings(dirf); return Bundle.getMessage("LN_MSG_HELPER_DIRF", Bundle.getMessage( (dirf & LnConstants.DIRF_DIR) != 0 ? "LN_MSG_DIRECTION_REV" : "LN_MSG_DIRECTION_FWD"), dirf0_4[0], dirf0_4[1], dirf0_4[2], dirf0_4[3], dirf0_4[4]); } private static String interpretOpcLocoDirf(LocoNetMessage l) { int slot = l.getElement(1); int dirf = l.getElement(2); String dirFinfo = interpretDIRF(dirf); if (dirFinfo.length() == 0) { return ""; } return Bundle.getMessage("LN_MSG_OPC_LOCO_DIRF", slot, dirFinfo); } private static String interpretOpcLocoSpd(LocoNetMessage l) { int slot = l.getElement(1); int spd = l.getElement(2); if (spd == LnConstants.OPC_LOCO_SPD_ESTOP) { return Bundle.getMessage("LN_MSG_OPC_LOCO_SPD_ESTOP", slot); } else { return Bundle.getMessage("LN_MSG_OPC_LOCO_SPD_NORMAL", slot, spd); } } private static String interpretOpcPanelQuery(LocoNetMessage l) { switch (l.getElement(1)) { case 0x00: { return Bundle.getMessage("LN_MSG_OPC_DF_TETHERLESS_QUERY"); } case 0x40: { if (l.getElement(2) == 0x1F) { // Some UR devices treat this operation as a set plus query, others // treat this only as a set. return Bundle.getMessage("LN_MSG_OPC_DF_SET_LOCONETID", l.getElement(3)); } break; } default: { break; } } return ""; } private static String interpretOpcSwReq(LocoNetMessage l, String turnoutPrefix) { int sw1 = l.getElement(1); int sw2 = l.getElement(2); if ((sw2 & 0x40) == 0x40) { return ""; } if ((!(((sw2 & 0xCF) == 0x0F) && ((sw1 & 0xFC) == 0x78))) && (!(((sw2 & 0xCF) == 0x07) && ((sw1 & 0xFC) == 0x78)))) { // ordinary form, LPU V1.0 page 9 // handle cases which are not "stationary decoder interrogate" messages // get system and user names String turnoutUserName = ""; String turnoutSystemName = turnoutPrefix + SENSOR_ADR(l.getElement(1), l.getElement(2)); Turnout turnout = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnoutSystemName); String uname = ""; if (turnout != null) { uname = turnout.getUserName(); if ((uname != null) && (!uname.isEmpty())) { turnoutUserName = uname; } else { turnoutUserName = ""; } } String pointsDirection = ((sw2 & LnConstants.OPC_SW_ACK_CLOSED) != 0 ? Bundle.getMessage("LN_MSG_SW_POS_CLOSED") : Bundle.getMessage("LN_MSG_SW_POS_THROWN")); String outputState = ((sw2 & LnConstants.OPC_SW_ACK_OUTPUT) != 0 ? Bundle.getMessage("LN_MSG_SW_OUTPUT_STATE_ON") : Bundle.getMessage("LN_MSG_SW_OUTPUT_STATE_OFF")); if (turnoutUserName.length() == 0) { return Bundle.getMessage("LN_MSG_OPC_SW_REQ_NORMAL_WITHOUT_USERNAME", turnoutSystemName, pointsDirection, outputState); } else { return Bundle.getMessage("LN_MSG_OPC_SW_REQ_NORMAL_WITH_USERNAME", turnoutSystemName, turnoutUserName, pointsDirection, outputState); } } /* Handle cases which are "stationary decoder interrogate" messages. */ /* * Decodes a/c/b bits to allow proper creation of a list of addresses * which ought to reply to the "stationary decoder interrogate" message. */ int a = (sw2 & 0x20) >> 5; int c = (sw1 & 0x02) >> 1; int b = (sw1 & 0x01); /* * All this blob does is loop through the ranges indicated by the * a/c/b bits, they are mask bits in the midde of the range. The * idea is to get 8 sensors at a time, since that is generally what * units have, and to query units 1, 9, 17... then 2, 10, 18... and * so on such that if they are all in a row they don't get hit at * the same time. */ int topbits = 0; int midbits = (a << 2) + (c << 1) + b; int count = 0; StringBuilder addrListB = new StringBuilder(); for (topbits = 0; topbits < 32; topbits++) { // The extra "+1" adjusts for the fact that we show 1-2048, // rather than 0-2047 on the wire. int lval = (topbits << 6) + (midbits << 3) + 1; int hval = lval + 7; if ((count % 8) != 0) { addrListB.append(", "); // NOI18N } else { if (count == 0) { addrListB.append("\t"); // NOI18N } else { addrListB.append(",\n\t"); // NOI18N } } addrListB.append("").append(lval); // NOI18N addrListB.append("-").append(hval); // NOI18N count++; } String addrList = addrListB.toString(); if (((sw2 & 0xCF) == 0x0F) && ((sw1 & 0xFC) == 0x78)) { // broadcast address LPU V1.0 page 12 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_INTERROGATE_TURNOUTS", a, c, b, addrList); } else { // broadcast address LPU V1.0 page 13 return Bundle.getMessage("LN_MSG_OPC_SW_REQ_INTERROGATE_SENSORS_TURNOUTS", a, c, b, addrList); } } private static String interpretFastClockSlot(LocoNetMessage l, String mode, int id1, int id2) { /** * FAST Clock: The system FAST clock and parameters are implemented in * Slot#123 <7B>. Use <EF> to write new clock information, Slot read of * 0x7B,<BB><7B>.., will return current System clock information, and * other throttles will update to this SYNC. Note that all attached * display devices keep a current clock calculation based on this SYNC * read value, i.e. devices MUST not continuously poll the clock SLOT to * generate time, but use this merely to restore SYNC and follow current * RATE etc. This clock slot is typically "pinged" * or read SYNC'd * every 70 to 100 seconds, by a single user, so all attached devices * can synchronise any phase drifts. Upon seeing a SYNC read, all * devices should reset their local sub-minute phase counter and * invalidate the SYNC update ping generator. * <p> * Clock Slot Format: * <p> * <0xEF>,<0E>,<7B>,<CLK_RATE>,<FRAC_MINSL>,<FRAC_MINSH>,<256-MINS_60>, * <TRK><256-HRS_24>,<DAYS>,<CLK_CNTRL>,<ID1>,<1D2>,<CHK> * <p> * where: * <p> * <CLK_RATE> 0=Freeze clock, * 1=normal 1:1 rate, 10=10:1 etc, max * VALUE is 7F/128 to 1 * <p> * <FRAC_MINSL> FRAC mins hi/lo are a sub-minute counter, depending on * the CLOCK generator * <p> * <FRAC_MINSH> Not for ext. usage. This counter is reset when valid * <E6><7B> * SYNC message is seen * <p> * <256-MINS_60> This is FAST clock MINUTES subtracted from 256. Modulo * 0-59 * <p> * <256-HRS_24> This is FAST clock HOURS subtracted from 256. Modulo * 0-23 * <p> * <DAYS> number of 24 Hr clock rolls, positive count * <p> * <CLK_CNTRL> Clock Control Byte D6- 1=This is valid Clock information, * 0=ignore this <E6><7B>, SYNC reply * <p> * <ID1>,<1D2> This is device ID last setting the clock. * <p> * <00><00> shows no set has happened * <p> * <7F><7x> are reserved for PC access * */ int minutes; // temporary time values int hours; int clk_rate = l.getElement(3); // 0 = Freeze clock, 1 = normal, // 10 = 10:1 etc. Max is 0x7f int mins_60 = l.getElement(6); // 256 - minutes int track_stat = l.getElement(7); // track status int hours_24 = l.getElement(8); // 256 - hours int days = l.getElement(9); // clock rollovers int clk_cntrl = l.getElement(10); // bit 6 = 1; data is valid // clock info // " " 0; ignore this reply // id1/id2 is device id of last device to set the clock // " " = zero shows not set has happened /* recover hours and minutes values */ minutes = ((255 - mins_60) & 0x7f) % 60; hours = ((256 - hours_24) & 0x7f) % 24; hours = (24 - hours) % 24; minutes = (60 - minutes) % 60; return Bundle.getMessage("LN_MSG_SLOT_ACCESS_FAST_CLOCK", mode, ((clk_cntrl & 0x20) != 0 ? "" : Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_SYNC")), (clk_rate != 0 ? Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_RUNNING") : Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_FROZEN")), clk_rate, days, fcTimeToString(hours, minutes), idString(id1, id2), trackStatusByteToString(track_stat)); } private static String interpretProgSlot(LocoNetMessage l, String mode, int id1, int id2, int command) { /** * ******************************************************************************************** * Programmer track: * ================= * The programmer track is * accessed as Special slot #124 ( $7C, 0x7C). It is a full * * asynchronous shared system resource. * * To start Programmer task, * write to slot 124. There will be an immediate LACK acknowledge * that * indicates what programming will be allowed. If a valid programming * task is started, * then at the final (asynchronous) programming * completion, a Slot read <E7> from slot 124 * will be sent. This is * the final task status reply. * * Programmer Task Start: * * ---------------------- * <p> * <0xEF>,<0E>,<7C>,<PCMD>,<0>,<HOPSA>,<LOPSA>,<TRK>;<CVH>,<CVL>, * <p> * <DATA7>,<0>,<0>,<CHK> * * This OPC leads to immediate LACK codes: * <p> * <B4>,<7F>,<7F>,<chk> Function NOT implemented, no reply. * <p> * <B4>,<7F>,<0>,<chk> Programmer BUSY , task aborted, no reply. * <p> * <B4>,<7F>,<1>,<chk> Task accepted , <E7> reply at completion. * <p> * <B4>,<7F>,<0x40>,<chk> Task accepted blind NO <E7> * reply at completion. * * Note that the <7F> code will occur in * Operations Mode Read requests if the System is not * configured for * and has no Advanced Acknowlegement detection installed.. Operations * Mode * requests can be made and executed whilst a current Service * Mode programming task is keeping * the Programming track BUSY. If a * Programming request is rejected, delay and resend the * complete * request later. Some readback operations can keep the Programming * track busy for up * to a minute. Multiple devices, throttles/PC's * etc, can share and sequentially use the * Programming track as long * as they correctly interpret the response messages. Any Slot RD * from * the master will also contain the Programmer Busy status in bit 3 of * the <TRK> byte. * * A <PCMD> value of * <00> will abort current SERVICE mode programming task and will echo * with * an <E6> RD the command string that was aborted. * * <PCMD> * Programmer Command: * -------------------------- * Defined as * D7 -0 * * D6 -Write/Read 1= Write, * 0=Read * D5 -Byte Mode 1= Byte * operation, * 0=Bit operation (if possible) * D4 -TY1 Programming Type * select bit * D3 -TY0 Prog type select bit * D2 -Ops Mode 1=Ops Mode * on Mainlines, * 0=Service Mode on Programming Track * D1 -0 reserved * * D0 -0-reserved * * Type codes: * ----------- * Byte Mode Ops Mode * TY1 TY0 Meaning * 1 0 0 0 Paged mode byte Read/Write on Service Track * * 1 0 0 0 Paged mode byte Read/Write on Service Track * 1 0 0 1 * Direct mode byteRead/Write on Service Track * 0 0 0 1 Direct mode bit * Read/Write on Service Track * x 0 1 0 Physical Register byte * Read/Write on Service Track * x 0 1 1 Service Track- reserved * function * 1 1 0 0 Ops mode Byte program, no feedback * 1 1 0 1 Ops * mode Byte program, feedback * 0 1 0 0 Ops mode Bit program, no * feedback * 0 1 0 1 Ops mode Bit program, feedback * * * <HOPSA>Operations Mode Programming * 7 High address bits of Loco to * program, 0 if Service Mode * <p> * <LOPSA>Operations Mode Programming * 7 Low address bits of Loco to * program, 0 if Service Mode * <p> * <TRK> Normal Global Track status for this Master, * Bit 3 also is 1 * WHEN Service Mode track is BUSY * <p> * <CVH> High 3 BITS of CV#, and ms bit of DATA.7 * <p> * <0,0,CV9,CV8 - 0,0, D7,CV7> * <p> * <CVL> Low 7 bits of 10 bit CV address. * <p> * <0,CV6,CV5,CV4-CV3,CV2,CV1,CV0> * <p> * <DATA7>Low 7 BITS OF data to WR or RD COMPARE * <p> * <0,D6,D5,D4 - D3,D2,D1,D0> * ms bit is at CVH bit 1 position. * * * Programmer Task Final Reply: * ---------------------------- * (if saw * LACK * <B4>,<7F>,<1>,<chk> code reply at task start) * <p> * <0xE7>,<0E>,<7C>,<PCMD>,<PSTAT>,<HOPSA>,<LOPSA>,<TRK>;<CVH>,<CVL>, * <p> * <DATA7>,<0>,<0>,<CHK> * * <PSTAT> Programmer Status error flags. * Reply codes resulting from * completed task in PCMD * D7-D4 -reserved * * D3 -1= User Aborted this command * D2 -1= Failed to detect READ * Compare acknowledge response * from decoder * D1 -1= No Write * acknowledge response from decoder * D0 -1= Service Mode programming * track empty- No decoder detected * * This <E7> response is issued * whenever a Programming task is completed. It echos most of the * * request information and returns the PSTAT status code to indicate how * the task completed. * If a READ was requested <DATA7> and <CVH> * contain the returned data, if the PSTAT indicates * a successful * readback (typically =0). Note that if a Paged Read fails to detect a * * successful Page write acknowledge when first setting the Page * register, the read will be * aborted, showing no Write acknowledge * flag D1=1. * * ******************************************************************************************** */ int cvData; int cvNumber; // progTask = (progTaskMsg *) msgBuf; // slot - slot number for this request - slot 124 is programmer int pcmd = l.getElement(3); // programmer command int pstat = l.getElement(4); // programmer status error flags in // reply message int hopsa = l.getElement(5); // Ops mode - 7 high address bits // of loco to program int lopsa = l.getElement(6); // Ops mode - 7 low address bits of // loco to program /* trk - track status. Note: bit 3 shows if prog track is busy */ int cvh = l.getElement(8); // hi 3 bits of CV# and msb of data7 int cvl = l.getElement(9); // lo 7 bits of CV# int data7 = l.getElement(10); // 7 bits of data to program, msb // is in cvh above cvData = (((cvh & LnConstants.CVH_D7) << 6) | (data7 & 0x7f)); // was // PROG_DATA cvNumber = (((((cvh & LnConstants.CVH_CV8_CV9) >> 3) | (cvh & LnConstants.CVH_CV7)) * 128) + (cvl & 0x7f)) + 1; // was // PROG_CV_NUM(progTask) if (command == LnConstants.OPC_WR_SL_DATA) { /* interpret the programming mode request (to programmer) */ switch ((pcmd & (LnConstants.PCMD_MODE_MASK | LnConstants.PCMD_RW))) { case LnConstants.PAGED_ON_SRVC_TRK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_PAGED_RD", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.PAGED_ON_SRVC_TRK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_PAGED_WR", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case LnConstants.DIR_BYTE_ON_SRVC_TRK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BYTE_RD", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.DIR_BYTE_ON_SRVC_TRK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BYTE_WR", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case LnConstants.DIR_BIT_ON_SRVC_TRK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BIT_RD", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber), cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true)); case LnConstants.DIR_BIT_ON_SRVC_TRK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_DIR_BIT_WR", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case LnConstants.REG_BYTE_RW_ON_SRVC_TRK: case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_REG_BYTE_RD", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_RW: case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_REG_BYTE_WR", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case LnConstants.SRVC_TRK_RESERVED: case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_RD_RESERVED", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_RW: case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_SRVC_TRK_WR_RESERVED", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case LnConstants.OPS_BYTE_NO_FEEDBACK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_RD_NO_FEEDBACK", convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.OPS_BYTE_NO_FEEDBACK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_WR_NO_FEEDBACK", convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case LnConstants.OPS_BYTE_FEEDBACK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_RD_FEEDBACK", convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.OPS_BYTE_FEEDBACK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BYTE_WR_FEEDBACK", convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case LnConstants.OPS_BIT_NO_FEEDBACK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_RD_NO_FEEDBACK", convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.OPS_BIT_NO_FEEDBACK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_WR_NO_FEEDBACK", convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case LnConstants.OPS_BIT_FEEDBACK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_RD_FEEDBACK", convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.OPS_BIT_FEEDBACK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_OPS_BIT_WR_FEEDBACK", convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); case 0: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UHLENBROCK_RD", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_READ_REQ", cvNumber)); case LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UHLENBROCK_WR", Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); default: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_REQUEST_UNKNOWN", pcmd, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(pcmd)), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_WRITE_REQ", cvNumber, cvData, Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))); } } else { /* interpret the programming mode response (from programmer) */ /* if we're reading the slot back, check the status * this is supposed to be the Programming task final reply * and will have the resulting status byte. */ String responseMessage = "(ODD BEHAVIOR - Default value not overwritten - report to developers!"; // NOI18N String hexMessage = ""; if (pstat != 0) { if ((pstat & LnConstants.PSTAT_USER_ABORTED) != 0) { responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_USER_ABORT"); } else if ((pstat & LnConstants.PSTAT_READ_FAIL) != 0) { responseMessage = Bundle .getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_READ_COMPARE_ACK_DETECT"); } else if ((pstat & LnConstants.PSTAT_WRITE_FAIL) != 0) { responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_WRITE_ACK_DETECT"); } else if ((pstat & LnConstants.PSTAT_NO_DECODER) != 0) { responseMessage = Bundle .getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_NO_LOCO_ON_PROGRAMMING_TRACK"); } else if ((pstat & 0xF0) != 0) { if ((pstat & 0xF0) == 0x10) { // response from transponding decoder responseMessage = Bundle .getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_SUCCESS_VIA_RX4_BDL16X"); } else { responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_UNDECODED", Bundle .getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(pstat))); hexMessage = Bundle.getMessage("LN_MONITOR_MESSGAGE_RAW_HEX_INFO", l.toString()); } } } else { responseMessage = Bundle.getMessage("LN_MSG_SLOT_PROG_HELPER_RESPONSE_SUCCEEDED"); } switch ((pcmd & (LnConstants.PCMD_MODE_MASK | LnConstants.PCMD_RW))) { case LnConstants.PAGED_ON_SRVC_TRK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_PAGED_RD", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.PAGED_ON_SRVC_TRK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_PAGED_WR", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.DIR_BYTE_ON_SRVC_TRK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BYTE_RD", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.DIR_BYTE_ON_SRVC_TRK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BYTE_WR", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.DIR_BIT_ON_SRVC_TRK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BIT_RD", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.DIR_BIT_ON_SRVC_TRK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_DIR_BIT_WR", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.REG_BYTE_RW_ON_SRVC_TRK: case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_REG_BYTE_RD", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_RW: case LnConstants.REG_BYTE_RW_ON_SRVC_TRK | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_REG_BYTE_WR", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.SRVC_TRK_RESERVED: case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_RD_RESERVED", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_RW: case LnConstants.SRVC_TRK_RESERVED | LnConstants.PCMD_BYTE_MODE | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_SRVC_TRK_WR_RESERVED", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.OPS_BYTE_NO_FEEDBACK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_RD_NO_FEEDBACK", responseMessage, convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.OPS_BYTE_NO_FEEDBACK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_WR_NO_FEEDBACK", responseMessage, convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.OPS_BYTE_FEEDBACK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_RD_FEEDBACK", responseMessage, convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.OPS_BYTE_FEEDBACK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BYTE_WR_FEEDBACK", responseMessage, convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.OPS_BIT_NO_FEEDBACK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_RD_NO_FEEDBACK", responseMessage, convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.OPS_BIT_NO_FEEDBACK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_WR_NO_FEEDBACK", responseMessage, convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.OPS_BIT_FEEDBACK: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_RD_FEEDBACK", responseMessage, convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.OPS_BIT_FEEDBACK | LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_OPS_BIT_WR_FEEDBACK", responseMessage, convertToMixed(lopsa, hopsa), Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case 0: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UHLENBROCK_RD", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; case LnConstants.PCMD_RW: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UHLENBROCK_WR", responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; default: return Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_RESPONSE_UNKNOWN", pcmd, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(pcmd)), responseMessage, Bundle.getMessage("LN_MSG_SLOT_PROG_MODE_CV_INFO_HELPER_REPLY", cvNumber, cvData, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(cvData)), StringUtil.to8Bits(cvData, true))) + hexMessage; } } } private static String interpretCmdStnCfgSlotRdWr(LocoNetMessage l, int command) { /** * ************************************************ * Configuration slot, holding op switches * ************************************************ * <p> * NOTE: previously, this message provided specific text about the * meaning of each OpSw when it was closed. With the advent of newer * Digitrax command stations, the specific information was no longer * completely accurate. As such, this information now only shows bits as * "closed" or "thrown". */ String thrown = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); String closed = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED"); String opswGroup1, opswGroup2, opswGroup3, opswGroup4, opswGroup5, opswGroup6, opswGroup7, opswGroup8; opswGroup1 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 1, ((l.getElement(3) & 0x01) != 0 ? closed : thrown), 2, ((l.getElement(3) & 0x02) != 0 ? closed : thrown), 3, ((l.getElement(3) & 0x04) != 0 ? closed : thrown), 4, ((l.getElement(3) & 0x08) != 0 ? closed : thrown), 5, ((l.getElement(3) & 0x10) != 0 ? closed : thrown), 6, ((l.getElement(3) & 0x20) != 0 ? closed : thrown), 7, ((l.getElement(3) & 0x40) != 0 ? closed : thrown), 8, thrown); opswGroup2 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 9, ((l.getElement(4) & 0x01) != 0 ? closed : thrown), 10, ((l.getElement(4) & 0x02) != 0 ? closed : thrown), 11, ((l.getElement(4) & 0x04) != 0 ? closed : thrown), 12, ((l.getElement(4) & 0x08) != 0 ? closed : thrown), 13, ((l.getElement(4) & 0x10) != 0 ? closed : thrown), 14, ((l.getElement(4) & 0x20) != 0 ? closed : thrown), 15, ((l.getElement(4) & 0x40) != 0 ? closed : thrown), 16, thrown); opswGroup3 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 17, ((l.getElement(5) & 0x01) != 0 ? closed : thrown), 18, ((l.getElement(5) & 0x02) != 0 ? closed : thrown), 19, ((l.getElement(5) & 0x04) != 0 ? closed : thrown), 20, ((l.getElement(5) & 0x08) != 0 ? closed : thrown), 21, ((l.getElement(5) & 0x10) != 0 ? closed : thrown), 22, ((l.getElement(5) & 0x20) != 0 ? closed : thrown), 23, ((l.getElement(5) & 0x40) != 0 ? closed : thrown), 24, thrown); opswGroup4 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 25, ((l.getElement(6) & 0x01) != 0 ? closed : thrown), 26, ((l.getElement(6) & 0x02) != 0 ? closed : thrown), 27, ((l.getElement(6) & 0x04) != 0 ? closed : thrown), 28, ((l.getElement(6) & 0x08) != 0 ? closed : thrown), 29, ((l.getElement(6) & 0x10) != 0 ? closed : thrown), 30, ((l.getElement(6) & 0x20) != 0 ? closed : thrown), 31, ((l.getElement(6) & 0x40) != 0 ? closed : thrown), 32, thrown); opswGroup5 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 33, ((l.getElement(8) & 0x01) != 0 ? closed : thrown), 34, ((l.getElement(8) & 0x02) != 0 ? closed : thrown), 35, ((l.getElement(8) & 0x04) != 0 ? closed : thrown), 36, ((l.getElement(8) & 0x08) != 0 ? closed : thrown), 37, ((l.getElement(8) & 0x10) != 0 ? closed : thrown), 38, ((l.getElement(8) & 0x20) != 0 ? closed : thrown), 39, ((l.getElement(8) & 0x40) != 0 ? closed : thrown), 40, thrown); opswGroup6 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 41, ((l.getElement(9) & 0x01) != 0 ? closed : thrown), 42, ((l.getElement(9) & 0x02) != 0 ? closed : thrown), 43, ((l.getElement(9) & 0x04) != 0 ? closed : thrown), 44, ((l.getElement(9) & 0x08) != 0 ? closed : thrown), 45, ((l.getElement(9) & 0x10) != 0 ? closed : thrown), 46, ((l.getElement(9) & 0x20) != 0 ? closed : thrown), 47, ((l.getElement(9) & 0x40) != 0 ? closed : thrown), 48, thrown); opswGroup7 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 49, ((l.getElement(10) & 0x01) != 0 ? closed : thrown), 50, ((l.getElement(10) & 0x02) != 0 ? closed : thrown), 51, ((l.getElement(10) & 0x04) != 0 ? closed : thrown), 52, ((l.getElement(10) & 0x08) != 0 ? closed : thrown), 53, ((l.getElement(10) & 0x10) != 0 ? closed : thrown), 54, ((l.getElement(10) & 0x20) != 0 ? closed : thrown), 55, ((l.getElement(10) & 0x40) != 0 ? closed : thrown), 56, thrown); opswGroup8 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 57, ((l.getElement(11) & 0x01) != 0 ? closed : thrown), 58, ((l.getElement(11) & 0x02) != 0 ? closed : thrown), 59, ((l.getElement(11) & 0x04) != 0 ? closed : thrown), 60, ((l.getElement(11) & 0x08) != 0 ? closed : thrown), 61, ((l.getElement(11) & 0x10) != 0 ? closed : thrown), 62, ((l.getElement(11) & 0x20) != 0 ? closed : thrown), 63, ((l.getElement(11) & 0x40) != 0 ? closed : thrown), 64, thrown); return Bundle.getMessage( ((command == LnConstants.OPC_WR_SL_DATA) ? "LN_MSG_SLOT_CMD_STN_CFG_WRITE_REQ" : "LN_MSG_SLOT_CMD_STN_CFG_READ_REPORT"), opswGroup1, opswGroup2, opswGroup3, opswGroup4, opswGroup5, opswGroup6, opswGroup7, opswGroup8); } private static String interpretCmdStnExtCfgSlotRdWr(LocoNetMessage l, int command) { /* * ************************************************ * Extended Configuration slot, holding op switches * ************************************************ */ String thrown = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_THROWN"); String closed = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_OPSW_HELPER_CLOSED"); String opswGroup1, opswGroup2, opswGroup3, opswGroup4, opswGroup5, opswGroup6, opswGroup7, opswGroup8; opswGroup1 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 65, ((l.getElement(3) & 0x01) != 0 ? closed : thrown), 66, ((l.getElement(3) & 0x02) != 0 ? closed : thrown), 67, ((l.getElement(3) & 0x04) != 0 ? closed : thrown), 68, ((l.getElement(3) & 0x08) != 0 ? closed : thrown), 69, ((l.getElement(3) & 0x10) != 0 ? closed : thrown), 70, ((l.getElement(3) & 0x20) != 0 ? closed : thrown), 71, ((l.getElement(3) & 0x40) != 0 ? closed : thrown), 72, thrown); opswGroup2 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 73, ((l.getElement(4) & 0x01) != 0 ? closed : thrown), 74, ((l.getElement(4) & 0x02) != 0 ? closed : thrown), 75, ((l.getElement(4) & 0x04) != 0 ? closed : thrown), 76, ((l.getElement(4) & 0x08) != 0 ? closed : thrown), 77, ((l.getElement(4) & 0x10) != 0 ? closed : thrown), 78, ((l.getElement(4) & 0x20) != 0 ? closed : thrown), 79, ((l.getElement(4) & 0x40) != 0 ? closed : thrown), 80, thrown); opswGroup3 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 81, ((l.getElement(5) & 0x01) != 0 ? closed : thrown), 82, ((l.getElement(5) & 0x02) != 0 ? closed : thrown), 83, ((l.getElement(5) & 0x04) != 0 ? closed : thrown), 84, ((l.getElement(5) & 0x08) != 0 ? closed : thrown), 85, ((l.getElement(5) & 0x10) != 0 ? closed : thrown), 86, ((l.getElement(5) & 0x20) != 0 ? closed : thrown), 87, ((l.getElement(5) & 0x40) != 0 ? closed : thrown), 88, thrown); opswGroup4 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 89, ((l.getElement(6) & 0x01) != 0 ? closed : thrown), 90, ((l.getElement(6) & 0x02) != 0 ? closed : thrown), 91, ((l.getElement(6) & 0x04) != 0 ? closed : thrown), 92, ((l.getElement(6) & 0x08) != 0 ? closed : thrown), 93, ((l.getElement(6) & 0x10) != 0 ? closed : thrown), 94, ((l.getElement(6) & 0x20) != 0 ? closed : thrown), 95, ((l.getElement(6) & 0x40) != 0 ? closed : thrown), 96, thrown); opswGroup5 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 97, ((l.getElement(8) & 0x01) != 0 ? closed : thrown), 98, ((l.getElement(8) & 0x02) != 0 ? closed : thrown), 99, ((l.getElement(8) & 0x04) != 0 ? closed : thrown), 100, ((l.getElement(8) & 0x08) != 0 ? closed : thrown), 101, ((l.getElement(8) & 0x10) != 0 ? closed : thrown), 102, ((l.getElement(8) & 0x20) != 0 ? closed : thrown), 103, ((l.getElement(8) & 0x40) != 0 ? closed : thrown), 104, thrown); opswGroup6 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 105, ((l.getElement(9) & 0x01) != 0 ? closed : thrown), 106, ((l.getElement(9) & 0x02) != 0 ? closed : thrown), 107, ((l.getElement(9) & 0x04) != 0 ? closed : thrown), 108, ((l.getElement(9) & 0x08) != 0 ? closed : thrown), 109, ((l.getElement(9) & 0x10) != 0 ? closed : thrown), 110, ((l.getElement(9) & 0x20) != 0 ? closed : thrown), 111, ((l.getElement(9) & 0x40) != 0 ? closed : thrown), 112, thrown); opswGroup7 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 113, ((l.getElement(10) & 0x01) != 0 ? closed : thrown), 114, ((l.getElement(10) & 0x02) != 0 ? closed : thrown), 115, ((l.getElement(10) & 0x04) != 0 ? closed : thrown), 116, ((l.getElement(10) & 0x08) != 0 ? closed : thrown), 117, ((l.getElement(10) & 0x10) != 0 ? closed : thrown), 118, ((l.getElement(10) & 0x20) != 0 ? closed : thrown), 119, ((l.getElement(10) & 0x40) != 0 ? closed : thrown), 120, thrown); opswGroup8 = Bundle.getMessage("LN_MSG_SLOT_CMD_STN_CFG_HELPER_EIGHT_OPSWS", 121, ((l.getElement(11) & 0x01) != 0 ? closed : thrown), 122, ((l.getElement(11) & 0x02) != 0 ? closed : thrown), 123, ((l.getElement(11) & 0x04) != 0 ? closed : thrown), 124, ((l.getElement(11) & 0x08) != 0 ? closed : thrown), 125, ((l.getElement(11) & 0x10) != 0 ? closed : thrown), 126, ((l.getElement(11) & 0x20) != 0 ? closed : thrown), 127, ((l.getElement(11) & 0x40) != 0 ? closed : thrown), 128, thrown); return Bundle.getMessage( ((command == LnConstants.OPC_WR_SL_DATA) ? "LN_MSG_SLOT_CMD_STN_EXT_CFG_WRITE_REQ" : "LN_MSG_SLOT_CMD_STN_EXT_CFG_READ_REPORT"), opswGroup1, opswGroup2, opswGroup3, opswGroup4, opswGroup5, opswGroup6, opswGroup7, opswGroup8); } private static String interpretStandardSlotRdWr(LocoNetMessage l, int id1, int id2, int command, int slot) { /** * ************************************************ * normal slot read/write message - see info above * * ************************************************ */ int trackStatus = l.getElement(7); // track status int stat = l.getElement(3); // slot status int adr = l.getElement(4); // loco address int spd = l.getElement(5); // command speed int dirf = l.getElement(6); // direction and F0-F4 bits String[] dirf0_4 = interpretF0_F4toStrings(dirf); int ss2 = l.getElement(8); // slot status 2 (tells how to use // ID1/ID2 & ADV Consist) int adr2 = l.getElement(9); // loco address high int snd = l.getElement(10); // Sound 1-4 / F5-F8 String[] sndf5_8 = interpretF5_F8toStrings(snd); String locoAdrStr = figureAddressIncludingAliasing(adr, adr2, ss2, id1, id2); return Bundle.getMessage( ((command == LnConstants.OPC_WR_SL_DATA) ? "LN_MSG_SLOT_LOCO_INFO_WRITE" : "LN_MSG_SLOT_LOCO_INFO_READ"), slot, locoAdrStr, LnConstants.CONSIST_STAT(stat), LnConstants.LOCO_STAT(stat), LnConstants.DEC_MODE(stat), directionOfTravelString((dirf & LnConstants.DIRF_DIR) == 0), spd, // needs re-interpretation for some cases of slot consisting state dirf0_4[0], dirf0_4[1], dirf0_4[2], dirf0_4[3], dirf0_4[4], sndf5_8[0], sndf5_8[1], sndf5_8[2], sndf5_8[3], trackStatusByteToString(trackStatus), Bundle.getMessage("LN_MSG_SLOT_HELPER_SS2_SIMPLE", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(ss2))), Bundle.getMessage("LN_MSG_SLOT_HELPER_ID1_ID2_AS_THROTTLE_ID", idString(id1, id2))); } private static String interpretOpcPanelResponse(LocoNetMessage l) { switch (l.getElement(1)) { case 0x12: { // Bit 3 (0x08 in hex) is set by every UR-92 we've ever captured. // The hypothesis is this indicates duplex enabled, but this has // not been confirmed with Digitrax. return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR92", l.getElement(3) & 0x07, ((l.getElement(3) & 0x08) == 0x08 ? Bundle.getMessage("LN_MSG_HELPER_D7_UR92_DUPLEX") : "")); } case 0x17: { return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR90", l.getElement(3) & 0x07); } case 0x1F: { return Bundle.getMessage("LN_MSG_OPC_D7_TETHERLESS_REPORT_UR91", l.getElement(3) & 0x07); } default: { return ""; } } } private static String interpretOpcLissyUpdate(LocoNetMessage l) { /* * OPC_LISSY_UPDATE 0xE4 * * LISSY is an automatic train detection system made by Uhlenbrock. * All documentation appears to be in German. */ switch (l.getElement(1)) { case 0x08: // Format LISSY message int unit = (l.getElement(4) & 0x7F); int address = (l.getElement(6) & 0x7F) + 128 * (l.getElement(5) & 0x7F); switch (l.getElement(2)) { case 0x00: // Reverse-engineering note: interpretation of element 2 per wiki.rocrail.net // OPC_LISSY_REP return Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_LOCO_MOVEMENT", unit, Integer.toString(address), ((l.getElement(3) & 0x20) == 0 ? Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_DIRECTION_NORTH") : Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_DIRECTION_SOUTH"))); case 0x01: // Reverse-engineering note: interpretation of element 2 per wiki.rocrail.net // OPC_WHEELCNT_REP int wheelCount = (l.getElement(6) & 0x7F) + 128 * (l.getElement(5) & 0x7F); return Bundle.getMessage("LN_MSG_LISSY_WHEEL_REPORT_LOCO_MOVEMENT", unit, Integer.toString(wheelCount), ((l.getElement(3) & 0x20) == 0 ? Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_DIRECTION_NORTH") : Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_DIRECTION_SOUTH"))); default: break; } break; case 0x0A: // Format special message int element = l.getElement(2) * 128 + l.getElement(3); int stat1 = l.getElement(5); int stat2 = l.getElement(6); String status; switch (stat1 & 0x30) { case 0x30: status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_BOTH_RES"); break; case 0x10: status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_AX_RES"); break; case 0x20: status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_XA_RES"); break; default: status = Bundle.getMessage("LN_MSG_SE_REPORT_HELPER_NO_RES"); break; } return Bundle.getMessage("LN_MSG_SE_REPORT", (element + 1), element, l.getElement(7), l.getElement(8), status, Bundle.getMessage( ((stat2 & 0x01) != 0) ? "LN_MSG_SWITCH_STATE_THROWN" : "LN_MSG_SWITCH_STATE_CLOSED"), Bundle.getMessage(((stat1 & 0x01) != 0) ? "LN_MSG_SE_REPORT_HELPER_OCCUPIED" : "LN_MSG_SE_REPORT_HELPER_UNOCCUPIED")); case 0x09: if (l.getElement(4) == 0x00) { return Bundle.getMessage("LN_MSG_UNRECOGNIZED_SIG_STATE_REPORT_MAY_BE_FROM_CML_HW") + Bundle.getMessage("LN_MONITOR_MESSGAGE_RAW_HEX_INFO", l.toString()); } break; default: break; } return ""; } private static String interpretOpcImmPacket(LocoNetMessage l) { /* * OPC_IMM_PACKET 0xED ;SEND n-byte packet immediate LACK * ; Follow on message: LACK * ; <0xED>,<0B>,<7F>,<REPS>,<DHI>,<IM1>,<IM2>, * ; <IM3>,<IM4>,<IM5>,<CHK> * ; <DHI>=<0,0,1,IM5.7-IM4.7,IM3.7,IM2.7,IM1.7> * ; <REPS> D4,5,6=#IM bytes, * ; D3=0(reserved); * ; D2,1,0=repeat CNT * ; IF Not limited MASTER then * ; LACK=<B4>,<7D>,<7F>,<chk> if CMD ok * ; IF limited MASTER then Lim Masters respond * ; with <B4>,<7E>,<lim adr>,<chk> * ; IF internal buffer BUSY/full respond * ; with <B4>,<7D>,<0>,<chk> * ; (NOT IMPLEMENTED IN DT200) * * This sends a raw NMRA packet across the LocoNet. * * Page 11 of LocoNet Personal Edition v1.0. * * Decodes for the F9-F28 functions taken from the NMRA standards and * coded by Leo Bicknell. */ // sendPkt = (sendPktMsg *) msgBuf; int val7f = l.getElement(2); /* fixed value of 0x7f */ int reps = l.getElement(3); /* repeat count */ int dhi = l.getElement(4); /* high bits of data bytes */ int im1 = l.getElement(5); int im2 = l.getElement(6); int im3 = l.getElement(7); int im4 = l.getElement(8); int im5 = l.getElement(9); int mobileDecoderAddress = -999; int nmraInstructionType = -999; int nmraSubInstructionType = -999; int playableWhistleLevel = -999; // see if it really is a 'Send Packet' as defined in LocoNet PE if ((val7f == 0x7f) && (l.getElement(1) == 0x0B)) { int len = ((reps & 0x70) >> 4); if (len < 2) { return ""; // no valid NMRA packets of less than 2 bytes. } // duplication of packet data as packetInt was deemed necessary // due to issues with msBit loss when converting from "byte" to // integral forms byte[] packet = new byte[len]; int[] packetInt = new int[len]; packet[0] = (byte) (im1 + ((dhi & 0x01) != 0 ? 0x80 : 0)); packetInt[0] = (im1 + ((dhi & 0x01) != 0 ? 0x80 : 0)); if (len >= 2) { packet[1] = (byte) (im2 + ((dhi & 0x02) != 0 ? 0x80 : 0)); packetInt[1] = (im2 + ((dhi & 0x02) != 0 ? 0x80 : 0)); } if (len >= 3) { packet[2] = (byte) (im3 + ((dhi & 0x04) != 0 ? 0x80 : 0)); packetInt[2] = (im3 + ((dhi & 0x04) != 0 ? 0x80 : 0)); } if (len >= 4) { packet[3] = (byte) (im4 + ((dhi & 0x08) != 0 ? 0x80 : 0)); packetInt[3] = (im4 + ((dhi & 0x08) != 0 ? 0x80 : 0)); } if (len >= 5) { packet[4] = (byte) (im5 + ((dhi & 0x10) != 0 ? 0x80 : 0)); packetInt[4] = (im5 + ((dhi & 0x10) != 0 ? 0x80 : 0)); } int address; // compute some information which is useful for decoding // the "Playable" whistle message // Information reverse-engineered by B. Milhaupt and used with permission if ((packetInt[0] & 0x80) == 0x0) { // immediate packet addresses a 7-bit multi-function (mobile) decoder mobileDecoderAddress = packetInt[0]; nmraInstructionType = (packetInt[1] & 0xE) >> 5; nmraSubInstructionType = (packetInt[1] & 0x1f); if ((nmraSubInstructionType == 0x1d) && (packetInt[2] == 0x7f)) { playableWhistleLevel = packetInt[3]; } } else if ((packetInt[0] & 0xC0) == 0xC0) { // immediate packet addresses a 14-bit multi-function (mobile) decoder mobileDecoderAddress = ((packetInt[0] & 0x3F) << 8) + packetInt[1]; nmraInstructionType = (packetInt[2] & 0xE0) >> 5; nmraSubInstructionType = (packetInt[2] & 0x1f); if ((nmraSubInstructionType == 0x1d) && (packetInt[3] == 0x7f)) { playableWhistleLevel = packetInt[4]; } } else { // immediate packet not addressed to a multi-function (mobile) decoder log.debug("got Here 1."); } if ((mobileDecoderAddress >= 0) && (nmraInstructionType == 1) && (nmraSubInstructionType == 0x1D)) { // the "Playable" whistle message // Information reverse-engineered by B. Milhaupt and used with permission return Bundle.getMessage("LN_MSG_PLAYABLE_WHISTLE_CONTROL", Integer.toString(mobileDecoderAddress), playableWhistleLevel, (reps & 0x7)); } // F9-F28 w/a long address. if ((packetInt[0] & 0xC0) == 0xC0) { address = ((packetInt[0] & 0x3F) << 8) + packetInt[1]; if ((packetInt[2] & 0xFF) == 0xDF) { // Functions 21-28 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F21_TO_F28", Integer.toString(address), Bundle.getMessage(((packetInt[3] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[3] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[3] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[3] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[3] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[3] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[3] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[3] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); } else if ((packetInt[2] & 0xFF) == 0xDE) { // Functions 13-20 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F13_TO_F20", Integer.toString(address), Bundle.getMessage( (((packetInt[3] & 0x01) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[3] & 0x02) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[3] & 0x04) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[3] & 0x08) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[3] & 0x10) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[3] & 0x20) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[3] & 0x40) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[3] & 0x80) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); } else if ((packetInt[2] & 0xF0) == 0xA0) { // Functions 9-12 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F9_TO_F12", Integer.toString(address), Bundle.getMessage( (((packetInt[2] & 0x01) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[2] & 0x02) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[2] & 0x04) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( (((packetInt[2] & 0x08) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); } else { return Bundle.getMessage("LN_MSG_OPC_IMM_PKT_GENERIC", ((reps & 0x70) >> 4), (reps & 0x07), reps, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(dhi)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im1)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im2)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im3)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im4)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im5)), NmraPacket.format(packet)); } } else { // F9-F28 w/a short address. address = packetInt[0]; if ((packetInt[1] & 0xFF) == 0xDF) { // Functions 21-28 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F21_TO_F28", address, Bundle.getMessage(((packetInt[2] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); } else if ((packetInt[1] & 0xFF) == 0xDE) { // Functions 13-20 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F13_TO_F20", address, Bundle.getMessage(((packetInt[2] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x10) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x20) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x40) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[2] & 0x80) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); } else if ((packetInt[1] & 0xF0) == 0xA0) { // Functions 9-12 return Bundle.getMessage("LN_MSG_SEND_PACKET_IMM_SET_F9_TO_F12", address, Bundle.getMessage(((packetInt[1] & 0x01) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[1] & 0x02) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[1] & 0x04) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage(((packetInt[1] & 0x08) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); } else { // Unknown return Bundle.getMessage("LN_MSG_OPC_IMM_PKT_GENERIC", ((reps & 0x70) >> 4), (reps & 0x07), reps, Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(dhi)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im1)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im2)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im3)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im4)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(im5)), NmraPacket.format(packet)); } } // else { // F9-F28 w/a short address. } else if (l.getElement(1) == 0x1F) { if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42 && l.getElement(6) != 0x5E && l.getElement(10) == 0x70 && l.getElement(11) == 0x00 && l.getElement(15) == 0x10) { // Uhlenbrock IB-COM / Intellibox I and II read or write CV value on programming track String cv = Integer .toString(l.getElement(8) * 256 + ((l.getElement(5) & 0x02) * 64) + l.getElement(7)); int val = l.getElement(9) + 16 * (l.getElement(5) & 0x08); switch (l.getElement(6)) { case 0x6C: return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_REG_MODE_FROM_PT", cv); case 0x6D: return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_REG_MODE_FROM_PT", cv); case 0x6E: return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_PAGED_MODE_FROM_PT", cv); case 0x6F: return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_PAGED_MODE_FROM_PT", cv); case 0x71: return Bundle.getMessage("LN_MSG_UHLEN_WRITE_CV_DIRECT_BYTE_MODE_FROM_PT", cv, val); case 0x70: // observed on Intellibox II, even though it does not work on IB-COM case 0x72: return Bundle.getMessage("LN_MSG_UHLEN_READ_CV_DIRECT_BYTE_MODE_FROM_PT", cv); default: break; } return ""; } else if (l.getElement(2) == 0x01 && l.getElement(3) == 0x49 && l.getElement(4) == 0x42 && l.getElement(6) == 0x5E) { // Uhlenbrock IB-COM / Intellibox I and II write CV value on main track int addr = l.getElement(8) * 256 + ((l.getElement(5) & 0x02) * 64) + l.getElement(7); String cv = Integer .toString(l.getElement(11) * 256 + ((l.getElement(5) & 0x08) << 4) + l.getElement(9)); int val = ((l.getElement(10) & 0x02) << 6) + l.getElement(12); return Bundle.getMessage("LN_MSG_UHLEN_CV_OPS_MODE_WRITE", addr, cv, val); } } return ""; // not an understood message. } private static String interpretOpcPr3Mode(LocoNetMessage l) { /* * Sets the operating mode of the PR3 device, if present. * * Information reverse-engineered by B. Milhaupt and used with permission */ if ((l.getElement(1) == 0x10) && ((l.getElement(2) & 0x7c) == 0) && (l.getElement(3) == 0) && (l.getElement(4) == 0)) { // set PR3 mode of operation, where LS 2 bits of byte 2 are encoded as: // 0x00 Set the PR3 mode to MS100 interface mode with PR3 LocoNet termination disabled // 0x01 Set the PR3 to decoder programming track mode // 0x03 Set the PR3 to MS100 interface mode with PR3 LocoNet termination enabled switch (l.getElement(2) & 0x3) { case 0x00: { return Bundle.getMessage("LN_MSG_SET_PR3_MODE_LOCONET_IF_WITHOUT_TERM"); } case 0x02: { return Bundle.getMessage("LN_MSG_SET_PR3_MODE_PR3_PROGRAMMING_TRACK_ONLY"); } case 0x03: { return Bundle.getMessage("LN_MSG_SET_PR3_MODE_LN_MSG_SET_PR3_MODE_LOCONET_IF_WITH_TERM"); } default: { break; } } } return ""; } private static String interpretIb2Special(LocoNetMessage l) { // Intellibox function control message for mobile decoder F0-F28 (IB-I) and F13-F28 (IB-II) if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) && ((l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN) || (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F13_F19_TOKEN) || (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F21_F27_TOKEN))) { // Intellibox-I function control message for mobile decoder F5 thru F27 except F12 and F20 // Intellibox-II function control message for mobile decoder F13 thru F27 except F20 // Note: Intellibox-II documentation implies capability to control // MANY more functions. This capability may be extended by // additional tokens in element 3, including the special-case encoding // for the "eighth bit" as handled in the following case, below, // for F12, F20 & F28 int funcOffset = 5 + 8 * (l.getElement(3) - LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN); String encodingType; if (l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F5_F11_TOKEN) { encodingType = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_IB1"); } else { encodingType = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_IB2"); } String funcInfo[] = new String[7]; int mask = 1; for (int i = 0; i < 7; i++) { // handle 7 bits of data funcInfo[i] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", funcOffset + i, Bundle.getMessage(((l.getElement(4) & mask) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")); mask *= 2; } return Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL", encodingType, l.getElement(2), funcInfo[0], funcInfo[1], funcInfo[2], funcInfo[3], funcInfo[4], funcInfo[5], funcInfo[6]); } else if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) && (l.getElement(3) == LnConstants.RE_IB2_SPECIAL_F20_F28_TOKEN)) { // Special-case for F12, F20 and F28, since the tokens from the previous case // can only encode 7 bits of data in element(4). return Bundle.getMessage("LN_MSG_INTELLIBOX_SPECIAL_FUNC_CTL", l.getElement(2), Bundle.getMessage( ((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F12_MASK) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), Bundle.getMessage( ((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F20_MASK) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"), Bundle.getMessage( ((l.getElement(4) & LnConstants.RE_IB2_SPECIAL_F28_MASK) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")); } else if ((l.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) && (l.getElement(3) == LnConstants.RE_IB1_SPECIAL_F0_F4_TOKEN)) { // For Intellibox-I "one" with SW version 2.x - Special-case for F0 to F4 String funcInfo[] = new String[7]; funcInfo[0] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", 0, (l.getElement(4) & LnConstants.RE_IB1_F0_MASK) == 0 ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); int mask = 1; for (int i = 0; i < 4; i++) { // handle 7 bits of data funcInfo[i + 1] = Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_HELPER_INDIV_FUNC", i + 1, Bundle.getMessage(((l.getElement(4) & mask) != 0) ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")); mask *= 2; } return Bundle.getMessage("LN_MSG_INTELLIBOX_FUNC_CTL_F0_TO_F4", l.getElement(2), funcInfo[0], funcInfo[1], funcInfo[2], funcInfo[3], funcInfo[4]); } // Because the usage of other tokens in message element(3) are not yet // understood, let execution fall thru to the "default" case return ""; } private static String interpretIb2F9_to_F12(LocoNetMessage l) { // Intellibox-II function control message for mobile decoder F9 thru F12. int slot = l.getElement(1); int funcs = l.getElement(2); return Bundle.getMessage("LN_MSG_INTELLIBOX_SLOT_SET_F9_TO_F12", slot, Bundle.getMessage( ((funcs & LnConstants.RE_IB2_F9_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( ((funcs & LnConstants.RE_IB2_F10_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( ((funcs & LnConstants.RE_IB2_F11_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF")), Bundle.getMessage( ((funcs & LnConstants.RE_IB2_F12_MASK) != 0 ? "LN_MSG_FUNC_ON" : "LN_MSG_FUNC_OFF"))); } /** * Convert bytes from LocoNet packet into a locomotive address. * * @param a1 Byte containing the upper bits. * @param a2 Byte containing the lower bits. * @return a locomotive address in the range of 0-16383 */ static private int LOCO_ADR(int a1, int a2) { return (((a1 & 0x7f) * 128) + (a2 & 0x7f)); } /** * Convert bytes from LocoNet packet into a 1-based address for a sensor or * turnout. * * @param a1 Byte containing the upper bits * @param a2 Byte containing the lower bits * @return 1-4096 address */ static private int SENSOR_ADR(int a1, int a2) { return (((a2 & 0x0f) * 128) + (a1 & 0x7f)) + 1; } /* * Take an int and convert it to a dotted version number * as used by the LocoIO protocol. * Example: 123 => 1.2.3 */ /** * Take the LocoIO version number and convert to human friendly format, like * "1.4.8" or "9.1". * * @param val The LocoIO version. * @return String with human readable format */ public static String dotme(int val) { if ((val >= 0) && (val < 10)) { return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_ONE_DIGIT", val); } else if ((val >= 10) && (val < 100)) { return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_TWO_DIGITS", val / 10, val % 10); } else if ((val >= 100) && (val < 1000)) { int hundreds = val / 100; int tens = (val - (hundreds * 100)) / 10; int ones = val % 10; return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_DOTTED_THREE_DIGITS", hundreds, tens, ones); } return Bundle.getMessage("LN_MSG_LOCOIO_HELPER_FIRMWARE_REV_OUT_OF_RANGE", val); } /** * Convert throttle ID to a human friendly format. * * @param id1 Byte #1 of the ID * @param id2 Byte #2 of the ID * @return String with human friendly format, without the influence of * Locale */ private static String idString(int id1, int id2) { /* the decimalIdValueWithoutLocale_SpecificFormatting variable is used to generate a string representation of the ID value without any local-specific formatting. In other words, in a us_EN locale, we want "14385", not "14,385". */ String decimalIdValueWithoutLocale_SpecificFormatting = Integer .toString(((id2 & 0x7F) * 128 + (id1 & 0x7F))); String s = Bundle.getMessage("LN_MSG_THROTTLE_ID", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(id2 & 0x7F)), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(id1 & 0x7F)), decimalIdValueWithoutLocale_SpecificFormatting); return s; } /** * Create a string representation of the loco address in * addressLow & addressHigh in a form appropriate for the type of address (2 * or 4 digit) using the Digitrax 'mixed mode' if necessary. * <p> * "Mixed mode" is used by DT100 and DT200 throttles to display loco * addresses between 100 and 127 as a two-digit displayable value, where the * left digit is either 'a', 'b', or 'c', (for addresses in the 10x, 11x, * and 12x ranges, respectively), and the right digit is the "x" from the * ranges above. * * @param addressLow the least-significant 7 bits of the loco address * @param addressHigh the most-significant 7 bits of the loco address * @return a String containing the address, using Digitrax 'mixed mode' * representation of the loco address, if appropriate */ private static String convertToMixed(int addressLow, int addressHigh) { // if we have a 2 digit decoder address, proceed accordingly switch (addressHigh) { case 0x7d: log.debug("addressLow / 10 = {}", addressLow / 10); switch (addressLow) { case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 109: // N (short, alternately 'An') (or long address NN) return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Ax", addressLow, addressLow - 100, String.valueOf(LOCO_ADR(addressHigh, addressLow))); // Note: .toString intentionally used here to remove the "internationalized" // presentation of integers, which, in US English, adds a "," between // the thousands digit and the hundreds digit. This comma is undesired // in this application. case 110: case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: // N (short, alternately 'Bn') (or long address NN) return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Bx", addressLow, addressLow - 110, String.valueOf(LOCO_ADR(addressHigh, addressLow))); // Note: .toString intentionally used here to remove the "internationalized" // presentation of integers, which, in US English, adds a "," between // the thousands digit and the hundreds digit. This comma is undesired // in this application. case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: // N (short, alternately 'Cn') (or long address NN) return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_AND_LONG_ADDRESS_Cx", addressLow, addressLow - 120, String.valueOf(LOCO_ADR(addressHigh, addressLow))); // Note: .toString intentionally used here to remove the "internationalized" // presentation of integers, which, in US English, adds a "," between // the thousands digit and the hundreds digit. This comma is undesired // in this application. default: // N (short) (or long address NN) return Bundle.getMessage("LN_MSG_HELPER_IS_SHORT_AND_LONG_ADDRESS", addressLow, String.valueOf(LOCO_ADR(addressHigh, addressLow))); // Note: .toString intentionally used here to remove the "internationalized" // presentation of integers, which, in US English, adds a "," between // the thousands digit and the hundreds digit. This comma is undesired // in this application. } case 0x00: case 0x7f: switch (addressLow) { case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 109: // N (short, alternately 'An') return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Ax", addressLow, addressLow - 100); case 110: case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: // N (short, alternately 'Bn') return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Bx", addressLow, addressLow - 110); case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: // N (short, alternately 'Cn') return Bundle.getMessage("LN_MSG_HELPER_IS_ALTERNATE_SHORT_ADDRESS_Cx", addressLow, addressLow - 120); default: // N (short) return Bundle.getMessage("LN_MSG_HELPER_IS_SHORT_ADDRESS", addressLow); } default: // return the full 4 digit address return String.valueOf(LOCO_ADR(addressHigh, addressLow)); // Note: .toString intentionally used here to remove the "internationalized" // presentation of integers, which, in US English, adds a "," between // the thousands digit and the hundreds digit. This comma is undesired // in this application. } } private static String trackStatusByteToString(int trackStatusByte) { return Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STAT", (((trackStatusByte & LnConstants.GTRK_MLOK1) != 0) ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_LN1_1") : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_DT200")), (((trackStatusByte & LnConstants.GTRK_POWER) != 0) ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_ON") : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_OFF")), (((trackStatusByte & LnConstants.GTRK_IDLE) != 0) ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_RUNNING") : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_TRK_PWR_PAUSED")), (((trackStatusByte & LnConstants.GTRK_PROG_BUSY) != 0) ? Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_PRG_BUSY") : Bundle.getMessage("LN_MSG_SLOT_HELPER_TRK_STATUS_PRG_AVAILABLE"))); } /** * Return a string which is formatted by a bundle Resource Name. * * @param hour fast-clock hour * @param minute fast-clock minute * @return a formatted string containing the time */ private static String fcTimeToString(int hour, int minute) { return Bundle.getMessage("LN_MSG_SLOT_HELPER_FC_TIME", LocalTime.of(hour, minute).toString()); } protected static String[] interpretF0_F4toStrings(int dirf) { String[] s = new String[5]; s[0] = (((dirf & LnConstants.DIRF_F0) == LnConstants.DIRF_F0) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); s[1] = (((dirf & LnConstants.DIRF_F1) == LnConstants.DIRF_F1) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); s[2] = (((dirf & LnConstants.DIRF_F2) == LnConstants.DIRF_F2) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); s[3] = (((dirf & LnConstants.DIRF_F3) == LnConstants.DIRF_F3) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); s[4] = (((dirf & LnConstants.DIRF_F4) == LnConstants.DIRF_F4) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); return s; } protected static String directionOfTravelString(boolean isForward) { return Bundle.getMessage(isForward ? "LN_MSG_DIRECTION_FWD" : "LN_MSG_DIRECTION_REV"); } protected static String[] interpretF5_F8toStrings(int snd) { String[] s = new String[4]; s[0] = (((snd & LnConstants.SND_F5) == LnConstants.SND_F5) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); s[1] = (((snd & LnConstants.SND_F6) == LnConstants.SND_F6) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); s[2] = (((snd & LnConstants.SND_F7) == LnConstants.SND_F7) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); s[3] = (((snd & LnConstants.SND_F8) == LnConstants.SND_F8) ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF")); return s; } private static String figureAddressIncludingAliasing(int adr, int adr2, int ss2, int id1, int id2) { /* * Build loco address string. String will be a simple * number, unless the address is between 100 and 127 * (inclusive), where a Digitrax "mixed mode" version * of the address will be appended. */ String mixedAdrStr = convertToMixed(adr, adr2); /* * If the address is a command station "alias" condition, * then note it in the string. */ if (adr2 == 0x7f) { if ((ss2 & LnConstants.STAT2_ALIAS_MASK) == LnConstants.STAT2_ID_IS_ALIAS) { /* this is an aliased address and we have the alias */ return Bundle.getMessage("LN_MSG_LOCO_ADDR_HELPER_ALIAS_2_DIGIT_WITH_KNOWN_4_DIGIT", Integer.toString(LOCO_ADR(id2, id1)), mixedAdrStr); } else { /* this is an aliased address and we don't have the alias */ return Bundle.getMessage("LN_MSG_LOCO_ADDR_HELPER_ALIAS_2_DIGIT_WITH_UNKNOWN_4_DIGIT", mixedAdrStr); } } else { /* a regular address which is not an alias */ return mixedAdrStr; } } private static String getAlmTaskType(int taskTypeByte) { if (taskTypeByte == 2) { return Bundle.getMessage("LN_MSG_ALM_HELPER_TASK_TYPE_RD"); } else if (taskTypeByte == 3) { return Bundle.getMessage("LN_MSG_ALM_HELPER_TASK_TYPE_WR"); } else if (taskTypeByte == 0) { return Bundle.getMessage("LN_MSG_ALM_HELPER_TASK_TYPE_ID"); } else { return Bundle.getMessage("LN_MSG_ALM_HELPER_TASK_TYPE_UNKONWN", taskTypeByte); } } public static String getDeviceNameFromIPLInfo(int manuf, int type) { if (manuf != LnConstants.RE_IPL_MFR_DIGITRAX) { return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_UNDEFINED_MFG_PROD", manuf, type); } switch (type) { case LnConstants.RE_IPL_DIGITRAX_HOST_ALL: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_ALLDEVICES"); case LnConstants.RE_IPL_DIGITRAX_HOST_LNRP: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_LNRP"); case LnConstants.RE_IPL_DIGITRAX_HOST_UT4: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UT4"); case LnConstants.RE_IPL_DIGITRAX_HOST_WTL12: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_WTL12"); case LnConstants.RE_IPL_DIGITRAX_HOST_DCS210: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS210"); case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS240"); case LnConstants.RE_IPL_DIGITRAX_HOST_PR3: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PR3"); case LnConstants.RE_IPL_DIGITRAX_HOST_DT402: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT402"); case LnConstants.RE_IPL_DIGITRAX_HOST_DT500: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT500"); case LnConstants.RE_IPL_DIGITRAX_HOST_DCS51: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS51"); case LnConstants.RE_IPL_DIGITRAX_HOST_DCS52: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS52"); case LnConstants.RE_IPL_DIGITRAX_HOST_UR92: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UR92"); case LnConstants.RE_IPL_DIGITRAX_HOST_PR4: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PR4"); case LnConstants.RE_IPL_DIGITRAX_HOST_LNWI: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_LNWI"); case LnConstants.RE_IPL_DIGITRAX_HOST_BXP88: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_BXP88"); case LnConstants.RE_IPL_DIGITRAX_HOST_DB210: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB210"); case LnConstants.RE_IPL_DIGITRAX_HOST_DB210OPTO: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB210OPTO"); case LnConstants.RE_IPL_DIGITRAX_HOST_DB220: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DB220"); default: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UNKNOWN", type); } } public static String getSlaveNameFromIPLInfo(int manuf, int slaveNum) { if (manuf != LnConstants.RE_IPL_MFR_DIGITRAX) { return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_UNDEFINED_MFG_PROD", manuf, slaveNum); } switch (slaveNum) { case LnConstants.RE_IPL_DIGITRAX_SLAVE_ALL: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_ALLDEVICES"); case LnConstants.RE_IPL_DIGITRAX_SLAVE_RF24: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_RF24"); default: return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_SLAVE_UNKNOWN", slaveNum); } } /** * Interpret messages with Opcode of OPC_ALM_READ, OPC_ALM_WRITE * <p> * @param l LocoNet Message to interpret * @return String containing interpreted message or empty string if * message is not interpretable. */ public static String interpretAlm(LocoNetMessage l) { if ((l.getOpCode() != LnConstants.OPC_ALM_READ) && (l.getOpCode() != LnConstants.OPC_ALM_WRITE)) { return ""; } if (l.getElement(1) == 0x10) { switch (l.getElement(2)) { case 0: if ((l.getElement(3) == 0) && (l.getElement(6) == 0)) { return Bundle.getMessage("LN_MSG_QUERY_ALIAS_INFO"); } else if ((l.getElement(3) == 0) && (l.getElement(6) == 0x0b)) { return Bundle.getMessage("LN_MSG_ALIAS_INFO_REPORT", l.getElement(4) * 2); } else if ((l.getElement(6) == 0xf) && (l.getElement(14) == 0)) { // Alias read and write messages String message; if (l.getElement(3) == 0x2) { if (l.getOpCode() == LnConstants.OPC_ALM_WRITE) { return Bundle.getMessage("LN_MSG_QUERY_ALIAS", l.getElement(4)); } else { message = "LN_MSG_REPORT_ALIAS_2_ALIASES"; } } else { break; } String longAddr = convertToMixed(l.getElement(7), l.getElement(8)); int shortAddr = l.getElement(9); String longAddr2 = convertToMixed(l.getElement(11), l.getElement(12)); int shortAddr2 = l.getElement(13); int pair = l.getElement(4); return Bundle.getMessage(message, pair, longAddr, shortAddr, longAddr2, shortAddr2); } else if ((l.getElement(3) == 0x43)) { String longAddr = convertToMixed(l.getElement(7), l.getElement(8)); int shortAddr = l.getElement(9); String longAddr2 = convertToMixed(l.getElement(11), l.getElement(12)); int shortAddr2 = l.getElement(13); int pair = l.getElement(4); return Bundle.getMessage("LN_MSG_SET_ALIAS_2_ALIASES", pair, longAddr, shortAddr, longAddr2, shortAddr2); } else if ((l.getElement(6) == 0) && (l.getElement(14) == 0)) { return Bundle.getMessage("LN_MSG_QUERY_ALIAS", l.getElement(4)); } break; case 1: if ((l.getElement(2) == 1) && ((l.getElement(3) & 0x7E) == 0x2) // ((l.getElement(6) & 0x7E) == 0x2) && // sometimes 0x00, sometimes 0x0F, not sure why ) { // appears to be related to command-station routes int turnoutGroup; int altTurnoutGroup; int routeNum; int altRouteNum; routeNum = 1 + (((l.getElement(4) + l.getElement(5) * 128) / 2) & 0x1f); turnoutGroup = 1 + ((l.getElement(4) & 0x1) << 2); altRouteNum = 1 + (((l.getElement(4) + l.getElement(5) * 128) / 4) & 0x3F); altTurnoutGroup = 1 + ((l.getElement(4) & 0x3) << 2); if ((l.getOpCode() == LnConstants.OPC_ALM_WRITE) && ((l.getElement(3) & 0x1) == 0)) { return Bundle.getMessage("LN_MSG_CMD_STN_ROUTE_QUERY", routeNum, turnoutGroup, turnoutGroup + 3, altRouteNum, altTurnoutGroup, altTurnoutGroup + 3); } String turnA, turnB, turnC, turnD; String statA, statB, statC, statD; if ((l.getElement(7) == 0x7f) && (l.getElement(8) == 0x7f)) { turnA = "Unused"; statA = ""; } else { turnA = Integer.toString(1 + l.getElement(7) + ((l.getElement(8) & 0x0f) << 7)); statA = (l.getElement(8) & 0x20) == 0x20 ? "c" : "t"; } if ((l.getElement(9) == 0x7f) && (l.getElement(10) == 0x7f)) { turnB = "Unused"; statB = ""; } else { turnB = Integer.toString(1 + l.getElement(9) + ((l.getElement(10) & 0x0f) << 7)); statB = (l.getElement(10) & 0x20) == 0x20 ? "c" : "t"; } if ((l.getElement(11) == 0x7f) && (l.getElement(12) == 0x7f)) { turnC = "Unused"; statC = ""; } else { turnC = Integer.toString(1 + l.getElement(11) + ((l.getElement(12) & 0x0f) << 7)); statC = (l.getElement(12) & 0x20) == 0x20 ? "c" : "t"; } if ((l.getElement(13) == 0x7f) && (l.getElement(14) == 0x7f)) { turnD = "Unused"; statD = ""; } else { turnD = Integer.toString(1 + l.getElement(13) + ((l.getElement(14) & 0x0f) << 7)); statD = (l.getElement(14) & 0x20) == 0x20 ? "c" : "t"; } return Bundle.getMessage( (l.getOpCode() == LnConstants.OPC_ALM_WRITE) ? "LN_MSG_CMD_STN_ROUTE_WRITE" : "LN_MSG_CMD_STN_ROUTE_REPORT", routeNum, turnoutGroup, turnoutGroup + 3, altRouteNum, altTurnoutGroup, altTurnoutGroup + 3, turnA, statA, turnB, statB, turnC, statC, turnD, statD); } return "routes unknown\n"; default: return Bundle.getMessage( ((l.getElement(0) == LnConstants.OPC_ALM_WRITE) ? "LN_MSG_ALM_WRITE" : "LN_MSG_ALM_WRITE_REPLY"), l.getElement(2), l.getElement(3), getAlmTaskType(l.getElement(3)), l.getElement(4), l.getElement(5), l.getElement(6), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(7))), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(8))), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(9))), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(10))), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(11))), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(12))), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(13))), Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(l.getElement(14)))); } } else if (l.getElement(1) == 0x15) { int slot = ((l.getElement(2) & 0x07) * 128) + l.getElement(3); // slot number for this request String result = interpretExtendedSlotRdWr(l, slot); if (result.length() > 0) { return result; } } return ""; } private static String interpretOpcExpMoveSlots(LocoNetMessage l) { int src = ((l.getElement(1) & 0x03) * 128) + (l.getElement(2) & 0x7f); int dest = ((l.getElement(3) & 0x03) * 128) + (l.getElement(4) & 0x7f); if ((src >= 0x79) && (src <= 0x7f)) { return ""; } if ((dest >= 0x79) && (dest <= 0x7f)) { return ""; } boolean isSettingStatus = ((l.getElement(3) & 0b01110000) == 0b01100000); if (isSettingStatus) { int stat = l.getElement(4); return Bundle.getMessage("LN_MSG_OPC_EXP_SET_STATUS", src, LnConstants.CONSIST_STAT(stat), LnConstants.LOCO_STAT(stat), LnConstants.DEC_MODE(stat)); } boolean isUnconsisting = ((l.getElement(3) & 0b01110000) == 0b01010000); if (isUnconsisting) { // source and dest same, returns slot contents return Bundle.getMessage("LN_MSG_OPC_EXP_UNCONSISTING", src); } boolean isConsisting = ((l.getElement(3) & 0b01110000) == 0b01000000); if (isConsisting) { //add dest to src, returns dest slot contents return Bundle.getMessage("LN_MSG_OPC_EXP_CONSISTING", src, dest); } /* check special cases */ if (src == 0) { /* DISPATCH GET */ // maybe return Bundle.getMessage("LN_MSG_MOVE_SL_GET_DISP"); } else if (src == dest) { /* IN USE */ // correct return Bundle.getMessage("LN_MSG_MOVE_SL_NULL_MOVE", src); } else if (dest == 0) { /* DISPATCH PUT */ return Bundle.getMessage("LN_MSG_MOVE_SL_DISPATCH_PUT", src); } else { /* general move */ return Bundle.getMessage("LN_MSG_MOVE_SL_MOVE", src, dest); } } private static String interpretPocExpLocoSpdDirFunction(LocoNetMessage l) { int slot = ((l.getElement(1) & 0x03) * 128) + (l.getElement(2) & 0x7f); if ((l.getElement(1) & LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_SPEED) == 0) { // speed and direction int spd = l.getElement(4); String direction = Bundle.getMessage( (l.getElement(1) & 0b00001000) != 0 ? "LN_MSG_DIRECTION_REV" : "LN_MSG_DIRECTION_FWD"); String throttleID = Integer.toHexString(l.getElement(3)); return Bundle.getMessage("LN_MSG_OPC_EXP_SPEED_DIRECTION", slot, spd, direction, throttleID); } // Build a string for the functions on off String[] fn = new String[8]; for (int bitIndex = 0; bitIndex < 8; bitIndex++) { fn[bitIndex] = (l.getElement(4) >> (7 - bitIndex) & 1) == 1 ? Bundle.getMessage("LN_MSG_FUNC_ON") : Bundle.getMessage("LN_MSG_FUNC_OFF"); } if ((l.getElement(1) & LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F0F6_MASK) { return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F0_F6", slot, fn[3], fn[7], fn[6], fn[5], fn[4], fn[2], fn[1]); } else if ((l.getElement(1) & LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F7F13_MASK) { return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F7_F13", slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], fn[1]); } else if ((l.getElement(1) & LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F14F20_MASK) { return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F14_F20", slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], fn[1]); } else if ((l.getElement(1) & LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F21F28_F28OFF_MASK) { return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F21_F28", slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], fn[1], Bundle.getMessage("LN_MSG_FUNC_OFF")); } else if ((l.getElement(1) & LnConstants.OPC_EXP_SEND_SUB_CODE_MASK_FUNCTION) == LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F21F28_F28ON_MASK) { return Bundle.getMessage("LN_MSG_OPC_EXP_FUNCTIONS_F21_F28", slot, fn[7], fn[6], fn[5], fn[4], fn[3], fn[2], fn[1], Bundle.getMessage("LN_MSG_FUNC_ON")); } return ""; } private static String interpretExtendedSlotRdWr(LocoNetMessage l, int slot) { /** * ************************************************ * extended slot read/write message * * ************************************************ */ /* * If its a "Special" slot (Stats etc) use a different routine */ if (slot > 247 && slot < 252) { return interpretExtendedSlot_StatusData(l, slot); } int trackStatus = l.getElement(7); // track status int id1 = l.getElement(19); int id2 = l.getElement(18); int command = l.getOpCode(); int stat = l.getElement(4); // slot status //int adr = l.getElement(5) + 128 * l.getElement(6); // loco address int adr = l.getElement(5); int spd = l.getElement(8); // command speed int dirf = l.getElement(10) & 0b00111111; // direction and F0-F4 bits String[] dirf0_4 = interpretF0_F4toStrings(dirf); int ss2 = l.getElement(18); // slot status 2 (tells how to use // ID1/ID2 & ADV Consist) int adr2 = l.getElement(6); // loco address high int snd = l.getElement(10); // Sound 1-4 / F5-F8 String[] sndf5_8 = interpretF5_F8toStrings(snd); String locoAdrStr = figureAddressIncludingAliasing(adr, adr2, ss2, id1, id2); return Bundle.getMessage(((command == 0xEE) ? "LN_MSG_SLOT_LOCO_INFO_WRITE" : "LN_MSG_SLOT_LOCO_INFO_READ"), slot, locoAdrStr, LnConstants.CONSIST_STAT(stat), LnConstants.LOCO_STAT(stat), LnConstants.DEC_MODE(stat), directionOfTravelString((dirf & LnConstants.DIRF_DIR) == 0), spd, // needs re-interpretation for some cases of slot consisting state dirf0_4[0], dirf0_4[1], dirf0_4[2], dirf0_4[3], dirf0_4[4], sndf5_8[0], sndf5_8[1], sndf5_8[2], sndf5_8[3], trackStatusByteToString(trackStatus), Bundle.getMessage("LN_MSG_SLOT_HELPER_SS2_SIMPLE", Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION", StringUtil.twoHexFromInt(ss2))), Bundle.getMessage("LN_MSG_SLOT_HELPER_ID1_ID2_AS_THROTTLE_ID", idString(id1, id2))); } private static String interpretExtendedSlot_StatusData(LocoNetMessage l, int slot) { String baseInfo = ""; String detailInfo = ""; switch (slot) { case 248: // Identifying information baseInfo = interpretExtendedSlot_StatusData_Base_Detail(l, slot); // Flags detailInfo = interpretExtendedSlot_StatusData_Flags(l, slot); break; case 249: // electric // Identifying information baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); detailInfo = interpretExtendedSlot_StatusData_Electric(l, slot); break; case 251: // Loconet stats // Identifying information baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); detailInfo = interpretExtendedSlot_StatusData_LocoNet(l, slot); break; case 250: // Identifying information baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Slots info detailInfo = interpretExtendedSlot_StatusData_Slots(l, slot); break; default: baseInfo = "Still working on it"; } return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS", slot, baseInfo, detailInfo); } /** * Interpret the base information in bytes 16,18,19 * for slots 249,250,251. not 248 * @param l loconetmessage * @param slot slot number * @return a format message. */ private static String interpretExtendedSlot_StatusData_Base(LocoNetMessage l, int slot) { String hwType = ""; int hwSerial; switch (l.getElement(16)) { case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240: hwType = "DCS240"; break; case LnConstants.RE_IPL_DIGITRAX_HOST_DCS210: hwType = "DCS210"; break; case LnConstants.RE_IPL_DIGITRAX_HOST_DCS52: hwType = "DCS52"; break; case LnConstants.RE_IPL_DIGITRAX_HOST_BXP88: hwType = "BXP88"; break; case LnConstants.RE_IPL_DIGITRAX_HOST_BXPA1: hwType = "BXPA1"; break; default: hwType = "Unknown"; } hwSerial = ((l.getElement(19) & 0x0f) * 128) + l.getElement(18); return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_BASE", hwType, hwSerial); } /** * Interp slot 248 base details * @param l loconetmessage * @param slot slot number * @return formated message */ private static String interpretExtendedSlot_StatusData_Base_Detail(LocoNetMessage l, int slot) { double hwVersion; double swVersion; int hwSerial; String hwType; switch (l.getElement(14)) { case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240: hwType = "DCS240"; break; case LnConstants.RE_IPL_DIGITRAX_HOST_DCS210: hwType = "DCS210"; break; case LnConstants.RE_IPL_DIGITRAX_HOST_BXP88: hwType = "BXP88"; break; case LnConstants.RE_IPL_DIGITRAX_HOST_BXPA1: hwType = "BXPA1"; break; default: hwType = "Unknown"; } hwSerial = ((l.getElement(19) & 0x0f) * 128) + l.getElement(18); hwVersion = ((double) (l.getElement(17) & 0x78) / 8) + ((double) (l.getElement(17) & 0x07) / 10); swVersion = ((double) (l.getElement(16) & 0x78) / 8) + ((double) (l.getElement(16) & 0x07) / 10); return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_BASEDETAIL", hwType, hwSerial, hwVersion, swVersion); } /** * Interp slot 249 electric stuff, bytes 4,5,6,7,10,12 * @param l loconetmessage * @param slot slot number * @return formated message */ private static String interpretExtendedSlot_StatusData_Electric(LocoNetMessage l, int slot) { double voltsTrack; double voltsIn; double ampsIn; double ampsLimit; double voltsRsLoaded; double voltsRsUnLoaded; voltsTrack = ((double) l.getElement(4)) * 2 / 10; voltsIn = ((double) l.getElement(5)) * 2 / 10; ampsIn = ((double) l.getElement(6)) / 10; ampsLimit = ((double) l.getElement(7)) / 10; voltsRsLoaded = ((double) l.getElement(12)) * 2 / 10; voltsRsUnLoaded = ((double) l.getElement(10)) * 2 / 10; return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_ELECTRIC", voltsTrack, voltsIn, ampsIn, ampsLimit, voltsRsLoaded, voltsRsUnLoaded); } /** * Interp slot 249 loconet stats, bytes 4 & 5,6 & 7 * @param l loconetmessage * @param slot slot number * @return formated message */ private static String interpretExtendedSlot_StatusData_LocoNet(LocoNetMessage l, int slot) { double msgTotal; double msgErrors; msgTotal = (l.getElement(4) + (l.getElement(5) * 128)); msgErrors = (l.getElement(6) + (l.getElement(7) * 128)); return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_LOCONET", msgTotal, msgErrors); } private static String interpretExtendedSlot_StatusData_Flags(LocoNetMessage l, int slot) { //TODO need more sample data return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_FLAGS"); } /** * Interp slot 250 slots used/free etc * @param l loconetmessage * @param slot slot number * @return formated message */ private static String interpretExtendedSlot_StatusData_Slots(LocoNetMessage l, int slot) { //TOD there is still more data in this slot. double msgInUse; double msgIdle; double msgFree; msgInUse = (l.getElement(4) + (l.getElement(5) * 128)); msgIdle = (l.getElement(6) + (l.getElement(7) * 128)); msgFree = (l.getElement(8) + (l.getElement(9) * 128)); return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_SLOTS", msgInUse, msgIdle, msgFree); } private static final String ds54sensors[] = { "AuxA", "SwiA", "AuxB", "SwiB", "AuxC", "SwiC", "AuxD", "SwiD" }; // NOI18N private static final String ds64sensors[] = { "A1", "S1", "A2", "S2", "A3", "S3", "A4", "S4" }; // NOI18N private static final String se8csensors[] = { "DS01", "DS02", "DS03", "DS04", "DS05", "DS06", "DS07", "DS08" }; // NOI18N private final static Logger log = LoggerFactory.getLogger(LocoNetMessageInterpret.class); }