001package jmri.jmrix.roco.z21; 002 003import jmri.SpeedStepMode; 004import jmri.jmrix.lenz.XNetConstants; 005import jmri.jmrix.lenz.XNetMessage; 006import jmri.jmrix.lenz.XPressNetMessageFormatter; 007import org.reflections.Reflections; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011import java.lang.reflect.Constructor; 012import java.lang.reflect.InvocationTargetException; 013import java.util.ArrayList; 014import java.util.List; 015import java.util.Set; 016 017/** 018 * Represents a single command or response on the XpressNet. 019 * <p> 020 * Content is represented with ints to avoid the problems with sign-extension 021 * that bytes have, and because a Java char is actually a variable number of 022 * bytes in Unicode. 023 * 024 * @author Bob Jacobsen Copyright (C) 2002 025 * @author Paul Bender Copyright (C) 2003-2010 026 */ 027public class Z21XNetMessage extends jmri.jmrix.lenz.XNetMessage { 028 029 /** 030 * Constructor, just pass on to the superclass. 031 * @param len message length. 032 */ 033 public Z21XNetMessage(int len) { 034 super(len); 035 } 036 037 /** 038 * Constructor from a Z21Message. 039 * @param m the Z21Message. 040 */ 041 public Z21XNetMessage(Z21Message m) { 042 super(m.getLength()-4); 043 for(int i = 4; i< m.getLength() ; i++ ){ 044 this.setElement(i-4,m.getElement(i)); 045 } 046 } 047 048 /** 049 * Create a new object, that is a copy of an existing message. 050 * 051 * @param message an existing Z21XpressNet message 052 */ 053 public Z21XNetMessage(Z21XNetMessage message) { 054 super(message); 055 } 056 057 /** 058 * Create an Z21XNetMessage from an Z21XNetReply. 059 * @param message the existing Z21XNetReply. 060 */ 061 public Z21XNetMessage(Z21XNetReply message) { 062 super(message); 063 } 064 065 /** 066 * Create an XNetMessage from a String containing bytes. 067 * @param s byte string. 068 */ 069 public Z21XNetMessage(String s) { 070 super(s); 071 } 072 private static final List<XPressNetMessageFormatter> formatterList = new ArrayList<>(); 073 074 @Override 075 public String toMonitorString() { 076 077 if(formatterList.isEmpty()) { 078 try { 079 Reflections reflections = new Reflections("jmri.jmrix"); 080 Set<Class<? extends XPressNetMessageFormatter>> f = reflections.getSubTypesOf(XPressNetMessageFormatter.class); 081 for (Class<?> c : f) { 082 log.debug("Found formatter: {}", f.getClass().getName()); 083 Constructor<?> ctor = c.getConstructor(); 084 formatterList.add((XPressNetMessageFormatter) ctor.newInstance()); 085 } 086 } catch (NoSuchMethodException | SecurityException | InstantiationException | 087 IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 088 log.error("Error instantiating formatter", e); 089 } 090 } 091 092 return formatterList.stream() 093 .filter(f -> f.handlesMessage(this)) 094 .findFirst().map(f -> f.formatMessage(this)) 095 .orElse(this.toString()); 096 } 097 098 /** 099 * Create messages of a particular form. 100 * @param cv CV index 101 * @return message to send. 102 */ 103 public static Z21XNetMessage getZ21ReadDirectCVMsg(int cv) { 104 Z21XNetMessage m = new Z21XNetMessage(5); 105 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 106 m.setTimeout(XNetProgrammingTimeout); 107 m.setElement(0, Z21Constants.LAN_X_CV_READ_XHEADER); 108 m.setElement(1, Z21Constants.LAN_X_CV_READ_DB0); 109 m.setElement(2, ((0xff00 & (cv - 1)) >> 8)); 110 m.setElement(3, (0xff & (cv - 1))); 111 m.setParity(); // Set the parity bit 112 return m; 113 } 114 115 public static Z21XNetMessage getZ21WriteDirectCVMsg(int cv, int val) { 116 Z21XNetMessage m = new Z21XNetMessage(6); 117 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 118 m.setTimeout(XNetProgrammingTimeout); 119 m.setElement(0, Z21Constants.LAN_X_CV_WRITE_XHEADER); 120 m.setElement(1, Z21Constants.LAN_X_CV_WRITE_DB0); 121 m.setElement(2, (0xff00 & (cv - 1)) >> 8); 122 m.setElement(3, (0xff & (cv - 1))); 123 m.setElement(4, val); 124 m.setParity(); // Set the parity bit 125 return m; 126 } 127 128 /** 129 * Given a locomotive address, request its status. 130 * 131 * @param address is the locomotive address 132 * @return message to send. 133 */ 134 public static Z21XNetMessage getZ21LocomotiveInfoRequestMsg(int address) { 135 Z21XNetMessage msg = new Z21XNetMessage(5); 136 msg.setElement(0, XNetConstants.LOCO_STATUS_REQ); 137 msg.setElement(1, Z21Constants.LAN_X_LOCO_INFO_REQUEST_Z21); 138 msg.setElement(2, jmri.jmrix.lenz.LenzCommandStation.getDCCAddressHigh(address)); 139 msg.setElement(3, jmri.jmrix.lenz.LenzCommandStation.getDCCAddressLow(address)); 140 msg.setParity(); 141 return (msg); 142 } 143 144 /** 145 * Given a locomotive address, a function number, and its value, 146 * generate a message to change the state. 147 * 148 * @param address is the locomotive address 149 * @param functionno is the function to change 150 * @param state is boolean representing whether the function is to be on or off 151 * @return message to send. 152 */ 153 public static Z21XNetMessage getZ21LocomotiveFunctionOperationMsg(int address, int functionno, boolean state) { 154 Z21XNetMessage msg = new Z21XNetMessage(6); 155 int functionbyte = functionno; 156 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 157 msg.setElement(1, Z21Constants.LAN_X_SET_LOCO_FUNCTION); 158 msg.setElement(2, jmri.jmrix.lenz.LenzCommandStation.getDCCAddressHigh(address)); 159 msg.setElement(3, jmri.jmrix.lenz.LenzCommandStation.getDCCAddressLow(address)); 160 if(state) { 161 //This function is on 162 functionbyte = functionbyte & 0x3F; // clear the 2 most significant bits. 163 functionbyte = functionbyte | 0x40; // set the 2 msb to 01. 164 } else { 165 //This function is off. 166 functionbyte = functionbyte & 0x3F; // clear the 2 most significant bits. 167 } 168 msg.setElement(4, functionbyte); 169 msg.setParity(); 170 msg.setBroadcastReply(); 171 return (msg); 172 } 173 174 /** 175 * Generate a Z21 message to change the speed/direction of a locomotive. 176 * 177 * @param address the locomotive address 178 * @param speedMode the speedstep mode see @jmri.DccThrottle 179 * for possible values. 180 * @param speed a normalized speed value (a floating point number between 0 181 * and 1). A negative value indicates emergency stop. 182 * @param isForward true for forward, false for reverse. 183 * @return message to send. 184 */ 185 public static XNetMessage getZ21LanXSetLocoDriveMsg(int address, SpeedStepMode speedMode, float speed, boolean isForward) { 186 XNetMessage msg = XNetMessage.getSpeedAndDirectionMsg(address, 187 speedMode,speed,isForward); 188 msg.setBroadcastReply(); 189 return (msg); 190 } 191 192 193 /** 194 * Given a turnout address, generate a message to request the state. 195 * 196 * @param address the turnout address 197 * @return message to send. 198 */ 199 public static Z21XNetMessage getZ21TurnoutInfoRequestMessage(int address ) { 200 // refer to section 5.1 of the z21 lan protocol manual. 201 Z21XNetMessage msg = new Z21XNetMessage(4); 202 msg.setElement(0,Z21Constants.LAN_X_GET_TURNOUT_INFO); 203 // compared to Lenz devices, the addresses on the Z21 is one below 204 // the numerical value. We will correct it here so higher level 205 // code doesn't see the difference. 206 msg.setElement(1,((address-1) &0xff00)>>8); 207 msg.setElement(2,((address-1) & 0x00ff)); 208 msg.setParity(); 209 return(msg); 210 } 211 212 /** 213 * Given a turnout address and whether or not it is thrown, generate 214 * a message to operate the turnout. 215 * 216 * @param address is the turnout address 217 * @param thrown boolean value representing whether the turnout is thrown. 218 * @param active boolean value representing whether the output is being set 219 * to active. 220 * @param queue boolean value representing whehter or not the message is 221 * added to the queue. 222 * @return message to send. 223 */ 224 public static Z21XNetMessage getZ21SetTurnoutRequestMessage(int address, boolean thrown, boolean active, boolean queue) { 225 // refer to section 5.2 of the z21 lan protocol manual. 226 Z21XNetMessage msg = new Z21XNetMessage(5); 227 msg.setElement(0,Z21Constants.LAN_X_SET_TURNOUT); 228 // compared to Lenz devices, the addresses on the Z21 is one below 229 // the numerical value. We will correct it here so higher level 230 // code doesn't see the difference. 231 msg.setElement(1,((address-1) &0xff00)>>8); 232 msg.setElement(2,((address-1) & 0x00ff)); 233 int element3=0x80; 234 if(active) { 235 element3 |= 0x08; 236 } 237 if(thrown) { 238 element3 |= 0x01; 239 } 240 if(queue) { 241 element3 |= 0x20; 242 } 243 244 msg.setElement(3,element3); 245 msg.setParity(); 246 msg.setBroadcastReply(); 247 return(msg); 248 } 249 250 private static final Logger log = LoggerFactory.getLogger(Z21XNetMessage.class); 251 252}