java/src/jmri/jmrix/loconet/messageinterp/LocoNetMessageInterpret.java
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.jmrix.loconet.uhlenbrock.LncvMessageContents;
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.
*
* Reverse-engineering of device-specific LNSV messages was provided by K. Drenth,
* used with permission.
*
* @author Bob Jacobsen Copyright 2001, 2002, 2003
* @author B. Milhaupt Copyright 2015, 2016, 2018, 2022
* @author Randall Wood Copyright 2016
* @author Michael Richardson Copyright (C) 2021
*/
public class LocoNetMessageInterpret {
private static final String Ln_Off = Bundle.getMessage("LN_MSG_OFF");
private static final String Ln_On = Bundle.getMessage("LN_MSG_ON");
/**
* 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");
}
case LnConstants.OPC_RE_LOCORESET_BUTTON: {
return Bundle.getMessage("LN_MSG_RE_LOCO_RESET");
}
/*
* 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_MULTI_SENSE_LONG 0xE0 messages about transponding.
*
* This op code is not documented by Digitrax. The use of this message was observed when using a
* Digikeijs 5088RC. With a capable decoder, this message contains additional Railcom information
* (direction, speed, QoS) compared to the standard OPC_MULTI_SENSE message.
*
* Reverse engineering performed by Michael Richardson.
* ********************************************************************************************
*/
case LnConstants.OPC_MULTI_SENSE_LONG: {
result = interpretOpcMultiSenseLong(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: {
// case OPC_EXP_RD_SL_DATA: // NOTE: Duplicate of definition of OPC_ALM_WRITE!
// case OPC_EXP_WR_SL_DATA: // NOTE: Duplicate of definition of 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;
}
// 0xE4
case LnConstants.OPC_LISSY_UPDATE: {
result = interpretOpcLissyUpdate(l);
if (result.length() > 0) {
return result;
}
break;
}
// 0xED
case LnConstants.OPC_IMM_PACKET: {
result = interpretOpcImmPacket(l);
if (result.length() > 0) {
return result;
}
break;
}
// 0xD3
case LnConstants.RE_OPC_PR3_MODE: {
result = interpretOpcPr3Mode(l);
if (result.length() > 0) {
return result;
}
break;
}
// 0xA3
case LnConstants.RE_OPC_IB2_F9_F12: {
result = interpretIb2F9_to_F12(l);
if (result.length() > 0) {
return result;
}
break;
}
// TODO: put this back for intellibox cmd station.
// it conflicts with loconet speed/dir etc.
// 0xD4
case LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL: {
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_MESSAGE_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 interpretOpcPeerXfer15(LocoNetMessage l) {
/*
* see interpretOpcImm15
* and jmri.jmrix.loconet.uhlenbrock.LncvMessageContents.java
*/
// check for a specific type - Uhlenbrock LNSV Programming messages format
String result = interpretLncvMessage(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 interpretLncvMessage(LocoNetMessage l) {
String lncvReply = "";
LncvMessageContents cvmc = null;
try {
// assume the message is an LNCV message
cvmc = new LncvMessageContents(l);
} catch (IllegalArgumentException e) {
// message is not an LNCV message. Ignore the exception.
}
if (cvmc != null) {
// the message was indeed an LNCV message
try {
// get string representation of the message from an
// available translation which is best suited to
// the currently-active "locale"
lncvReply = cvmc.toString();
} catch (IllegalArgumentException e) {
// message is not a properly-formatted LNCV message. Ignore the exception.
}
}
return lncvReply;
}
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 0x0F: {
result = interpretOpcPeerXfer15(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_MESSAGE_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_MESSAGE_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_MESSAGE_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 (ack1 == 0x01) { // (l.getElement(1) == 0x6D) is same as case, same code for LNCV "unsupported CV"
return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_UHL_PROG");
} else if (ack1 == 0x02) { // LNCV Uhlenbrock programming reply
return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LNCV_READONLY");
} else if (ack1 == 0x03) { // LNCV Uhlenbrock programming reply
return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_LNCV_ILLEGALVAL");
} else {
return Bundle.getMessage("LN_MSG_LONG_ACK_OPC_IMM_UNKNOWN",
Bundle.getMessage("LN_MSG_HEXADECIMAL_REPRESENTATION",
StringUtil.twoHexFromInt(ack1)))+
Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString());
}
case LnConstants.OPC_IMM_PACKET_2:
// response for OPC_IMM_PACKET_2
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;
case LnConstants.OPC_MULTI_SENSE_RAILCOM_AD:
result = interpretOpcMultiSenseRailcomAD(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 convertRailComAD(int indexValue, int dynamicValue) {
/**
***************************************************
* RailCom App DYN (ID 7) message
* indexValue = 6 bit value value per standard
* dynamicValue = 8 bit value per standard
**/
String indexString = "";
switch (indexValue) {
case 0: //Speed
indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_0");
break;
case 7: //QoS
indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_7");
break;
default:
indexString = Bundle.getMessage("LN_MSG_RAILCOM_HELPER_INDEX_VALUE_UNKNOWN");
break;
}
return Bundle.getMessage("LN_MSG_RAILCOM_REPORT",
indexValue, indexString, dynamicValue);
}
private static String interpretOpcMultiSenseRailcomAD(LocoNetMessage l, String reporterPrefix) {
/**
***************************************************
* Multi Sense Standard RailCom App DYN message (Loconet OpCode 0xD0)
* The message bytes as assigned as follows:
*
* <0xD0> <RC_I> <RCDV_L> <AD_H> <AD_L> <CHK>
*
* <RC_I> is encoded as shown below
* bit 7 always 0
* bits 6-5 always 10 (0x40)
* bits 4-1 RailCom App:Dyn Index Value (4 bit value, but expected 6 bits per standard)
* bit 0 RailCom Dynamic Value high bit
*
* <RCDV_L> RCDV_L{6:0} represent the upper 7 bits * of the 8 bit RailCom Dynamic Value. The 8th bit is bit 0 of <RC_I>
*
* <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.
*
* Information reverse-engineered by Michael Ricahrdson
**/
String locoAddr = convertToMixed(l.getElement(4), l.getElement(3));
int indexValue = (l.getElement(1) & 0x1E)/2; //bits 4-1
int dynamicValue = l.getElement(2) + (l.getElement(1) & 0x01) * 128;
String railcomAdString = convertRailComAD(indexValue, dynamicValue);
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_RAILCOM_REPORT",
locoAddr, railcomAdString);
}
private static String interpretOpcMultiSenseLong(LocoNetMessage l, String reporterPrefix) {
/***************************************************
* Multi Sense Long RailCom App DYN message (Loconet OpCode 0xE0)
* The message bytes as assigned as follows:
*
* <0xE0> <0x09> <MSL_I> <BLK_L> <AD_H> <AD_L> <RCDV_H> <RCDV_L> <CHK>
*
* <0xEO> OpCode
* <Ox09> Message Length
* <MSL_I> is encoded as shown below
* bit 7 always 0
* bits 6-5 (00 = Absent, 01 = Present, 10 = Present with AppDyn Message, 11 = unknown)
* bit 4 ? - unverified - currently part of detection block number logic following Multi Sense Standard
* bits 0-3 block high
*
* <BLK_L> 11 bit number representing the detection block. Lower 7 bits plus 4 high bits from <MSL_I> {3:0}
*
* <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.
*
* <RCDV_H> is encoded as shown below:
* bit 7 always 0
* bit 6 - Loco direction: 0 = East, 1 = West
* bits 5-1 RailCom App:Dyn Index Value (5 bit value, but expected 6 bits per standard)
* bit 0 RailCom Dynamic Value high bit
*
* <RCDV_L> {6:0} represent the lower 7 bits of the 8 bit RailCom Dynamic Value. The 8th bit is bit 0 of <RCDV_H>
*
* <CHK>
*
* Information reverse-engineered by Michael Ricahrdson
*/
if (l.getElement(1) == 0x09){ // Only process 0xE0 0x09 messages
// Transponding Event
// get system and user names
int type = l.getElement(2) & LnConstants.OPC_MULTI_SENSE_MSG; //bits 5-4
//0x00 = absent = 00
//0x20 = present = 10
//0x40 = present with App Dyn = 01
//0x60 = unknown = 11
if (type == 0x60) { //unknown at this point
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE");
}
// Now Process 0x00, 0x20, and 0x40
String reporterSystemName = reporterPrefix + ((l.getElement(2) & 0x1F) * 128 + l.getElement(3) + 1);
Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getReporter(reporterSystemName);
String reporterUserName = "";
if (reporter != null) {
String uname = reporter.getUserName();
if ((uname != null) && (!uname.isEmpty())) {
reporterUserName = uname;
}
}
String locoAddr = convertToMixed(l.getElement(5), l.getElement(4));
String transpActivity = "";
String direction = ((l.getElement(6) & 0x40) == 0)
? Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_LOCO_DIRECTION_HELPER_EAST")
: Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_LOCO_DIRECTION_HELPER_WEST");
switch (type) {
case LnConstants.OPC_MULTI_SENSE_RAILCOM_AD:
int indexValue = (l.getElement(6) & 0x3E)/2; //bits 5-1
int dynamicValue = l.getElement(7) + (l.getElement(6) & 0x01) * 128;
transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT");
String railcomAdString = convertRailComAD(indexValue, dynamicValue);
String multiSenseLongString = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT",
locoAddr, direction, transpActivity, reporterSystemName, reporterUserName);
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_RAILCOM_REPORT",
multiSenseLongString, railcomAdString);
case LnConstants.OPC_MULTI_SENSE_PRESENT:
if ((l.getElement(6) & 0x3F) != 0 || l.getElement(7) != 0 ) {
// within current understanding values here are not expected
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE");
}
transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_PRESENT");
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT",
locoAddr, direction, transpActivity, reporterSystemName, reporterUserName);
case LnConstants.OPC_MULTI_SENSE_ABSENT:
if ((l.getElement(6) & 0x3F) != 0 || l.getElement(7) != 0 ) {
// within current understanding values here are not expected
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE");
}
transpActivity = Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_TRANSP_HELPER_IS_ABSENT");
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_TRANSP_REPORT",
locoAddr, direction, transpActivity, reporterSystemName, reporterUserName);
default:
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE");
}
} else {
return Bundle.getMessage("LN_MSG_OPC_MULTI_SENSE_LONG_UNKNOWN_MESSAGE");
}
}
private static String interpretOpcWrSlDataOpcSlRdData(LocoNetMessage l) {
int slot = l.getElement(2); // slot number for this request
String mode;
int command = l.getOpCode();
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 dealt with separately, 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 Acknowledgement 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_MESSAGE_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.
*/
log.debug("Message from LISSY: {}", Bundle.getMessage("LN_MONITOR_MESSAGE_RAW_HEX_INFO", l.toString()));
switch (l.getElement(1)) {
case 0x08: // Format LISSY message
int unit = (l.getElement(4) & 0x7F);
if ((l.getElement(3) & 0x40) != 0) { // Loco movement
int category = l.getElement(2) + 1;
int address = (l.getElement(6) & 0x7F) + 128 * (l.getElement(5) & 0x7F);
return Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_LOCO_MOVEMENT",
unit,
Integer.toString(address),
Integer.toString(category),
((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")));
} else { // other messages
switch (l.getElement(2)) {
case 0x00: // Loco speed
int speed = (l.getElement(6) & 0x7F) + 128 * (l.getElement(5) & 0x7F);
return Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_LOCO_SPEED",
unit,
Integer.toString(speed));
case 0x01: // Block status
return Bundle.getMessage("LN_MSG_LISSY_BLOCK_REPORT",
unit,
((l.getElement(6) & 0x01) == 0
? Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_BLOCK_FREE")
: Bundle.getMessage("LN_MSG_LISSY_IR_REPORT_HELPER_BLOCK_OCCUPIED")));
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_MESSAGE_RAW_HEX_INFO", l.toString());
}
break;
default:
break;
}
return "";
}
private static String interpretOpcImmPacket(LocoNetMessage l) {
String result;
result = "";
/*
* OPC_IMM_PACKET 0xED
*/
if (l.getElement(1) == 0x0F) { // length = 15
// check for a specific type - Uhlenbrock LNSV Programming messages format
result = interpretLncvMessage(l);
if (result.length() > 0) {
return result;
}
return "";
}
/* Else:
* 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));
// len >= 2 always true at this point
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
if ((packetInt[0] & 0xC0) == 0x80 ) {
/*
* 2.4.7 Extended Decoder Control Packet address for
* operations mode programming and Aspect Setting.
* 10AAAAAA 0 0AAA0AA1
* Packets 3 bytes in length are Accessory Aspect packets (2.4.3)
* {preamble} 10AAAAAA 0 0AAA0AA1 0 XXXXXXXX 0 EEEEEEEE 1
* Please note that the use of 0 in bit 3 of byte 2 is to
* ensure that this packet cannot be confused with the
* legacy accessory-programming packets. The resulting packet
* would be:
* {preamble} 10AAAAAA 0 0AAA0AA1 0 (1110CCVV 0 VVVVVVVV 0 DDDDDDDD) 0 EEEEEEEE 1
* A5 43 00
* 10100101 0010011 00000000
* Signal Decoder Address (Configuration Variable Access Instruction) Error Byte
*/
log.debug("Is an Extended Accessory Ops-mode CV access or Set Signal Aspect");
log.debug(" LocoNet message: {} {} {} {} {} {} {} {} {} {} {}",
StringUtil.twoHexFromInt(l.getElement(0)),
StringUtil.twoHexFromInt(l.getElement(1)),
StringUtil.twoHexFromInt(l.getElement(2)),
StringUtil.twoHexFromInt(l.getElement(3)),
StringUtil.twoHexFromInt(l.getElement(4)),
StringUtil.twoHexFromInt(l.getElement(5)),
StringUtil.twoHexFromInt(l.getElement(6)),
StringUtil.twoHexFromInt(l.getElement(7)),
StringUtil.twoHexFromInt(l.getElement(8)),
StringUtil.twoHexFromInt(l.getElement(9)),
StringUtil.twoHexFromInt(l.getElement(10))
);
if (packetInt.length == 5) {
log.debug(" NMRA packet: {} {} {} {} {}",
StringUtil.twoHexFromInt(packetInt[0]),
StringUtil.twoHexFromInt(packetInt[1]),
StringUtil.twoHexFromInt(packetInt[2]),
StringUtil.twoHexFromInt(packetInt[3]),
StringUtil.twoHexFromInt(packetInt[4])
);
} else if (packetInt.length == 3) {
log.debug(" NMRA packet: {} {} {}",
StringUtil.twoHexFromInt(packetInt[0]),
StringUtil.twoHexFromInt(packetInt[1]),
StringUtil.twoHexFromInt(packetInt[2])
);
} else {
log.warn(" Unknown Extended Accessory Packet length [{}])",packetInt.length);
return "";
}
if ((packetInt[2] & 0xF0) == 0xF0) {
/*
* 2.3.7.2 Configuration Variable Access Instruction - Short Form
* This instruction has the format of:
* {instruction bytes} = 1111GGGG 0 DDDDDDDD 0 DDDDDDDD
* The 8 bit data DDDDDDDD is placed in the configuration
* variable identified by GGGG according
*/
log.debug("Is an Short-form Extended Accessory Ops-mode CV access");
}
if ((packetInt[2] & 0xf0) == 0xe0) {
/*
* 2.3.7.3 Configuration Variable Access Instruction - Long Form
* The long form allows the direct manipulation of all CVs8. This
* instruction is valid both when the Digital Decoder has its
* long address active and short address active. Digital Decoders
* shall not act on this instruction if sent to its consist
* address.
*
* The format of the instructions using Direct CV
* addressing is:
* {instruction bytes}= 1110GGVV 0 VVVVVVVV 0 DDDDDDDD
*
* The actual Configuration Variable desired is selected
* via the 10-bit address with the 2-bit address (VV) in
* the first data byte being the most significant bits of
* the address. The Configuration variable being addressed
* is the provided 10-bit address plus 1. For example, to
* address CV1 the 10 bit address is "00 00000000".
*
* The defined values for Instruction type (CC) are:
* GG=00 Reserved for future use
* GG=01 Verify byte 505
* GG=11 Write byte
* GG=10 Bit manipulation
* */
int addr = getExtendedAccessoryAddressFromDCCPacket(packetInt);
log.debug("Long-format Extended Accessory Ops-mode CV access: Extended Acceccory Address {}", addr);
int cvnum = 1 + ((packetInt[2] & 0x03) << 2) + (packetInt[3] & 0xff);
switch (packetInt[2] & 0x0C) {
case 0x04:
// GG=01 Verify byte
/*
* Type = "01" VERIFY BYTE
*
* The contents of the Configuration Variable as indicated
* by the 10-bit address are compared with the data byte
* (DDDDDDDD). If the decoder successfully receives this
* packet and the values are identical, the Digital
* Decoder shall respond with the contents of the CV as
* the Decoder Response Transmission, if enabled.
*/
log.debug("CV # {}, Verify Byte: {}", cvnum, packetInt[4]);
return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_VERIFY",
addr, cvnum, packetInt[4] );
case 0x08:
// GG=10 Bit manipulation
/*
* Type = "10" BIT MANIPULATION.
*
* The bit manipulation instructions use a special
* format for the data byte (DDDDDDDD): 111FDBBB, where
* BBB represents the bit position within the CV,
* D contains the value of the bit to be verified
* or written, and F describes whether the
* operation is a verify bit or a write bit
* operation.
*
* F = "1" : WRITE BIT
* F = "0" : VERIFY BIT
* The VERIFY BIT and WRITE BIT instructions operate
* in a manner similar to the VERIFY BYTE and WRITE
* BYTE instructions (but operates on a single bit).
* Using the same criteria as the VERIFY BYTE
* instruction, an operations mode acknowledgment
* will be generated in response to a VERIFY BIT
* instruction if appropriate. Using the same
* criteria as the WRITE BYTE instruction, a
* configuration variable access acknowledgment
* will be generated in response to the second
* identical WRITE BIT instruction if appropriate.
*/
if ((packetInt[4]& 0xE0) != 0xE0) {
break;
}
log.debug("CV # {}, Bit Manipulation: {} {} (of bits 0-7) with {}",
cvnum, (packetInt[4] & 0x10) == 0x10 ? "Write" : "Verify",
(packetInt[4] & 0x7),
(packetInt[4] >> 3) & 0x1);
// "Extended Accessory Decoder CV Bit {} bit,
// Address {}, CV {}, bit # {} (of bits 0-7)
// with value {}.\n"
return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_BIT_ACCESS",
((packetInt[4] & 0x10) == 0x10 ? "Write" : "Verify"),
addr, cvnum, (packetInt[4] & 0x7),
((packetInt[4] >>3) & 0x1) );
case 0x0c:
// GG=11 Write byte
/*
* Type = "11" WRITE BYTE
*
* The contents of the Configuration Variable as indicated by the 10-bit
* address are replaced by the data byte (DDDDDDDD). Two identical
* packets are needed before the decoder shall modify a
* configuration variable. These two packets need not be back
* to back on the track. However any other packet to the same
* decoder will invalidate the write operation. (This includes
* broadcast packets.) If the decoder successfully receives
* this second identical packet, it shall respond with a
* configuration variable access acknowledgment.
*/
log.debug("CV # {}, Write Byte: {}", cvnum, packetInt[4]);
return Bundle.getMessage("LN_MSG_EXTEND_ACCY_CV_WRITE",
addr, cvnum, packetInt[4] );
case 0x0:
default:
// GG=00 Reserved for future use
log.debug("CV # {}, Reserved (GG=0); {}", cvnum, packetInt[4]);
}
} else if (packetInt.length == 3) {
int addr = getExtendedAccessoryAddressFromDCCPacket(packetInt);
return Bundle.getMessage("LN_MSG_EXTEND_ACCY_SET_ASPECT",
addr, addr - 4, packetInt[2] );
}
}
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.
}
/*
* Returns the Digitrax Extended Accessory Packet Address
*/
private static int getExtendedAccessoryAddressFromDCCPacket(int[] packetInt) {
return ( 1 + ((packetInt[0] & 0x3F) << 2) +
((( ~ packetInt[1]) & 0x70) << 4)
+ ((packetInt[1] & 0x06) >> 1));
}
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 and 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
*/
public 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;
}
}
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_UT6:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UT6");
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_DCS210PLUS:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS210PLUS");
case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS240");
case LnConstants.RE_IPL_DIGITRAX_HOST_DCS240PLUS:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DCS240PLUS");
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_DT602:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DT602");
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_UR93:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_UR93");
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_BXPA1:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_BXPA1");
case LnConstants.RE_IPL_DIGITRAX_HOST_DS74:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DS74");
case LnConstants.RE_IPL_DIGITRAX_HOST_DS78V:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_DS78V");
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");
case LnConstants.RE_IPL_DIGITRAX_HOST_PM74:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_PM74");
case LnConstants.RE_IPL_DIGITRAX_HOST_SE74:
return Bundle.getMessage("LN_MSG_IPL_DEVICE_HELPER_DIGITRAX_HOST_SE74");
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.
*
* @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.getElement(1) == 0x10) {
String ret;
ret = jmri.jmrix.loconet.alm.almi.Almi.interpretAlm(l);
if (ret.length() > 1) {
return ret;
}
}
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) == LnConstants.OPC_EXP_SEND_SPEED_AND_DIR_FWD) {
// speed and direction
int spd = l.getElement(4);
String direction = Bundle.getMessage((l.getElement(1) & LnConstants.OPC_EXP_SEND_SPEED_AND_DIR_REV) != 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) {
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) {
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) {
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) {
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) {
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 < 253) {
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)));
}
/**
* Interprets an Enhanced Slot Report message in the "Query Mode" range of
* slot numbers.
*
* Only the primary slot numbers are interpreted, not any "aliases".
*
* @param l Enhanced Slot report LocoNetMessage to be interpreted
* @param slot
* @return String showing interpretation.
*/
private static String interpretExtendedSlot_StatusData(LocoNetMessage l, int slot) {
String baseInfo = "";
String detailInfo = "";
switch (slot) {
case 248:
baseInfo = interpretExtendedSlot_StatusData_Base_Detail(l, slot); // Basic Identifying information
detailInfo = interpretExtendedSlot_Query_Mode_248(l); // Flags
break;
case 249:
baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information
detailInfo = interpretExtendedSlot_Query_Mode_249(l); // Electrical properties
break;
case 250:
baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information
detailInfo = interpretExtendedSlot_Query_Mode_250(l); // Slots info
break;
case 251:
baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information
detailInfo = interpretExtendedSlot_Query_Mode_251(l); // LocoNet events and messages and stats
break;
case 252:
baseInfo = interpretExtendedSlot_StatusData_Base(l, slot); // Basic Identifying information
detailInfo = interpretExtendedSlot_Query_Mode_252(l); // DCC track status info
break;
default:
baseInfo = "Wrong Slot # ("+Integer.toString(slot)+")";
}
return Bundle.getMessage("LN_MSG_OPC_EXP_QUERY_MODE_OVERALL",
slot, baseInfo, detailInfo);
}
/**
* Interpret the base information in bytes 16,18,19
* for slots 249,250,251, but not 248.
*
* @param l LocoNetMessage to be interpreted
* @param slot slot number
* @return formatted message
*/
private static String interpretExtendedSlot_StatusData_Base(LocoNetMessage l, int slot) {
String hwType = LnConstants.IPL_NAME(l.getElement(16));
int hwSerial = ((l.getElement(19) & 0x3f) * 128 ) + l.getElement(18);
String serNumHex = "0000"+Integer.toHexString(hwSerial).toUpperCase();
serNumHex = serNumHex.substring(serNumHex.length()-4);
return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_BASE",
hwType,
hwSerial + "(0x" + serNumHex + ")");
}
/**
* Interpret slot 248 base details.
*
* @param l LocoNetMessage to be interpreted
* @param slot slot number
* @return formatted message
*/
private static String interpretExtendedSlot_StatusData_Base_Detail(LocoNetMessage l, int slot) {
String hwType = LnConstants.IPL_NAME(l.getElement(14));
if ((l.getElement(19) & 0x40) == 0x40) {
hwType = hwType + Bundle.getMessage("LN_MSG_COMMAND_STATION");
}
int hwSerial = ((l.getElement(19) & 0x3f) * 128 ) + l.getElement(18);
String serNumHex = "0000"+Integer.toHexString(hwSerial).toUpperCase();
serNumHex = serNumHex.substring(serNumHex.length()-4);
float hwVersion = ((float)(l.getElement(17) & 0x78) / 8 ) + ((float)(l.getElement(17) & 0x07) / 10 ) ;
float swVersion = ((float)(l.getElement(16) & 0x78) / 8 ) + ((float)(l.getElement(16) & 0x07) / 10 ) ;
return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_BASEDETAIL",
hwType,
hwSerial + "(0x" + serNumHex + ")",
hwVersion, swVersion);
}
private static String queryOnOff(int val, int bit) {
return (((val & 1 << bit) == 1 << bit)?Ln_On:Ln_Off);
}
/**
* Interprets _some_ of the data in Query Mode report of slot 248 (and aliases!)
* - "Flags" info.
*
* @param l LocoNetMessage to be interpreted
* @return formatted message
*/
private static String interpretExtendedSlot_Query_Mode_248(LocoNetMessage l) {
int b = l.getElement(4);
String lnetVmin = Bundle.getMessage("LNET_QUERY_LNETVMIN", queryOnOff(b, 6));
String overTemp = Bundle.getMessage("LNET_QUERY_OVERTEMP", queryOnOff(b, 5));
String fuseBad = Bundle.getMessage("LNET_QUERY_FUSEBAD", queryOnOff(b, 4));
String rsynMax = Bundle.getMessage("LNET_QUERY_RSYNMAX", queryOnOff(b, 3));
String vinHi = Bundle.getMessage("LNET_QUERY_VINHI", queryOnOff(b, 2));
String vinLo = Bundle.getMessage("LNET_QUERY_VINLO", queryOnOff(b, 1));
String iTrk = Bundle.getMessage("LNET_QUERY_ITRK", queryOnOff(b, 0));
b = l.getElement(5);
String usbLink = Bundle.getMessage("LNET_QUERY_ULINK", queryOnOff(b, 5));
String iLim = Bundle.getMessage("LNET_QUERY_ILIM", queryOnOff(b, 3));
String PTrkMaxI = Bundle.getMessage("LNET_QUERY_PTRKMAXI", queryOnOff(b, 2));
String PtrkIsol = Bundle.getMessage("LNET_QUERY_PTRKISOL", queryOnOff(b, 1));
return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_FLAGS",
rsynMax, usbLink, iTrk, vinLo, vinHi, fuseBad,
overTemp, lnetVmin, PtrkIsol, PTrkMaxI, iLim);
}
/**
* Interprets _some_ of the data in Query Mode report of slot 249 (and aliases!)
* - "Electrical" info.
*
* @param l LocoNetMessage to be interpreted
* @return formatted message
*/
private static String interpretExtendedSlot_Query_Mode_249(LocoNetMessage l) {
float voltsTrack = ((float)l.getElement(4)) * 2 / 10 ;
float voltsIn = ((float)l.getElement(5)) * 2 / 10;
float ampsIn = ((float)l.getElement(6)) / 10;
float ampsLimit = ((float)l.getElement(7)) / 10;
float voltsRsLoaded = ((float)l.getElement(12)) * 2 / 10;
float voltsRsUnLoaded = ((float)l.getElement(10)) * 2 / 10;
return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_ELECTRIC",
voltsTrack,
voltsIn,
ampsIn,
ampsLimit,
voltsRsLoaded,
voltsRsUnLoaded);
}
/**
* Interprets _some_ of the data in Query Mode report of slot 250 (and aliases!)
* - "Slots" info.
*
* @param l LocoNetMessage to be interpreted
* @return formatted message
*/
private static String interpretExtendedSlot_Query_Mode_250(LocoNetMessage l) {
int msgInUse = (l.getElement(4) + ( l.getElement(5) * 128)) ;
int msgIdle = (l.getElement(6) + ( l.getElement(7) * 128)) ;
int msgFree = (l.getElement(8) + ( l.getElement(9) * 128)) ;
int ctop = (l.getElement(10) + ( l.getElement(11) * 128)) ;
int cup = (l.getElement(12) + ( l.getElement(13) * 128)) ;
return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_SLOTS",
msgInUse, msgIdle, msgFree, ctop, cup);
}
/**
* Interprets _some_ of the data in Query Mode report of slot 251 (and aliases!)
* - "LocoNet message" info.
*
* @param l LocoNetMessage to be interpreted
* @return formatted message
*/
private static String interpretExtendedSlot_Query_Mode_251(LocoNetMessage l) {
int msgTotal = (l.getElement(4) + ( l.getElement(5) * 128)) ;
int msgErrors = (l.getElement(6) + ( l.getElement(7) * 128)) ;
int sleeps = (l.getElement(10) + ( l.getElement(11) * 128));
return Bundle.getMessage("LN_MSG_OPC_EXP_SPECIALSTATUS_LOCONET",
msgTotal, msgErrors, sleeps);
}
/**
* Interprets _some_ of the data in Query Mode report of slot 252 (and aliases!)
* - "DCC status" info.
*
* @param l LocoNetMessage to be interpreted
* @return formatted message
*/
private static String interpretExtendedSlot_Query_Mode_252(LocoNetMessage l) {
int flt = (l.getElement(4) & 0x7f) + ((l.getElement(5) & 0x7f) << 7);
int arv = (l.getElement(6) & 0x7f) + ((l.getElement(7) & 0x7f) << 7);
int dst = (l.getElement(8) & 0x7f) + ((l.getElement(9) & 0x7f) << 7);
return Bundle.getMessage("LN_MSG_OPC_EXP_QUERY_LOCONET_STAT2_LOCONET",
flt, arv, dst);
}
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);
}