001package jmri.jmrix.dcc4pc; 002 003import java.util.Hashtable; 004import java.util.Map; 005import jmri.RailCom; 006import jmri.Sensor; 007import jmri.implementation.AbstractRailComReporter; 008 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Extend jmri.implementation.AbstractRailComReporter for Dcc4Pc Reporters. 014 * Implementation for providing status of rail com decoders at this 015 * reporter location. 016 * <p> 017 * The reporter will decode the rail com packets and add the information to the 018 * rail com tag. 019 * 020 * @author Kevin Dickerson Copyright (C) 2012 021 */ 022public class Dcc4PcReporter extends AbstractRailComReporter { 023 024 public Dcc4PcReporter(String systemName, String userName) { // a human-readable Reporter number must be specified! 025 super(systemName, userName); // can't use prefix here, as still in construction 026 } 027 028 // data members 029 transient RailComPacket[] rcPacket = new RailComPacket[3]; 030 031 void setPacket(int[] arraytemp, int dcc_addr_type, int dcc_addr, int cvNumber, int speed, int packetTypeCmd) { 032 log.debug("{} dcc_addr {} {} {}", getDisplayName(), dcc_addr, cvNumber, speed); 033 RailComPacket rc = new RailComPacket(arraytemp, dcc_addr_type, dcc_addr, cvNumber, speed); 034 decodeRailComInfo(rc, packetTypeCmd); 035 rcPacket[2] = rcPacket[1]; 036 rcPacket[1] = rcPacket[0]; 037 rcPacket[0] = rc; 038 synchronized(lock) { 039 log.debug("Packets Seen {} in error {}", packetseen, packetsinerror); 040 } 041 } 042 043 static class RailComPacket { 044 045 transient final int[] rcPacket; 046 int dcc_addr_type; 047 int dccAddress; 048 int cvNumber; 049 int speed; 050 051 RailComPacket(int[] array, int dcc_addr_type, int dcc_addr, int cvNumber, int speed) { 052 rcPacket = array; 053 this.dcc_addr_type = dcc_addr_type; 054 this.dccAddress = dcc_addr; 055 this.cvNumber = cvNumber; 056 this.speed = speed; 057 } 058 059 int[] getPacket() { 060 return rcPacket; 061 } 062 063 int getAddressType() { 064 return dcc_addr_type; 065 } 066 067 int getDccAddress() { 068 return dccAddress; 069 } 070 071 int getCvNumber() { 072 return cvNumber; 073 } 074 075 int getSpeed() { 076 return speed; 077 } 078 079 String toHexString() { 080 StringBuilder buf = new StringBuilder(); 081 buf.append("0x").append(Integer.toHexString(0xFF & rcPacket[0])); 082 for (int i = 1; i < rcPacket.length; i++) { 083 buf.append(", 0x").append(Integer.toHexString(0xFF & rcPacket[i])); 084 } 085 return buf.toString(); 086 } 087 } 088 089 void duplicatePacket(int dup) { 090 RailComPacket temp; 091 switch (dup) { 092 case 0x00: 093 break; //re-use the rcPacket at the head. 094 case 0x02: 095 temp = rcPacket[1]; //move rcPacket one to the head 096 rcPacket[1] = rcPacket[0]; 097 rcPacket[0] = temp; 098 break; 099 case 0x03: 100 temp = rcPacket[2]; //move rcPacket two to the head 101 rcPacket[2] = rcPacket[1]; 102 rcPacket[1] = rcPacket[0]; 103 rcPacket[0] = temp; 104 break; 105 default: 106 break; 107 } 108 } 109 110 int state = Sensor.UNKNOWN; 111 112 public void setRailComState(int ori) { 113 if (state == ori) { 114 return; 115 } 116 state = ori; 117 if (ori == Sensor.INACTIVE || ori == Sensor.UNKNOWN) { 118 //We reset everything as the associated sensor has gone inactive 119 synchronized (this) { 120 addr = 0; 121 address_part_1 = 0x100; 122 address_part_2 = -1; 123 addr_type = -1; 124 actual_speed = -1; 125 actual_load = -1; 126 actual_temperature = -1; 127 fuelLevel = -1; 128 waterLevel = -1; 129 location = -1; 130 routing_no = -1; 131 } 132 cvNumber = -1; 133 cvValues = new Hashtable<>(); 134 setReport(null); 135 } else if (ori == RailCom.ORIENTA || ori == RailCom.ORIENTB) { 136 if (super.getCurrentReport() != null && super.getCurrentReport() instanceof RailCom) { 137 ((RailCom) super.getCurrentReport()).setOrientation(state); 138 } 139 firePropertyChange("currentReport", null, null); 140 } 141 } 142 143 public int getRailComState() { 144 return state; 145 } 146 147 public String getReport() { 148 if (super.getCurrentReport() != null && super.getCurrentReport() instanceof RailCom) { 149 return ((RailCom) super.getCurrentReport()).getTagID(); 150 } 151 if ((getRailComState() < RailCom.ORIENTA) || (rcPacket[0] == null) || rcPacket[0].getPacket() == null) { 152 return ""; 153 } 154 return ""; 155 } 156 157 //packet Length is a temp store used for decoding the railcom packet 158 int packetLength = 0; 159 160 void setPacketLength(int i) { 161 packetLength = i; 162 } 163 164 int getPacketLength() { 165 return packetLength; 166 } 167 168 int addr = 0; 169 int address_part_1 = 0x100; 170 int address_part_2 = -1; 171 int addr_type = -1; 172 int actual_speed = -1; 173 int actual_load = -1; 174 int actual_temperature = -1; 175 int fuelLevel = -1; 176 int waterLevel = -1; 177 int location = -1; 178 int routing_no = -1; 179 int cvNumber = -1; 180 int cvvalue = -1; 181 182 int addressp1found = 0; 183 184 static int packetseen = 0; 185 static int packetsinerror = 0; 186 private static final Object lock = new Object(); 187 188 Hashtable<Integer, Integer> cvValues = new Hashtable<>(); 189 190 void decodeRailComInfo(RailComPacket rc, int packetTypeCmd) { 191 192 synchronized(lock) { 193 addressp1found++; 194 RailCom rcTag = null; 195 196 if (super.getCurrentReport() instanceof RailCom) { 197 rcTag = (RailCom) super.getCurrentReport(); 198 } 199 int[] packet = rc.getPacket(); 200 char chbyte; 201 char type; 202 203 if (log.isDebugEnabled()) { 204 log.debug("{} packet type {}", getDisplayName(), packetTypeCmd); 205 log.debug("decodeRailComInfo {} {}", this.getDisplayName(), super.getCurrentReport()); 206 StringBuilder buf = new StringBuilder(); 207 for (int i = 0; i < packet.length; ++i) { 208 buf.append(packet[i]); 209 } 210 log.debug("Rail Comm Packets {}", buf); 211 212 } 213 int i = 0; 214 while (i < packet.length) { 215 packetseen++; 216 chbyte = (char) packet[i]; 217 chbyte = decode[chbyte]; 218 if (chbyte == ERROR) { 219 if (log.isDebugEnabled()) { 220 log.debug("{} Error packet stage 1: {}", this.getDisplayName(), Integer.toHexString(packet[i])); 221 } 222 packetsinerror++; 223 return; 224 } 225 i++; 226 if ((chbyte & ACK) == ACK) { 227 chbyte = (char) packet[i]; 228 i++; 229 chbyte = decode[chbyte]; 230 if (chbyte == ERROR) { 231 log.debug("{} Error packet stage 2", this.getDisplayName()); 232 packetsinerror++; 233 return; 234 } 235 if ((chbyte & ACK) == ACK) { 236 if (packet.length <= (i + 1)) { 237 log.debug("No further data to process Only had the ack 1"); 238 break; 239 } 240 chbyte = (char) packet[i]; 241 i++; 242 chbyte = decode[chbyte]; 243 } 244 } 245 if (packet.length <= i) { 246 break; 247 } 248 type = chbyte; 249 chbyte = (char) packet[i]; 250 chbyte = decode[chbyte]; 251 if ((chbyte == ERROR) || ((chbyte & ACK) == ACK)) { 252 if (log.isDebugEnabled()) { 253 log.debug("{} Error packet stage 3 {}\n{}", this.getDisplayName(), Integer.toHexString(packet[i]), rc.toHexString()); 254 } 255 i++; 256 packetsinerror++; 257 return; 258 } 259 260 chbyte = (char) (((type & 0x03) << 6) | (chbyte & 0x3f)); 261 type = (char) ((type >> 2) & 0x0F); 262 263 switch (type) { 264 case 0: 265 log.debug("{} CV Value {}{}", this.getDisplayName(), (int) chbyte, rcTag); 266 cvvalue = chbyte; 267 if (rcTag != null) { 268 rcTag.setWhereLastSeen(this); 269 if (rcTag.getExpectedCv() != -1) { 270 rcTag.setCvValue(chbyte); 271 } else { 272 rcTag.setCV(rc.getCvNumber(), chbyte); 273 } 274 } 275 break; 276 case 4: 277 if (log.isDebugEnabled()) { 278 log.debug("{} Create/Get id tag for {}", this.getDisplayName(), rc.getDccAddress()); 279 } 280 addr = rc.getDccAddress(); 281 addr_type = rc.getAddressType(); 282 break; 283 case 1: // Address byte 1 284 log.debug("Address Byte 1"); 285 address_part_1 = (0x100 | chbyte); 286 addressp1found = 0; 287 break; 288 case 2: //Address byte 2 289 log.debug("{} Address part 2:", this.getDisplayName()); 290 address_part_2 = chbyte; 291 if (packetTypeCmd == 0x03) { 292 log.debug("Type three packet so shouldn't not pair part one with part two if it came from the previous packet"); 293 //As the last command was a type 3, an address part one packet can not be paired with this address part two packet. Therefore will set it back to default 294 //address_part_1 = 0x100; 295 // break; 296 } 297 if (!((address_part_1 & 0x100) == 0x100)) { 298 log.debug("{} Break at Address part 1, part one not complete", this.getDisplayName()); 299 break; 300 } 301 rcTag = decodeAddress(); 302 break; 303 case 3: //Actual Speed / load 304 if ((chbyte & 0x80) == 0x80) { 305 actual_speed = (chbyte & 0x7f); 306 log.debug("{} Actual Speed: {}", this.getDisplayName(), actual_speed); 307 } else { 308 actual_load = (chbyte & 0x7f); 309 log.debug("{} Actual Load: {}", this.getDisplayName(), actual_load); 310 } 311 if (rcTag != null) { 312 rcTag.setActualLoad(actual_load); 313 rcTag.setActualSpeed(actual_speed); 314 rcTag.setWhereLastSeen(this); 315 } 316 break; 317 case 5: //Routing number 318 routing_no = chbyte; 319 if (rcTag != null) { 320 rcTag.setRoutingNo(routing_no); 321 rcTag.setWhereLastSeen(this); 322 } 323 break; 324 case 6: //Location 325 location = chbyte; 326 if (rcTag != null) { 327 rcTag.setLocation(location); 328 rcTag.setWhereLastSeen(this); 329 } 330 break; 331 case 7: //Fuel water 332 if ((chbyte & 0x80) == 0x80) { 333 fuelLevel = (chbyte & 0x7f); 334 } else { 335 waterLevel = (chbyte & 0x7f); 336 } 337 338 if (rcTag != null) { 339 rcTag.setWaterLevel(waterLevel); 340 rcTag.setFuelLevel(fuelLevel); 341 rcTag.setWhereLastSeen(this); 342 } 343 break; 344 case 8: //Temp 345 if (!((chbyte & 0x80) == 0x80)) { 346 actual_temperature = (chbyte & 0x7F); 347 } 348 if (rcTag != null) { 349 rcTag.setActualTemperature(actual_temperature); 350 rcTag.setWhereLastSeen(this); 351 } 352 break; 353 case 15: //CV Address Value 354 log.debug("{} CV Address and value:", this.getDisplayName()); 355 i = i + 2; 356 //len = 4; 357 break; 358 default: 359 log.info("unknown railcom type packet {}", type); 360 break; 361 } 362 i++; 363 364 } 365 } 366 } 367 368 RailCom decodeAddress() { 369 RailCom rcTag; 370 log.debug("{} Create/Get id tag for {}", this.getDisplayName(), addr); 371 rcTag = (RailCom)jmri.InstanceManager.getDefault(jmri.RailComManager.class).provideIdTag("" + addr); 372 373 if ((fuelLevel != -1)) { 374 rcTag.setFuelLevel(fuelLevel); 375 } 376 if ((waterLevel != -1)) { 377 rcTag.setWaterLevel(waterLevel); 378 } 379 if ((routing_no != -1)) { 380 rcTag.setRoutingNo(routing_no); 381 } 382 if ((location != -1)) { 383 rcTag.setLocation(location); 384 } 385 if ((actual_temperature != -1)) { 386 rcTag.setActualTemperature(actual_temperature); 387 } 388 if ((actual_load != -1)) { 389 rcTag.setActualLoad(actual_load); 390 } 391 if ((actual_speed != -1)) { 392 rcTag.setActualSpeed(actual_speed); 393 } 394 for (Map.Entry<Integer, Integer> entry : cvValues.entrySet()) { 395 rcTag.setCV(entry.getKey(), entry.getValue()); 396 if (cvvalue != -1) { 397 rcTag.setCvValue(cvvalue); 398 } 399 } 400 401 address_part_1 = 0; 402 address_part_2 = -1; 403 notify(rcTag); 404 return rcTag; 405 } 406 407 RailCom provideTag(int address, int addr_type) { 408 log.debug("provide Tag"); 409 RailCom rcTag = (RailCom) jmri.InstanceManager.getDefault(jmri.RailComManager.class).provideIdTag("" + address); 410 notify(rcTag); 411 return rcTag; 412 } 413 414 public final static char ACK = 0x80; 415 public final static char ACK_1 = 0x81; 416 public final static char ACK_2 = 0x82; 417 public final static char ACK_3 = 0x83; 418 public final static char ACK_4 = 0x84; 419 public final static char ACK_5 = 0x85; 420 public final static char ACK_6 = 0x86; 421 public final static char ERROR = 0xFF; 422 423 private final static char[] decode = new char[]{ 424 ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 425 ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ACK_1, 426 ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 0x33, 427 ERROR, ERROR, ERROR, 0x34, ERROR, 0x35, 0x36, ERROR, 428 ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 0x3A, 429 ERROR, ERROR, ERROR, 0x3B, ERROR, 0x3C, 0x37, ERROR, 430 ERROR, ERROR, ERROR, 0x3F, ERROR, 0x3D, 0x38, ERROR, 431 ERROR, 0x3E, 0x39, ERROR, ACK_5, ERROR, ERROR, ERROR, 432 ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 0x24, 433 ERROR, ERROR, ERROR, 0x23, ERROR, 0x22, 0x21, ERROR, 434 ERROR, ERROR, ERROR, 0x1F, ERROR, 0x1E, 0x20, ERROR, 435 ERROR, 0x1D, 0x1C, ERROR, 0x1B, ERROR, ERROR, ERROR, 436 ERROR, ERROR, ERROR, 0x19, ERROR, 0x18, 0x1A, ERROR, 437 ERROR, 0x17, 0x16, ERROR, 0x15, ERROR, ERROR, ERROR, 438 ERROR, 0x25, 0x14, ERROR, 0x13, ERROR, ERROR, ERROR, 439 0x32, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 440 ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ACK_2, 441 ERROR, ERROR, ERROR, 0x0E, ERROR, 0x0D, 0x0C, ERROR, 442 ERROR, ERROR, ERROR, 0x0A, ERROR, 0x09, 0x0B, ERROR, 443 ERROR, 0x08, 0x07, ERROR, 0x06, ERROR, ERROR, ERROR, 444 ERROR, ERROR, ERROR, 0x04, ERROR, 0x03, 0x05, ERROR, 445 ERROR, 0x02, 0x01, ERROR, 0x00, ERROR, ERROR, ERROR, 446 ERROR, 0x0F, 0x10, ERROR, 0x11, ERROR, ERROR, ERROR, 447 0x12, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 448 ERROR, ERROR, ERROR, ACK_3, ERROR, 0x2B, 0x30, ERROR, 449 ERROR, 0x2A, 0x2F, ERROR, 0x31, ERROR, ERROR, ERROR, 450 ERROR, 0x29, 0x2E, ERROR, 0x2D, ERROR, ERROR, ERROR, 451 0x2C, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 452 ERROR, ACK_6, 0x28, ERROR, 0x27, ERROR, ERROR, ERROR, 453 0x26, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 454 ACK_4, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, 455 ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR}; 456 457 private final static Logger log = LoggerFactory.getLogger(Dcc4PcReporter.class); 458 459}